# CodeBlock

> Syntax-highlighted code with filename header, line numbers, highlighted lines and a copy button; Shiki loads lazily. Also InlineCode.

Source: https://docs.nasaqui.com/components/code-block

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/code-block.json
```

A `<figure>` holding a scrollable `<pre>` of highlighted code. Highlighting uses Shiki, loaded with a dynamic
import the first time a block renders, and each language grammar is its own chunk. Until Shiki arrives the
block shows the same lines as plain text, so nothing shifts when colours appear. Colours come from Shiki's
CSS-variables theme, mapped to `--nq-*` tokens, so light and dark work with no second theme and no hex in source.
`InlineCode` is the inline counterpart for a word or command inside a sentence.

## When to use

- Snippets, commands, config and API examples in docs, chat and tutorials.
- A short token in running text: `InlineCode`.

## When not to use

- Copying a single value such as an API key: use `CopyField` from [copy-button](https://docs.nasaqui.com/components/copy-button).
- Rendering Markdown that contains code fences: use [Markdown](https://docs.nasaqui.com/components/markdown), which uses CodeBlock for you.
- Editable code: this component is read-only.

## Import

```tsx
import { CodeBlock, InlineCode } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { CodeBlock } from "@fadymondy/nasaq/web";

export function Example() {
  return <CodeBlock language="ts" filename="greet.ts" lineNumbers code={`export const greet = (name: string) => \`Hello, \${name}\`;`} />;
}
```

## Anatomy

```
CodeBlock            data-slot="code-block"          <figure dir="ltr">
├─ header            data-slot="code-block-header"   <figcaption> filename + CopyButton (when filename is set)
├─ CopyButton        data-slot="copy-button"         floats at the inline-end corner when there is no filename
└─ pre               data-slot="code-block-pre"      <pre role="region" tabindex="0">
   └─ code > span[data-line]                         one span per line, with optional gutter number
InlineCode           data-slot="inline-code"         <code dir="ltr">
```

## API

### `CodeBlock`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `code` | `string` | required | Source text. One trailing newline is dropped. |
| `language?` | `CodeLanguage \| string` | `"text"` | `ts tsx js jsx json bash sh css html md go php python py sql yaml yml`. Unknown values render as plain text. |
| `filename?` | `string` | none | Renders a header. Also the accessible name of the code region. |
| `lineNumbers?` | `boolean` | `false` | Gutter with 1-based numbers. Not selectable, so copying gives clean code. |
| `highlightLines?` | `number[] \| string` | none | Emphasised lines: `[2, 3]` or `"2-4,7"`. |
| `copyable?` | `boolean` | `true` | Show the copy button. |
| `copyAction?` | `ReactNode` | none | Replaces the copy button (same place). Used by the AI copy menu in [code-block-variants](https://docs.nasaqui.com/components/code-block-variants). |
| `copyLabel?` | `string` | "Copy code" / "نسخ الشيفرة" | Accessible name of the copy button. |
| `label?` | `string` | filename, else "Code" / "شيفرة برمجية" | Accessible name of the scroll region. |
| `preClassName?` | `string` | none | Classes for the scrolling `<pre>`, e.g. `max-h-80`. |
| `className?` | `string` | none | Merged onto the `<figure>`. |

`dir` is fixed to `ltr` and cannot be overridden.

### `InlineCode`

`(props: ComponentProps<"code">) => JSX.Element`. Left-to-right, bidi-isolated, `font-mono`, on `bg-secondary`.

### `highlightCode(code, language)`

`(code: string, language: string) => Promise<CodeToken[][] | null>`. Tokenises with the shared lazy Shiki instance;
resolves `null` for plain text or an unknown language.

## Examples

### Highlighted lines and a fixed height

```tsx
import { CodeBlock } from "@fadymondy/nasaq/web";

export function Long() {
  return (
    <CodeBlock
      language="bash"
      highlightLines="2"
      preClassName="max-h-48"
      code={"pnpm install\npnpm --filter @nasaq/web typecheck\npnpm build"}
    />
  );
}
```

### Arabic page, code stays left-to-right

```tsx
import { CodeBlock, InlineCode } from "@fadymondy/nasaq/web";

