# Code Block

## Usage

`CodeBlock` shows a read-only snippet. Import it from `@astryx-vue/core/Code`, next to inline `Code`. The `code` string is what the reader sees and what the copy button writes.

`language` selects the tokenizer. `title` adds a filename in the header. `hasLineNumbers` draws a gutter. `highlightLines` takes one-indexed line numbers and tints those lines. `maxHeight` bounds the body before it scrolls. `hasCopyButton` stays available in the header, or floats at the top end when there is no header.

`isDiff` paints the snippet as a unified diff. A leading `+` marks an added line, `-` a removed line, and `@@` a hunk header. A `--- ` line, a `+++ ` line, and an `@@` line in that order are a file header. The glyph sits in its own column, and the rest of the line keeps the `language` syntax colors. The copy button still writes the original string, glyphs included. The header and the viewport name read `diff`, or `typescript diff` when a language is set. A highlighted line replaces the diff tint. The colors are `--astryx-code-syntax-added`, `--astryx-code-syntax-removed`, `--astryx-code-syntax-diff-header`, plus the `added-marker` and `removed-marker` slots, the same `--astryx-code-syntax-*` scheme as the other syntax colors.

`SyntaxTheme` sets the syntax custom properties for the blocks inside it. `githubDark` is the dark preset shipped with the package. Wrap a bash block in it when the snippet should read as a terminal on a light page. `defineSyntaxTheme` builds another preset from the same token names.

```vue
<script setup lang="ts">
import { CodeBlock, githubDark, SyntaxTheme } from '@astryx-vue/core/Code'

const commands = `$ astryx init --features agents`
</script>

<template>
  <SyntaxTheme :theme="githubDark">
    <CodeBlock :code="commands" language="bash" has-copy-button width="100%" />
  </SyntaxTheme>
</template>
```

### Anatomy

- **Header Bar**: Shows the title, visible language label, and copy button when a header is present. A title or visible language label creates the header; the copy button alone floats at the top-end of a headerless block.
- **Header Title**: Groups the optional title, visible language label, and collapsible chevron inside the header bar.
- **Line Numbers**: Numbered gutter along the left edge. Enable with hasLineNumbers.
- **Code Body**: The syntax-highlighted code content.
- **Highlighted Lines**: Background accent on specific lines to draw attention.
- **Copy Button**: Copies the code string to the clipboard. Shown by default.

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Set `language` to the snippet, and add `title` when the code is a file. |
| Do | Leave line numbers off for a one-line command, and turn them on when a reader must point at a line. |
| Do | Use `highlightLines` for the lines a tutorial just changed. When the text is already a diff, use `isDiff` instead of painting those lines by hand. |
| Do | Reach for `githubDark` through `SyntaxTheme` when a command should look like a terminal. |
| Don't | Paint a dark box with one-off CSS. A preset keeps the token colors and the border together. |
| Don't | Put a paragraph of prose in a code block. |

## Examples

### Code — Snippet

Short terminal commands with a copy button and no line numbers. Use for install instructions or one-liner commands that readers will paste directly.

```vue
<script setup lang="ts">
import { CodeBlock } from '@astryx-vue/core/Code'
import { VStack } from '@astryx-vue/core/Stack'
</script>

<template>
  <VStack :gap="4" width="100%" :max-width="400">
    <CodeBlock code="npm install @astryxdesign/core" language="bash" has-copy-button width="100%" />
    <CodeBlock code="yarn add @astryxdesign/theme-neutral" language="bash" has-copy-button width="100%" />
  </VStack>
</template>
```

### Code — Highlighted

TypeScript code with specific lines highlighted to draw attention to a key section. Use highlightLines to call out new or important code in tutorials and changelogs.

```vue
<script setup lang="ts">
import { CodeBlock } from '@astryx-vue/core/Code'

const code = `interface User {
  id: string;
  name: string;
}

export function useUser(id: string) {
  const [user, setUser] = useState<User>();
  useEffect(() => {
    fetchUser(id).then(setUser);
  }, [id]);
  return user;
}`
</script>

<template>
  <CodeBlock
    :code="code"
    language="typescript"
    title="useUser.ts"
    has-line-numbers
    :highlight-lines="[8, 9, 10]"
  />
</template>
```

### Code — Diff

The same TypeScript snippet read as a diff. Added and removed rows are tinted, and the code after the glyph stays highlighted as tsx. File headers and @@ lines stay quiet.

```vue
<script setup lang="ts">
import { CodeBlock } from '@astryx-vue/core/Code'

const code = `--- a/useUser.ts
+++ b/useUser.ts
@@ -1,6 +1,6 @@
 export function useUser(id: string) {
-  const [user, setUser] = useState<User>()
+  const [user, setUser] = useState<User | null>(null)
   useEffect(() => {
-    fetchUser(id).then(setUser)
+    void fetchUser(id).then(setUser)
   }, [id])
   return user
 }`
</script>

<template>
  <CodeBlock
    :code="code"
    language="tsx"
    title="useUser.ts"
    is-diff
    has-line-numbers
    width="100%"
  />
</template>
```

### Code — Config

