Перейти к содержимому

Документация

Блок кода

Кодовые блоки с подсветкой синтаксиса Shiki, копированием и встроенным поиском.


Блок кода

Компонент Pre оборачивает кодовые блоки с подсветкой синтаксиса через Shiki, кнопкой копирования и встроенным поиском. Используется автоматически для всех ``` блоков в mdsvex.

Превью

const greeting = 'Привет, мир!';
console.log(greeting);
export default greeting;
<script lang="ts">
	import type { Snippet } from 'svelte';
	import { cn } from '$lib/utils/cn';
	import ScrollArea from '$lib/components/ui/ScrollArea.svelte';
	import CopyCodeButton from './CopyCodeButton.svelte';
	import Search from 'carbon-icons-svelte/lib/Search.svelte';

	type ComponentProps = {
		class?: string;
		children?: Snippet;
		code?: string;
		unstyled?: boolean;
		[prop: string]: unknown;
	};

	const props = $props();
	const className = $derived((props as ComponentProps).class ?? '');
	const code = $derived((props as ComponentProps).code ?? '');
	const unstyled = $derived((props as ComponentProps).unstyled ?? false);
	const children = $derived((props as ComponentProps).children);
	let searchOpen = $state(false);
	let searchQuery = $state('');
	const hasSearchMatch = $derived(
		searchQuery.length > 0 && code.toLowerCase().includes(searchQuery.toLowerCase())
	);

	const restProps = $derived.by(() => {
		const {
			class: _class,
			children: _children,
			code: _code,
			unstyled: _unstyled,
			...rest
		} = props as ComponentProps;
		return rest;
	});
</script>

<div class="inset-shadow mt-8 rounded-lg bg-background-inset p-1.5">
	<div
		{...restProps}
		class={cn(
			unstyled
				? 'group/pre relative font-mono text-base font-normal'
				: 'group/pre relative rounded-md bg-background p-4 font-mono text-base font-normal text-foreground card',
			className
		)}
	>
		<ScrollArea mode="horizontal" class="w-full" thumbTabbable={false}>
			{@render children?.()}
		</ScrollArea>
		{#if code}
			<div class="pointer-events-none absolute top-2 right-2 z-10 flex items-center gap-2">
				{#if searchOpen}
					<input
						bind:value={searchQuery}
						class="pointer-events-auto h-7 w-40 rounded-sm bg-background-inset px-2 text-xs text-foreground"
						placeholder="Search code"
					/>
				{/if}
				<button
					type="button"
					class="pointer-events-auto inset-shadow flex size-7 items-center justify-center rounded-sm bg-background-inset text-foreground"
					class:text-accent={hasSearchMatch}
					onclick={() => (searchOpen = !searchOpen)}
					aria-label="Search code"><Search size={16} /></button
				>
				<CopyCodeButton {code} class="pointer-events-auto" />
			</div>
		{/if}
	</div>
</div>

<style>
	:global(.shiki) {
		background-color: transparent !important;
		font-size: 14px;
		font-weight: 400;
	}
</style>
<script lang="ts">
	import type { Snippet } from 'svelte';
	import { cn } from '$lib/utils/cn';
	import ScrollArea from '$lib/components/ui/ScrollArea.svelte';
	import CopyCodeButton from './CopyCodeButton.svelte';
	import Search from 'carbon-icons-svelte/lib/Search.svelte';

	type ComponentProps = {
		class?: string;
		children?: Snippet;
		code?: string;
		unstyled?: boolean;
		[prop: string]: unknown;
	};

	const props = $props();
	const className = $derived((props as ComponentProps).class ?? '');
	const code = $derived((props as ComponentProps).code ?? '');
	const unstyled = $derived((props as ComponentProps).unstyled ?? false);
	const children = $derived((props as ComponentProps).children);
	let searchOpen = $state(false);
	let searchQuery = $state('');
	const hasSearchMatch = $derived(
		searchQuery.length > 0 && code.toLowerCase().includes(searchQuery.toLowerCase())
	);

	const restProps = $derived.by(() => {
		const {
			class: _class,
			children: _children,
			code: _code,
			unstyled: _unstyled,
			...rest
		} = props as ComponentProps;
		return rest;
	});
</script>

<div class="inset-shadow mt-8 rounded-lg bg-background-inset p-1.5">
	<div
		{...restProps}
		class={cn(
			unstyled
				? 'group/pre relative font-mono text-base font-normal'
				: 'group/pre relative rounded-md bg-background p-4 font-mono text-base font-normal text-foreground card',
			className
		)}
	>
		<ScrollArea mode="horizontal" class="w-full" thumbTabbable={false}>
			{@render children?.()}
		</ScrollArea>
		{#if code}
			<div class="pointer-events-none absolute top-2 right-2 z-10 flex items-center gap-2">
				{#if searchOpen}
					<input
						bind:value={searchQuery}
						class="pointer-events-auto h-7 w-40 rounded-sm bg-background-inset px-2 text-xs text-foreground"
						placeholder="Search code"
					/>
				{/if}
				<button
					type="button"
					class="pointer-events-auto inset-shadow flex size-7 items-center justify-center rounded-sm bg-background-inset text-foreground"
					class:text-accent={hasSearchMatch}
					onclick={() => (searchOpen = !searchOpen)}
					aria-label="Search code"><Search size={16} /></button
				>
				<CopyCodeButton {code} class="pointer-events-auto" />
			</div>
		{/if}
	</div>
</div>

<style>
	:global(.shiki) {
		background-color: transparent !important;
		font-size: 14px;
		font-weight: 400;
	}
</style>

Нажмите иконку поиска — появится поле для поиска по коду. Иконка копирования — копирует в буфер.

Особенности

  • Подсветка синтаксиса через Shiki (темы github-light / github-dark)
  • Кнопка копирования в буфер обмена
  • Встроенный поиск с подсветкой совпадений
  • Горизонтальный скролл для длинных строк (ScrollArea)
  • Адаптируется к теме сайта

В markdown (.svx)

Кодовые блоки преобразуются автоматически:

```typescript
export function add(a: number, b: number) {
	return a + b;
}
```
```typescript
export function add(a: number, b: number) {
	return a + b;
}
```

Прямое использование

<script>
	import Pre from '$lib/components/docs/markdown/Pre.svelte';
</script>

<Pre code="const x = 42;">
	<!-- Подсвеченный HTML от Shiki -->
</Pre>
<script>
	import Pre from '$lib/components/docs/markdown/Pre.svelte';
</script>

<Pre code="const x = 42;">
	<!-- Подсвеченный HTML от Shiki -->
</Pre>

Пропсы

Пропс Тип По умолчанию Описание
code string '' Исходный код для поиска и копирования
unstyled boolean false Отключить стили обёртки
class string '' Дополнительные CSS-классы
children Snippet Подсвеченный HTML код

Поддерживаемые языки

typescript, svelte, bash, json, html, css, markdown, text и другие — список задаётся в svelte.config.ts.