export function Install() {
  return (
    <div dir="rtl" className="space-y-3">
      <p>
        شغّل الأمر <InlineCode>pnpm add @nasaq/web</InlineCode> ثم أضف المزوّد.
      </p>
      <CodeBlock language="tsx" filename="app.tsx" code={"<NasaqProvider defaultLocale=\"ar\">{children}</NasaqProvider>"} />
    </div>
  );
}
```

## Accessibility

| Key | Action |
| --- | --- |
| `Tab` | Focuses the code region, then the copy button |
| Arrow keys | Scroll the focused region |
| `Enter` / `Space` | Copies, on the copy button |

- The `<pre>` is `role="region"` and focusable so keyboard users can scroll long or wide code. Its name is the filename or "Code"; localise `label` when you pass your own.
- Copy results are announced through the CopyButton live region ("Code copied to clipboard").
- Colours are supplementary: every token is still readable text. Keep the theme mapping at AA contrast.

## RTL & i18n

- Code is always `dir="ltr"`, inside Arabic pages too; the block never mirrors. Its outer margins and layout still follow the page.
- Built-in strings: "Copy code" / "نسخ الشيفرة", "Code" / "شيفرة برمجية", "Code copied to clipboard" / "تم نسخ الشيفرة إلى الحافظة".
- Line numbers are Latin digits.
- `InlineCode` is an LTR isolate so it keeps its character order inside an Arabic sentence.

## Styling & tokens

- Surface: `bg-nq-surface-soft`, `border-border`, `rounded-surface`. Highlighted lines use `bg-nq-selected` and an `border-nq-accent` edge.
- Token colours are Shiki variables set on the figure: `--shiki-token-keyword` → `--nq-accent-text`, `string` → `--nq-success-text`, `constant` → `--nq-warning-text`, `function` → `--nq-info-text`, `comment` and `punctuation` → `--nq-fg-muted`. Override them from a parent with `[--shiki-token-keyword:var(--nq-brand)]`.
- State attributes: `data-highlighted` on the figure once Shiki has loaded; `data-highlighted` on a line that is emphasised; `data-line="n"` on each line.

## Do / Don't

- **Do** give a `filename` when the snippet belongs to a file.
- **Do** cap the height with `preClassName` for long output.
- **Don't** put prose inside a CodeBlock.
- **Don't** override colours with raw hex.

## Related

- [CopyButton](https://docs.nasaqui.com/components/copy-button) · [Markdown](https://docs.nasaqui.com/components/markdown) · [Text](https://docs.nasaqui.com/components/text)

## Lab

https://docs.nasaqui.com/?path=/docs/components-developer-tools-code-block--docs

## Code

### React

```tsx
import { CodeBlock } from "@fadymondy/nasaq/web";

export function Example() {
  return <CodeBlock language="ts" filename="greet.ts" lineNumbers code={`export const greet = (name: string) => \`Hello, \${name}\`;`} />;
}
```

### shadcn

```tsx
import { CodeBlock } from "@/components/ui/code-block";

export function Example() {
  return <CodeBlock language="ts" filename="greet.ts" lineNumbers code={`export const greet = (name: string) => \`Hello, \${name}\`;`} />;
}
```

### Vue

```vue
<script setup lang="ts">
import { NqCodeBlock } from "@fadymondy/nasaq/vue";

const code = "export const greet = (name: string) => `Hello, ${name}`;";
</script>

<template>
  <NqCodeBlock language="ts" filename="greet.ts" line-numbers :code="code" />
</template>
```

### Blade

```blade
@php
    $source = 'export const greet = (name: string) => `Hello, ${name}`;'."\n".'// highlighted line'."\n".'greet("Nasaq");';
@endphp
<x-nq::code-block language="ts" filename="greet.ts" line-numbers highlight-lines="2" :code="$source" />
<p class="mt-3 text-body">Run <x-nq::code-block.inline-code>pnpm add @fadymondy/nasaq</x-nq::code-block.inline-code> first.</p>

<div class="mt-6" x-data="{ snippet: 'npm i nasaq', next() { this.snippet = 'pnpm add nasaq\npnpm dlx nasaq init' } }">
    <x-nq::code-block id="live-code" language="bash" filename="install.sh" line-numbers code="npm i nasaq" code-expr="snippet" />
    <x-nq::button id="live-code-change" size="sm" class="mt-3" x-on:click="next()">Use pnpm</x-nq::button>
