| | |
| | |
| | |
| | |
| |
|
| | import yaml from 'js-yaml' |
| | import fs from 'fs' |
| | import { visit } from 'unist-util-visit' |
| | import { h } from 'hastscript' |
| | import octicons from '@primer/octicons' |
| | import { parse } from 'parse5' |
| | import { fromParse5 } from 'hast-util-from-parse5' |
| | import murmur from 'imurmurhash' |
| | import { getPrompt } from './copilot-prompt' |
| | import { generatePromptId } from '../lib/prompt-id' |
| | import type { Element } from 'hast' |
| |
|
| | interface LanguageConfig { |
| | name: string |
| | |
| | [key: string]: any |
| | } |
| |
|
| | type Languages = Record<string, LanguageConfig> |
| |
|
| | const languages = yaml.load(fs.readFileSync('./data/code-languages.yml', 'utf8')) as Languages |
| |
|
| | |
| | const matcher = (node: any): boolean => |
| | node.type === 'element' && |
| | node.tagName === 'pre' && |
| | |
| | |
| | (getPreMeta(node).copy || getPreMeta(node).prompt) && |
| | |
| | !getPreMeta(node).annotate |
| |
|
| | export default function codeHeader() { |
| | |
| | return (tree: any) => { |
| | |
| | visit(tree, matcher, (node: any, index: number | undefined, parent: any) => { |
| | if (index !== undefined && parent) { |
| | parent.children[index] = wrapCodeExample(node, tree) |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | function wrapCodeExample(node: any, tree: any): Element { |
| | const lang: string = node.children[0].properties.className?.[0].replace('language-', '') |
| | const code: string = node.children[0].children[0].value |
| |
|
| | const subnav = null |
| | const hasPrompt: boolean = Boolean(getPreMeta(node).prompt) |
| | const promptResult = hasPrompt ? getPrompt(node, tree, code) : null |
| | const hasCopy: boolean = Boolean(getPreMeta(node).copy) |
| |
|
| | const headerHast = header( |
| | lang, |
| | code, |
| | subnav, |
| | promptResult?.element ?? null, |
| | hasCopy, |
| | promptResult?.promptContent, |
| | ) |
| |
|
| | return h('div', { className: 'code-example' }, [headerHast, node]) |
| | } |
| |
|
| | export function header( |
| | lang: string, |
| | code: string, |
| | subnav: Element | null = null, |
| | prompt: Element | null = null, |
| | hasCopy: boolean = true, |
| | promptContent?: string, |
| | ): Element { |
| | const codeId: string = murmur('js-btn-copy').hash(code).result().toString() |
| |
|
| | return h( |
| | 'header', |
| | { |
| | class: [ |
| | 'd-flex', |
| | 'flex-items-center', |
| | 'flex-justify-between', |
| | 'p-2', |
| | 'text-small', |
| | 'rounded-top-1', |
| | 'border-top', |
| | 'border-left', |
| | 'border-right', |
| | ], |
| | }, |
| | [ |
| | h('span', { className: 'flex-1' }, languages[lang]?.name), |
| | subnav, |
| | prompt, |
| | hasCopy |
| | ? h( |
| | 'button', |
| | { |
| | class: ['js-btn-copy', 'btn', 'btn-sm', 'tooltipped', 'tooltipped-nw'], |
| | 'aria-label': `Copy ${languages[lang]?.name} code to clipboard`, |
| | 'data-clipboard': codeId, |
| | }, |
| | btnIcon(), |
| | ) |
| | : null, |
| | h('pre', { hidden: true, 'data-clipboard': codeId }, code), |
| | promptContent |
| | ? h('pre', { hidden: true, id: generatePromptId(promptContent) }, promptContent) |
| | : null, |
| | ], |
| | ) |
| | } |
| |
|
| | function btnIcon(): Element { |
| | const btnIconHtml: string = octicons.copy.toSVG() |
| | const btnIconAst = parse(String(btnIconHtml), { sourceCodeLocationInfo: true }) |
| | |
| | |
| | const btnIconElement = fromParse5(btnIconAst, { file: btnIconHtml as any }) |
| | return btnIconElement as Element |
| | } |
| |
|
| | |
| | |
| | export function getPreMeta(node: any): Record<string, any> { |
| | |
| | |
| | return node.children[0]?.data?.meta || {} |
| | } |
| |
|