Best use case
Skill: Syntax Highlighting is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
## Purpose
Teams using Skill: Syntax Highlighting should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/syntax-highlighting/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How Skill: Syntax Highlighting Compares
| Feature / Agent | Skill: Syntax Highlighting | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
## Purpose
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Skill: Syntax Highlighting
## Purpose
Implement client-side syntax highlighting using highlight.js 11. Covers language detection, hljs initialization, React integration, supported language list, and the copy-to-clipboard flow.
---
## highlight.js Setup (lazy-load only needed languages)
```typescript
// web/src/lib/hljs.ts
import hljs from 'highlight.js/lib/core';
// Register only the languages the app supports (reduces bundle size)
import typescript from 'highlight.js/lib/languages/typescript';
import javascript from 'highlight.js/lib/languages/javascript';
import python from 'highlight.js/lib/languages/python';
import rust from 'highlight.js/lib/languages/rust';
import go from 'highlight.js/lib/languages/go';
import sql from 'highlight.js/lib/languages/sql';
import xml from 'highlight.js/lib/languages/xml'; // covers HTML
import css from 'highlight.js/lib/languages/css';
import bash from 'highlight.js/lib/languages/bash';
import json from 'highlight.js/lib/languages/json';
import yaml from 'highlight.js/lib/languages/yaml';
import markdown from 'highlight.js/lib/languages/markdown';
import c from 'highlight.js/lib/languages/c';
import cpp from 'highlight.js/lib/languages/cpp';
import java from 'highlight.js/lib/languages/java';
import csharp from 'highlight.js/lib/languages/csharp';
import php from 'highlight.js/lib/languages/php';
import ruby from 'highlight.js/lib/languages/ruby';
import swift from 'highlight.js/lib/languages/swift';
import kotlin from 'highlight.js/lib/languages/kotlin';
import scala from 'highlight.js/lib/languages/scala';
import r from 'highlight.js/lib/languages/r';
import matlab from 'highlight.js/lib/languages/matlab';
import lua from 'highlight.js/lib/languages/lua';
import perl from 'highlight.js/lib/languages/perl';
import haskell from 'highlight.js/lib/languages/haskell';
import elixir from 'highlight.js/lib/languages/elixir';
import erlang from 'highlight.js/lib/languages/erlang';
import clojure from 'highlight.js/lib/languages/clojure';
import ini from 'highlight.js/lib/languages/ini'; // covers TOML approx.
import diff from 'highlight.js/lib/languages/diff';
hljs.registerLanguage('typescript', typescript);
hljs.registerLanguage('javascript', javascript);
hljs.registerLanguage('python', python);
hljs.registerLanguage('rust', rust);
hljs.registerLanguage('go', go);
hljs.registerLanguage('sql', sql);
hljs.registerLanguage('xml', xml);
hljs.registerLanguage('css', css);
hljs.registerLanguage('bash', bash);
hljs.registerLanguage('json', json);
hljs.registerLanguage('yaml', yaml);
hljs.registerLanguage('markdown', markdown);
hljs.registerLanguage('c', c);
hljs.registerLanguage('cpp', cpp);
hljs.registerLanguage('java', java);
hljs.registerLanguage('csharp', csharp);
hljs.registerLanguage('php', php);
hljs.registerLanguage('ruby', ruby);
hljs.registerLanguage('swift', swift);
hljs.registerLanguage('kotlin', kotlin);
hljs.registerLanguage('scala', scala);
hljs.registerLanguage('r', r);
hljs.registerLanguage('matlab', matlab);
hljs.registerLanguage('lua', lua);
hljs.registerLanguage('perl', perl);
hljs.registerLanguage('haskell', haskell);
hljs.registerLanguage('elixir', elixir);
hljs.registerLanguage('erlang', erlang);
hljs.registerLanguage('clojure', clojure);
hljs.registerLanguage('ini', ini);
hljs.registerLanguage('diff', diff);
export { hljs };
```
---
## Supported Languages List
```typescript
// web/src/lib/languages.ts
export interface LanguageOption {
value: string; // hljs alias, stored in DB
label: string; // display name in selector
badge: string; // short abbreviation for badge
color: string; // badge background color
textColor: string;
}
export const LANGUAGES: LanguageOption[] = [
{ value: 'plaintext', label: 'Plain text', badge: 'TXT', color: '#e7e5e4', textColor: '#57534e' },
{ value: 'typescript', label: 'TypeScript', badge: 'TS', color: '#3178c6', textColor: '#ffffff' },
{ value: 'javascript', label: 'JavaScript', badge: 'JS', color: '#f7df1e', textColor: '#1c1917' },
{ value: 'python', label: 'Python', badge: 'PY', color: '#3572a5', textColor: '#ffffff' },
{ value: 'rust', label: 'Rust', badge: 'RS', color: '#dea584', textColor: '#1c1917' },
{ value: 'go', label: 'Go', badge: 'GO', color: '#00add8', textColor: '#1c1917' },
{ value: 'sql', label: 'SQL', badge: 'SQL', color: '#e38c00', textColor: '#ffffff' },
{ value: 'xml', label: 'HTML', badge: 'HTML',color: '#e34c26', textColor: '#ffffff' },
{ value: 'css', label: 'CSS', badge: 'CSS', color: '#563d7c', textColor: '#ffffff' },
{ value: 'bash', label: 'Shell/Bash', badge: 'SH', color: '#89e051', textColor: '#1c1917' },
{ value: 'json', label: 'JSON', badge: 'JSON',color: '#57534e', textColor: '#ffffff' },
{ value: 'yaml', label: 'YAML', badge: 'YAML',color: '#cb171e', textColor: '#ffffff' },
{ value: 'markdown', label: 'Markdown', badge: 'MD', color: '#083fa1', textColor: '#ffffff' },
{ value: 'c', label: 'C', badge: 'C', color: '#555555', textColor: '#ffffff' },
{ value: 'cpp', label: 'C++', badge: 'C++', color: '#f34b7d', textColor: '#ffffff' },
{ value: 'java', label: 'Java', badge: 'JV', color: '#b07219', textColor: '#ffffff' },
{ value: 'csharp', label: 'C#', badge: 'C#', color: '#178600', textColor: '#ffffff' },
{ value: 'php', label: 'PHP', badge: 'PHP', color: '#4f5d95', textColor: '#ffffff' },
{ value: 'ruby', label: 'Ruby', badge: 'RB', color: '#701516', textColor: '#ffffff' },
{ value: 'swift', label: 'Swift', badge: 'SW', color: '#f05138', textColor: '#ffffff' },
{ value: 'kotlin', label: 'Kotlin', badge: 'KT', color: '#7f52ff', textColor: '#ffffff' },
{ value: 'scala', label: 'Scala', badge: 'SC', color: '#c22d40', textColor: '#ffffff' },
{ value: 'r', label: 'R', badge: 'R', color: '#276dc3', textColor: '#ffffff' },
{ value: 'lua', label: 'Lua', badge: 'LUA', color: '#000080', textColor: '#ffffff' },
{ value: 'perl', label: 'Perl', badge: 'PL', color: '#0298c3', textColor: '#ffffff' },
{ value: 'haskell', label: 'Haskell', badge: 'HS', color: '#5e5086', textColor: '#ffffff' },
{ value: 'elixir', label: 'Elixir', badge: 'EX', color: '#6e4a7e', textColor: '#ffffff' },
{ value: 'diff', label: 'Diff/Patch', badge: 'DIFF',color: '#88562a', textColor: '#ffffff' },
{ value: 'ini', label: 'INI/TOML', badge: 'INI', color: '#d1dae3', textColor: '#1c1917' },
];
export function findLanguage(value: string): LanguageOption {
return LANGUAGES.find(l => l.value === value) ?? LANGUAGES[0];
}
```
---
## PasteViewer Component
```tsx
// web/src/components/PasteViewer.tsx
import { useMemo } from 'react';
import { hljs } from '../lib/hljs';
interface Props {
content: string;
language: string; // hljs alias
showLineNumbers?: boolean;
}
export function PasteViewer({ content, language, showLineNumbers = true }: Props) {
const highlighted = useMemo(() => {
if (language === 'plaintext') {
// Escape HTML manually for plain text
return content
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
}
try {
return hljs.highlight(content, { language }).value;
} catch {
// Fallback to plain text if language not registered
return content.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
}
}, [content, language]);
const lines = highlighted.split('\n');
return (
<div style={{ background: '#1c1917', borderRadius: 16, overflow: 'hidden', border: '1px solid #292524' }}>
<div style={{ display: 'flex', overflowX: 'auto' }}>
{showLineNumbers && (
<div style={{
padding: '16px 12px 16px 16px',
textAlign: 'right',
userSelect: 'none',
fontFamily: 'var(--font-mono)',
fontSize: 13,
lineHeight: 1.6,
color: '#57534e',
minWidth: 40,
borderRight: '1px solid #292524',
flexShrink: 0,
}}>
{lines.map((_, i) => (
<div key={i}>{i + 1}</div>
))}
</div>
)}
<pre style={{
padding: 16,
fontFamily: 'var(--font-mono)',
fontSize: 13,
lineHeight: 1.6,
color: '#e7e5e4',
flex: 1,
margin: 0,
overflow: 'visible',
}}>
<code dangerouslySetInnerHTML={{ __html: highlighted }} />
</pre>
</div>
</div>
);
}
```
---
## LanguageBadge Component
```tsx
// web/src/components/LanguageBadge.tsx
import { findLanguage } from '../lib/languages';
interface Props {
language: string;
size?: 'sm' | 'md';
}
export function LanguageBadge({ language, size = 'md' }: Props) {
const lang = findLanguage(language);
return (
<span style={{
background: lang.color,
color: lang.textColor,
borderRadius: 9999,
padding: size === 'sm' ? '2px 8px' : '3px 10px',
fontSize: size === 'sm' ? 11 : 12,
fontWeight: 600,
display: 'inline-flex',
alignItems: 'center',
flexShrink: 0,
}}>
{lang.badge}
</span>
);
}
```
---
## LanguageSelector Component
```tsx
// web/src/components/LanguageSelector.tsx
import { LANGUAGES } from '../lib/languages';
interface Props {
value: string;
onChange: (value: string) => void;
}
export function LanguageSelector({ value, onChange }: Props) {
return (
<select
className="select"
value={value}
onChange={e => onChange(e.target.value)}
style={{ fontSize: 13 }}
>
{LANGUAGES.map(lang => (
<option key={lang.value} value={lang.value}>
{lang.label}
</option>
))}
</select>
);
}
```
---
## Copy to Clipboard Hook
```typescript
// web/src/hooks/useCopy.ts
import { useState, useCallback } from 'react';
export function useCopy(text: string, resetMs = 2000) {
const [copied, setCopied] = useState(false);
const copy = useCallback(async () => {
try {
if (navigator.clipboard) {
await navigator.clipboard.writeText(text);
} else {
// Fallback for older browsers
const el = document.createElement('textarea');
el.value = text;
el.style.position = 'fixed';
el.style.opacity = '0';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
}
setCopied(true);
setTimeout(() => setCopied(false), resetMs);
} catch {
console.error('Copy failed');
}
}, [text, resetMs]);
return { copied, copy };
}
```
---
## CopyButton Component
```tsx
// web/src/components/CopyButton.tsx
import { useCopy } from '../hooks/useCopy';
interface Props {
text: string;
size?: 'sm' | 'md';
}
export function CopyButton({ text, size = 'sm' }: Props) {
const { copied, copy } = useCopy(text);
return (
<button
className={`btn btn-secondary${size === 'sm' ? ' btn-sm' : ''}`}
onClick={copy}
title="Copy to clipboard"
>
{copied ? 'Copied!' : 'Copy'}
</button>
);
}
```
---
## hljs CSS Theme (dark, custom tokens)
```css
/* web/src/styles/hljs-theme.css */
/* Inline token colours matching the design mock palette */
.hljs-keyword { color: #c792ea; }
.hljs-built_in { color: #82aaff; }
.hljs-type { color: #82aaff; }
.hljs-literal { color: #ff5874; }
.hljs-number { color: #f78c6c; }
.hljs-string { color: #c3e88d; }
.hljs-template-tag { color: #89ddff; }
.hljs-template-variable { color: #89ddff; }
.hljs-comment { color: #546e7a; font-style: italic; }
.hljs-doctag { color: #546e7a; font-style: italic; }
.hljs-meta { color: #89ddff; }
.hljs-attr { color: #ffcb6b; }
.hljs-attribute { color: #a8a29e; }
.hljs-name { color: #f07178; }
.hljs-section { color: #82aaff; font-weight: bold; }
.hljs-tag { color: #f07178; }
.hljs-variable { color: #eeffff; }
.hljs-symbol { color: #89ddff; }
.hljs-bullet { color: #89ddff; }
.hljs-link { color: #c3e88d; text-decoration: underline; }
.hljs-selector-class { color: #ffcb6b; }
.hljs-selector-id { color: #82aaff; }
.hljs-selector-tag { color: #f07178; }
.hljs-deletion { background: #3d1515; color: #ff5874; }
.hljs-addition { background: #1a3d1a; color: #c3e88d; }
```
---
## Vitest Tests
```typescript
// web/src/lib/__tests__/hljs.test.ts
import { describe, it, expect } from 'vitest';
import { hljs } from '../hljs';
import { findLanguage, LANGUAGES } from '../languages';
describe('hljs', () => {
it('highlights TypeScript without throwing', () => {
const result = hljs.highlight('const x: number = 1;', { language: 'typescript' });
expect(result.value).toContain('hljs-keyword');
});
it('returns fallback for unregistered language', () => {
// plaintext should not throw
expect(() => {
hljs.highlight('hello world', { language: 'plaintext' });
}).toThrow(); // plaintext is not registered; we handle this in PasteViewer
});
});
describe('findLanguage', () => {
it('returns plaintext for unknown value', () => {
expect(findLanguage('unknown').value).toBe('plaintext');
});
it('finds typescript', () => {
expect(findLanguage('typescript').badge).toBe('TS');
});
it('covers all registered hljs languages', () => {
const hljsLangs = new Set(['plaintext', 'typescript', 'javascript', 'python', 'rust', 'go',
'sql', 'xml', 'css', 'bash', 'json', 'yaml', 'markdown']);
for (const lang of LANGUAGES) {
if (hljsLangs.has(lang.value)) {
expect(lang.label.length).toBeGreaterThan(0);
}
}
});
});
```
---
## Character and Line Counter Hook
```typescript
// web/src/hooks/useEditorStats.ts
import { useMemo } from 'react';
export function useEditorStats(content: string) {
return useMemo(() => {
const lines = content.split('\n').length;
const chars = content.length;
const bytes = new TextEncoder().encode(content).length;
const overLimit = bytes > 524288; // 512 KB
return { lines, chars, bytes, overLimit };
}, [content]);
}
```
---
## Key Integration Notes
1. Import `highlight.js/lib/core` (not the full bundle) to avoid a 900 KB bundle.
2. Each language registration adds roughly 5-20 KB gzipped; all 30 languages together are under 200 KB gzipped.
3. The `PasteViewer` uses `dangerouslySetInnerHTML` only with output from `hljs.highlight()`, not with raw user content. The content is HTML-escaped by hljs before token wrapping.
4. Plain text pastes skip hljs entirely and receive manual `&`, `<`, `>` escaping to prevent XSS.
5. Server does not run hljs. Highlighting is purely client-side to avoid Node.js overhead on large pastes.
6. The `useCopy` fallback uses `document.execCommand('copy')` which is deprecated but needed for non-HTTPS environments during local development.Related Skills
Skill: Uptime Monitoring
## Overview
Skill: Status Page
## Overview
Skill: unit-conversion
## Overview
Skill: recipe-scaler
## Overview
reading-list
Operate the reading-list API to save, manage, tag, search, and export articles.
email-digest
Configure, test, and troubleshoot the reading-list daily email digest delivered via nodemailer.
websocket-realtime
Use the WebSocket connection in poll-builder to receive live vote updates. Use when you need to stream real-time poll results, monitor a poll for new votes, or build a live dashboard. Triggers include "live results", "real-time updates", "stream votes", "watch poll", or "WebSocket".
poll-builder
Self-hosted poll creation tool with real-time results. Use when you need to create a poll, check vote counts, close a poll, export results, or get the shareable link for a poll. Triggers include "create poll", "vote", "poll results", "survey", "collect votes", "share poll", or any task involving polling or voting.
Skill: personal-finance
## Overview
Skill: csv-import
## Overview
Skill: Pastebin Core
## Purpose
Skill: Cost Reporting
## Overview