A JSON configuration file with a title bar and line numbers. The title prop adds a filename label in the header so readers know which file the code belongs to.

```vue
<script setup lang="ts">
import { CodeBlock } from '@astryx-vue/core/Code'

const code = `{
  "name": "@astryxdesign/core",
  "version": "0.0.5",
  "dependencies": {
    "@astryxdesign/theme-neutral": "^0.1.0",
    "react": "^19.0.0"
  },
  "scripts": {
    "build": "tsup",
    "test": "vitest"
  }
}`
</script>

<template>
  <CodeBlock :code="code" language="json" title="package.json" has-line-numbers />
</template>
```

### Code — Scrollable

A long code block with a max height that enables vertical scrolling. Use maxHeight to keep the block from dominating the page when displaying large files.

```vue
<script setup lang="ts">
import { CodeBlock } from '@astryx-vue/core/Code'

const code = Array.from({ length: 50 }, (_, index) => `const line${index + 1} = ${index + 1};`).join('\n')
</script>

<template>
  <CodeBlock :code="code" language="typescript" title="many-lines.ts" has-line-numbers :max-height="280" />
</template>
```

### Code — Terminal

A dark terminal-style command block: a bash CodeBlock wrapped in SyntaxTheme with the GitHub Dark preset, copy button on, and no line numbers. Use for shell sessions or CLI output that should read as a terminal even on light pages.

```vue
<script setup lang="ts">
import { CodeBlock, githubDark, SyntaxTheme } from '@astryx-vue/core/Code'

const commands = `$ astryx init --features agents
✓ AI agent docs installed → AGENTS.md
$ pnpm astryx component CodeBlock --dense`
</script>

<template>
  <SyntaxTheme :theme="githubDark" style="width: 100%; max-width: 480px">
    <CodeBlock :code="commands" language="bash" has-copy-button width="100%" />
  </SyntaxTheme>
</template>
```

## Properties

`CodeBlock` and `SyntaxTheme` are exported from `@astryx-vue/core/Code`. Inline `Code`, the tokenizer, and the copy helper are documented on the Code page.

## CodeBlock props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `code` | `string` | — | The read-only code. The copy button writes this string. |
| `language` | `string` | `'plaintext'` | Tokenizer language. `plaintext` hides the language label. |
| `syntaxTheme` | `SyntaxThemeDefinition` | — | Syntax colors for this block. Shorthand for wrapping it in `SyntaxTheme`. |
| `title` | `string` | — | Filename or label in the header. |
| `hasLanguageLabel` | `boolean` | `true` | Whether the language name appears in the header. |
| `hasLineNumbers` | `boolean` | `false` | Whether to draw a line number gutter. |
| `highlightLines` | `number[]` | — | One-indexed lines to emphasize. On a diff row, the highlight replaces the diff tint. |
| `isDiff` | `boolean` | `false` | Paint the snippet as a unified diff. Text after the glyph keeps the `language` syntax colors. Copy still writes the original string. |
| `hasCopyButton` | `boolean` | `true` | Whether to show the copy button. |
| `isWrapped` | `boolean` | `false` | Whether long lines wrap instead of scrolling sideways. |
| `maxHeight` | `number \| string` | — | Maximum height before the body scrolls. Numbers are pixels. |
| `isCollapsible` | `boolean` | `false` | Whether the header can collapse the body once the line count reaches the threshold. |
| `collapsibleThreshold` | `number` | `10` | Lines required before the collapse control appears. |
| `size` | `'sm' \| 'md'` | `'md'` | Text size. |
| `width` | `string` | `'fit-content'` | CSS width. `100%` fills the parent. |
| `container` | `'card' \| 'section'` | `'card'` | `section` drops the border, radius, and background. |
| `tokenizer` | `(code, language) => SyntaxToken[]` | — | Custom tokenizer. Tokens use absolute offsets and do not replace the copied text. |
| `highlightMode` | `'auto' \| 'ranges' \| 'spans'` | `'auto'` | How tokens are painted. |
| `copyLabel` | `string` | `'Copy code'` | Accessible name of the copy control before a copy. |
| `copiedLabel` | `string` | `'Copied'` | Name and announcement after a successful copy. |
| `codeLabel` | `string` | `'Code'` | Accessible name of the code viewport. |

## CodeBlock events

- `copy`: emitted after the code string was written to the clipboard.

## SyntaxTheme

| Name | Type | Description |
| --- | --- | --- |
| `theme` | `SyntaxThemeDefinition` | A preset or a `defineSyntaxTheme()` result. A `[light, dark]` tuple is stored as `light-dark()`. The component page is Syntax Theme . |

The default slot is the content that should pick up those colors, usually one or more `CodeBlock`s.

`githubDark` sets `--astryx-code-syntax-*` and `--astryx-code-block-background`. A dark background also sets `--astryx-code-block-border` to white at 12% opacity. Diff colors are optional slots in that same list: `added`, `removed`, `diff-header`, `added-marker`, and `removed-marker`. When a syntax theme leaves them out, markers follow its punctuation color, the header row gets no extra paint, and the added and removed tints stay on the page theme's slots.