</div>

{{-- code-block-ai --}}
@php
    $greet = "export const greet = (name: string) => `Hello, \${name}`;\n";
@endphp
<x-nq::code-block-ai :code="$greet" language="ts" filename="greet.ts" />
```

### HTML + Alpine

````html
<figure data-slot="code-block" data-language="ts" dir="ltr" x-data="nqCodeBlock(JSON.parse('{\u0022code\u0022:\u0022export const greet = (name: string) =\\u003E `Hello, ${name}`;\\n\\\/\\\/ highlighted line\\ngreet(\\u0022Nasaq\\u0022);\u0022,\u0022language\u0022:\u0022ts\u0022}'))" x-bind:data-highlighted="highlighted ? '' : null"
    class="group/code relative m-0 overflow-hidden rounded-surface border border-border bg-nq-surface-soft text-start [--shiki-foreground:var(--nq-fg)] [--shiki-background:transparent] [--shiki-token-comment:var(--nq-fg-muted)] [--shiki-token-keyword:var(--nq-accent-text)] [--shiki-token-string:var(--nq-success-text)] [--shiki-token-string-expression:var(--nq-success-text)] [--shiki-token-constant:var(--nq-warning-text)] [--shiki-token-function:var(--nq-info-text)] [--shiki-token-parameter:var(--nq-fg-body)] [--shiki-token-punctuation:var(--nq-fg-muted)] [--shiki-token-link:var(--nq-info-text)]">
            <figcaption data-slot="code-block-header" class="flex h-row items-center justify-between gap-2 border-b border-border ps-3 pe-1.5 font-mono text-caption text-muted-foreground">
            <span class="truncate">greet.ts</span>
                            <span class="contents" x-data="nqCopyButton('export const greet = (name: string) =\u003E `Hello, ${name}`;\n\/\/ highlighted line\ngreet(\u0022Nasaq\u0022);', 1500)">
    <button data-slot="copy-button"
     type="button"                         aria-label="Copy code" x-on:click="copy()" x-bind:data-copied="state === &#039;copied&#039; ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0 data-copied:text-nq-success-text">
        <svg aria-hidden="true" x-show="state !== 'copied'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
  <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
</svg>        <svg aria-hidden="true" x-show="state === 'copied'" style="display: none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></button>
    <span data-slot="copy-button-status" role="status" aria-live="polite" class="sr-only"
        data-copied-label="Code copied to clipboard" data-failed-label="Could not copy"
        x-text="state === 'copied' ? $el.dataset.copiedLabel : state === 'failed' ? $el.dataset.failedLabel : ''"></span>
</span>
                    </figcaption>
        <pre data-slot="code-block-pre" role="region" tabindex="0" aria-label="greet.ts"
        class="m-0 overflow-auto bg-transparent py-3 font-mono text-code text-[var(--shiki-foreground)] outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus "><code class="block w-max min-w-full"><span data-line="1"  class="flex min-h-[1lh] border-s-2 border-transparent pe-4 whitespace-pre"><span aria-hidden="true" class="inline-block shrink-0 select-none pe-4 ps-3 text-end text-muted-foreground tabular-nums" style="min-width: 4ch">1</span><span data-code-line>export const greet = (name: string) =&gt; `Hello, ${name}`;</span></span><span data-line="2"  data-highlighted  class="flex min-h-[1lh] border-s-2 border-transparent pe-4 whitespace-pre border-nq-accent bg-nq-selected"><span aria-hidden="true" class="inline-block shrink-0 select-none pe-4 ps-3 text-end text-muted-foreground tabular-nums" style="min-width: 4ch">2</span><span data-code-line>// highlighted line</span></span><span data-line="3"  class="flex min-h-[1lh] border-s-2 border-transparent pe-4 whitespace-pre"><span aria-hidden="true" class="inline-block shrink-0 select-none pe-4 ps-3 text-end text-muted-foreground tabular-nums" style="min-width: 4ch">3</span><span data-code-line>greet(&quot;Nasaq&quot;);</span></span></code></pre>
</figure>
<p class="mt-3 text-body">Run <code data-slot="inline-code" dir="ltr" class="rounded-[4px] border border-border bg-secondary px-1 py-0.5 font-mono text-[0.9em] text-foreground [unicode-bidi:isolate]">pnpm add @fadymondy/nasaq</code>
 first.</p>
<div class="mt-6" x-data="{ snippet: 'npm i nasaq', next() { this.snippet = 'pnpm add nasaq\npnpm dlx nasaq init' } }">
    <figure data-slot="code-block" data-language="bash" dir="ltr" x-data="nqCodeBlock(JSON.parse('{\u0022code\u0022:\u0022npm i nasaq\u0022,\u0022language\u0022:\u0022bash\u0022,\u0022marked\u0022:[],\u0022lineNumbers\u0022:true}'))" x-bind:data-highlighted="highlighted ? '' : null" x-effect="setCode(snippet)"
    id="live-code" class="group/code relative m-0 overflow-hidden rounded-surface border border-border bg-nq-surface-soft text-start [--shiki-foreground:var(--nq-fg)] [--shiki-background:transparent] [--shiki-token-comment:var(--nq-fg-muted)] [--shiki-token-keyword:var(--nq-accent-text)] [--shiki-token-string:var(--nq-success-text)] [--shiki-token-string-expression:var(--nq-success-text)] [--shiki-token-constant:var(--nq-warning-text)] [--shiki-token-function:var(--nq-info-text)] [--shiki-token-parameter:var(--nq-fg-body)] [--shiki-token-punctuation:var(--nq-fg-muted)] [--shiki-token-link:var(--nq-info-text)]">
            <figcaption data-slot="code-block-header" class="flex h-row items-center justify-between gap-2 border-b border-border ps-3 pe-1.5 font-mono text-caption text-muted-foreground">
            <span class="truncate">install.sh</span>
                            <span class="contents" x-data="nqCopyButton(() => (code), 1500)">
    <button data-slot="copy-button"
     type="button"                         aria-label="Copy code" x-on:click="copy()" x-bind:data-copied="state === &#039;copied&#039; ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0 data-copied:text-nq-success-text">
        <svg aria-hidden="true" x-show="state !== 'copied'" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
  <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
</svg>        <svg aria-hidden="true" x-show="state === 'copied'" style="display: none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></button>
    <span data-slot="copy-button-status" role="status" aria-live="polite" class="sr-only"
        data-copied-label="Code copied to clipboard" data-failed-label="Could not copy"
        x-text="state === 'copied' ? $el.dataset.copiedLabel : state === 'failed' ? $el.dataset.failedLabel : ''"></span>
</span>
                    </figcaption>
        <pre data-slot="code-block-pre" role="region" tabindex="0" aria-label="install.sh"
        class="m-0 overflow-auto bg-transparent py-3 font-mono text-code text-[var(--shiki-foreground)] outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus "><code class="block w-max min-w-full"><span data-line="1"  class="flex min-h-[1lh] border-s-2 border-transparent pe-4 whitespace-pre"><span aria-hidden="true" class="inline-block shrink-0 select-none pe-4 ps-3 text-end text-muted-foreground tabular-nums" style="min-width: 4ch">1</span><span data-code-line>npm i nasaq</span></span></code></pre>
</figure>
    <button data-slot="button"
     type="button"                         id="live-code-change" x-on:click="next()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border-border bg-card text-foreground hover:bg-nq-hover h-control-sm px-2.5 mt-3">
        Use pnpm</button>
</div>

<!-- code-block-ai -->
<figure data-slot="code-block" data-language="ts" dir="ltr" x-data="nqCodeBlock(JSON.parse('{\u0022code\u0022:\u0022export const greet = (name: string) =\\u003E `Hello, ${name}`;\u0022,\u0022language\u0022:\u0022ts\u0022}'))" x-bind:data-highlighted="highlighted ? '' : null"
    class="group/code relative m-0 overflow-hidden rounded-surface border border-border bg-nq-surface-soft text-start [--shiki-foreground:var(--nq-fg)] [--shiki-background:transparent] [--shiki-token-comment:var(--nq-fg-muted)] [--shiki-token-keyword:var(--nq-accent-text)] [--shiki-token-string:var(--nq-success-text)] [--shiki-token-string-expression:var(--nq-success-text)] [--shiki-token-constant:var(--nq-warning-text)] [--shiki-token-function:var(--nq-info-text)] [--shiki-token-parameter:var(--nq-fg-body)] [--shiki-token-punctuation:var(--nq-fg-muted)] [--shiki-token-link:var(--nq-info-text)]">
            <figcaption data-slot="code-block-header" class="flex h-row items-center justify-between gap-2 border-b border-border ps-3 pe-1.5 font-mono text-caption text-muted-foreground">
            <span class="truncate">greet.ts</span>
                            <span data-slot="code-copy-menu" x-data="nqCodeCopyMenu(JSON.parse('{\u0022code\u0022:\u0022export const greet = (name: string) =\\u003E `Hello, ${name}`;\u0022,\u0022markdown\u0022:\u0022**greet.ts**\\n\\n```ts\\nexport const greet = (name: string) =\\u003E `Hello, ${name}`;\\n```\u0022,\u0022prompts\u0022:{\u0022claude\u0022:\u0022Read this snippet, explain what it does, and show how to use it in my project.\\n\\n**greet.ts**\\n\\n```ts\\nexport const greet = (name: string) =\\u003E `Hello, ${name}`;\\n```\\n\u0022,\u0022chatgpt\u0022:\u0022Explain what this snippet does and how to use it in my project.\\n\\n**greet.ts**\\n\\n```ts\\nexport const greet = (name: string) =\\u003E `Hello, ${name}`;\\n```\\n\u0022,\u0022cursor\u0022:\u0022Apply this snippet to my codebase. Match the existing style and keep the change small.\\n\\n**greet.ts**\\n\\n```ts\\nexport const greet = (name: string) =\\u003E `Hello, ${name}`;\\n```\\n\u0022},\u0022links\u0022:{\u0022claude\u0022:\u0022https:\\\/\\\/claude.ai\\\/new?q=Read%20this%20snippet%2C%20explain%20what%20it%20does%2C%20and%20show%20how%20to%20use%20it%20in%20my%20project.%0A%0A%2A%2Agreet.ts%2A%2A%0A%0A%60%60%60ts%0Aexport%20const%20greet%20%3D%20%28name%3A%20string%29%20%3D%3E%20%60Hello%2C%20%24%7Bname%7D%60%3B%0A%60%60%60%0A\u0022,\u0022chatgpt\u0022:\u0022https:\\\/\\\/chatgpt.com\\\/?q=Explain%20what%20this%20snippet%20does%20and%20how%20to%20use%20it%20in%20my%20project.%0A%0A%2A%2Agreet.ts%2A%2A%0A%0A%60%60%60ts%0Aexport%20const%20greet%20%3D%20%28name%3A%20string%29%20%3D%3E%20%60Hello%2C%20%24%7Bname%7D%60%3B%0A%60%60%60%0A\u0022,\u0022cursor\u0022:\u0022cursor:\\\/\\\/anysphere.cursor-deeplink\\\/prompt?text=Apply%20this%20snippet%20to%20my%20codebase.%20Match%20the%20existing%20style%20and%20keep%20the%20change%20small.%0A%0A%2A%2Agreet.ts%2A%2A%0A%0A%60%60%60ts%0Aexport%20const%20greet%20%3D%20%28name%3A%20string%29%20%3D%3E%20%60Hello%2C%20%24%7Bname%7D%60%3B%0A%60%60%60%0A\u0022},\u0022t\u0022:{\u0022code\u0022:\u0022Code copied to clipboard\u0022,\u0022markdown\u0022:\u0022Markdown copied to clipboard\u0022,\u0022prompt\u0022:\u0022Prompt for :name copied to clipboard\u0022,\u0022failed\u0022:\u0022Could not copy\u0022,\u0022tooLong\u0022:\u0022Too long for a link. Prompt copied instead.\u0022},\u0022names\u0022:{\u0022claude\u0022:\u0022Claude\u0022,\u0022chatgpt\u0022:\u0022ChatGPT\u0022,\u0022cursor\u0022:\u0022Cursor\u0022}}'))" class="inline-flex items-center">
    <button data-slot="button"
     type="button"                         aria-label="Copy code" x-on:click="copyCode()" x-bind:data-copied="done ? &#039;&#039; : null" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0 data-copied:text-nq-success-text">
        <svg aria-hidden="true" x-show="! done" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
  <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
</svg>        <svg aria-hidden="true" x-show="done" style="display: none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></button>
    <div data-slot="dropdown-menu" x-data="nqDropdownMenu(false)" x-modelable="open" class="contents"><button data-slot="code-copy-menu-trigger"
     type="button"                         x-bind="trigger" x-ref="trigger" aria-label="Copy options" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover size-control-sm p-0 -ms-1 w-5">
        <svg aria-hidden="true" class="size-3.5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m6 9 6 6 6-6"/>
</svg></button>
        <template x-teleport="body">
    <div data-slot="dropdown-menu-content" x-bind="popup" x-init="popupEl = $el" x-nq-presence="open" x-anchor.bottom-end.offset.4="$refs.trigger"
        class="z-50 overflow-hidden rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none max-h-[var(--available-height)] overflow-y-auto transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0 min-w-56"><div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="copyCode()" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
  <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
</svg> Copy code</div>
            <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="copyMarkdown()" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/>
  <path d="M14 2v5a1 1 0 0 0 1 1h5"/>
  <path d="M10 9H8"/>
  <path d="M16 13H8"/>
  <path d="M16 17H8"/>
</svg> Copy as Markdown</div>
                            <div data-slot="dropdown-menu-separator" role="separator" aria-orientation="horizontal" class="-mx-1.5 my-1.5 h-px bg-border"></div>
                <div data-slot="dropdown-menu-label" role="presentation" class="px-2.5 pt-1.5 pb-1 text-caption font-medium text-muted-foreground">Use with AI</div>
                                    <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="copyPrompt(&#039;claude&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/>
  <path d="M20 2v4"/>
  <path d="M22 4h-4"/>
  <circle cx="4" cy="20" r="2"/>
</svg> <span>Copy prompt for <bdi dir="ltr">Claude</bdi></span></div>
                                    <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="copyPrompt(&#039;chatgpt&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/>
  <path d="M20 2v4"/>
  <path d="M22 4h-4"/>
  <circle cx="4" cy="20" r="2"/>
</svg> <span>Copy prompt for <bdi dir="ltr">ChatGPT</bdi></span></div>
                                    <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="copyPrompt(&#039;cursor&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/>
  <path d="M20 2v4"/>
  <path d="M22 4h-4"/>
  <circle cx="4" cy="20" r="2"/>
</svg> <span>Copy prompt for <bdi dir="ltr">Cursor</bdi></span></div>
                                                    <div data-slot="dropdown-menu-separator" role="separator" aria-orientation="horizontal" class="-mx-1.5 my-1.5 h-px bg-border"></div>
                                            <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="open(&#039;claude&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M15 3h6v6"/>
  <path d="M10 14 21 3"/>
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
</svg> <span>Open in <bdi dir="ltr">Claude</bdi></span></div>
                                            <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="open(&#039;chatgpt&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M15 3h6v6"/>
  <path d="M10 14 21 3"/>
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
</svg> <span>Open in <bdi dir="ltr">ChatGPT</bdi></span></div>
                                            <div data-slot="dropdown-menu-item" role="menuitem" data-variant="default" x-bind="item"
        x-on:click="open(&#039;cursor&#039;)" class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control px-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground"><svg aria-hidden="true" class="rtl:-scale-x-100" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M15 3h6v6"/>
  <path d="M10 14 21 3"/>
  <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
</svg> <span>Open in <bdi dir="ltr">Cursor</bdi></span></div></div>
</template></div>
    <span role="status" aria-live="polite" class="sr-only" x-text="status"></span>
</span>                    </figcaption>
        <pre data-slot="code-block-pre" role="region" tabindex="0" aria-label="greet.ts"
        class="m-0 overflow-auto bg-transparent py-3 font-mono text-code text-[var(--shiki-foreground)] outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-nq-focus "><code class="block w-max min-w-full"><span data-line="1"  class="flex min-h-[1lh] border-s-2 border-transparent pe-4 whitespace-pre ps-3"><span data-code-line>export const greet = (name: string) =&gt; `Hello, ${name}`;</span></span></code></pre>
</figure>
````
