import type { AdvancedEventLabSlice } from '@/lib/advanced-event-lab-slice' import { AdvancedLabCitationPickerDialog, type LabCitationDisplayType } from '@/components/AdvancedEventLab/AdvancedLabCitationPickerDialog' import { Button } from '@/components/ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { Input } from '@/components/ui/input' import { ScrollArea } from '@/components/ui/scroll-area' import type { EditorView } from '@codemirror/view' import { Anchor, BookMarked, Braces, ChevronDown, Code2, Film, Heading, Hash, Image as ImageIcon, Link2, List, ListOrdered, Minus, Pilcrow, Quote, Sigma, Table2, Type, ListTodo, Volume2 } from 'lucide-react' import type { MutableRefObject } from 'react' import { Fragment, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { labInsertRaw, labInsertRawWithOptionalBlockLeadNl, labInsertSnippet, labWrapOrSnippet } from './markup-insert' /** Languages for fenced / source blocks (labels are English; widely recognized by highlighters). */ const CODE_LANGUAGES = [ 'text', 'markdown', 'asciidoc', 'javascript', 'typescript', 'tsx', 'jsx', 'json', 'yaml', 'toml', 'html', 'xml', 'css', 'scss', 'sass', 'sql', 'python', 'rust', 'go', 'c', 'cpp', 'csharp', 'java', 'kotlin', 'swift', 'ruby', 'php', 'bash', 'shell', 'powershell', 'dockerfile', 'graphql', 'http', 'diff', 'ini', 'makefile', 'lua', 'r', 'matlab', 'latex', 'haskell', 'elixir', 'scala', 'zig', 'nim', 'wasm', 'protobuf' ] as const const LAB_CITATION_MENU_ITEMS: { type: LabCitationDisplayType; labelKey: string }[] = [ { type: 'inline', labelKey: 'Advanced lab citation type inline' }, { type: 'quote', labelKey: 'Advanced lab citation type quote' }, { type: 'end', labelKey: 'Advanced lab citation type end' }, { type: 'foot', labelKey: 'Advanced lab citation type foot' }, { type: 'foot-end', labelKey: 'Advanced lab citation type footEnd' }, { type: 'prompt-inline', labelKey: 'Advanced lab citation type promptInline' }, { type: 'prompt-end', labelKey: 'Advanced lab citation type promptEnd' } ] export type AdvancedEventLabMarkupToolbarProps = { markupMode: 'markdown' | 'asciidoc' viewRef: MutableRefObject sliceRef: MutableRefObject } export function AdvancedEventLabMarkupToolbar({ markupMode, viewRef, sliceRef }: AdvancedEventLabMarkupToolbarProps) { const { t } = useTranslation() const [codeFilter, setCodeFilter] = useState('') const [langFilter, setLangFilter] = useState('') const [citationPickerOpen, setCitationPickerOpen] = useState(false) const [citationDisplayType, setCitationDisplayType] = useState('inline') const openCitationPicker = (displayType: LabCitationDisplayType) => { setCitationDisplayType(displayType) setCitationPickerOpen(true) } const filteredLangs = useMemo(() => { const q = codeFilter.trim().toLowerCase() if (!q) return [...CODE_LANGUAGES] return CODE_LANGUAGES.filter((id) => id.toLowerCase().includes(q)) }, [codeFilter]) const run = (fn: (v: EditorView) => void) => { const v = viewRef.current if (!v) return fn(v) } const citationPicker = ( run((v) => labInsertRawWithOptionalBlockLeadNl(v, sliceRef, `${macro}\n`)) } /> ) const citationDropdown = ( {t('Advanced lab tb citationsHint')} {LAB_CITATION_MENU_ITEMS.map(({ type, labelKey }) => ( openCitationPicker(type)}> {t(labelKey)} ))} ) /** Contiguous document header per https://docs.asciidoctor.org/asciidoc/latest/document/header/ (no blank lines until after the last header line). */ const adocInsertFullHeader = (titleLine: string) => { run((v) => labInsertRawWithOptionalBlockLeadNl( v, sliceRef, `${titleLine}\n${t('Advanced lab tb adocHeaderAuthorLine')}\n${t('Advanced lab tb adocHeaderRevisionLine')}\n${t('Advanced lab tb adocHeaderAttrsBlock')}\n\n== ${t('Advanced lab tb sectionTitle')}\n` ) ) } const mdFence = (lang: string) => { run((v) => labInsertSnippet(v, sliceRef, `\`\`\`${lang}\n`, 'your code here', `\n\`\`\`\n`) ) } const adocSource = (lang: string) => { run((v) => labInsertSnippet( v, sliceRef, `[source,${lang}]\n----\n`, 'your code here', `\n----\n` ) ) } if (markupMode === 'markdown') { return (
{t('Advanced lab tb markup tools')} {t('Advanced lab tb headings hint')} {( [ 'Advanced lab tb h1', 'Advanced lab tb h2', 'Advanced lab tb h3', 'Advanced lab tb h4', 'Advanced lab tb h5', 'Advanced lab tb h6' ] as const ).map((labelKey, i) => { const n = i + 1 return ( run((v) => labInsertRaw( v, sliceRef, `${n === 1 ? '' : '\n'}${'#'.repeat(n)} ${t('Advanced lab tb heading placeholder')}\n` ) ) } > {t(labelKey)} ) })} run((v) => labInsertRaw( v, sliceRef, `\n${t('Advanced lab tb heading placeholder')}\n===\n` ) ) } > {t('Advanced lab tb setextH1')} run((v) => labInsertRaw( v, sliceRef, `\n${t('Advanced lab tb heading placeholder')}\n---\n` ) ) } > {t('Advanced lab tb setextH2')} run((v) => labWrapOrSnippet(v, sliceRef, '**', 'bold'))}> {t('Advanced lab tb bold')} run((v) => labWrapOrSnippet(v, sliceRef, '*', 'italic'))}> {t('Advanced lab tb italic')} run((v) => labWrapOrSnippet(v, sliceRef, '__', 'bold'))}> {t('Advanced lab tb boldUnderscore')} run((v) => labWrapOrSnippet(v, sliceRef, '_', 'italic'))}> {t('Advanced lab tb italicUnderscore')} run((v) => labWrapOrSnippet(v, sliceRef, '~~', 'strikethrough'))}> {t('Advanced lab tb strike')} run((v) => labWrapOrSnippet(v, sliceRef, '`', 'code'))}> {t('Advanced lab tb inlineCode')} run((v) => labInsertSnippet(v, sliceRef, '[', 'link text', '](https://example.com)') ) } > {t('Advanced lab tb link')} run((v) => labInsertSnippet(v, sliceRef, '![', 'alt text', '](https://example.com/image.png)') ) } > {t('Advanced lab tb image')} run((v) => labInsertSnippet( v, sliceRef, '[', 'link text', '](https://example.com "Link title")' ) ) } > {t('Advanced lab tb linkTitled')} run((v) => labInsertSnippet( v, sliceRef, '![', 'alt text', '](https://example.com/image.png "Image title")' ) ) } > {t('Advanced lab tb imageTitled')} run((v) => labInsertRaw(v, sliceRef, ' \n'))}> {t('Advanced lab tb hardBreak')} {citationDropdown} run((v) => labInsertRaw(v, sliceRef, '\n- item one\n- item two\n'))}> {t('Advanced lab tb bulletList')} run((v) => labInsertRaw(v, sliceRef, '\n* item one\n* item two\n'))}> {t('Advanced lab tb bulletListStar')} run((v) => labInsertRaw(v, sliceRef, '\n1. first\n2. second\n'))}> {t('Advanced lab tb orderedList')} run((v) => labInsertRaw( v, sliceRef, '\n4. item starting at four\n5. next item\n' ) ) } > {t('Advanced lab tb orderedListStart')} run((v) => labInsertRaw( v, sliceRef, '\n- [x] Checked box\n- [ ] Unchecked box\n' ) ) } > {t('Advanced lab tb taskItem')} run((v) => labInsertRaw(v, sliceRef, '\n> quoted line\n'))}> {t('Advanced lab tb blockquote')} run((v) => labInsertRaw( v, sliceRef, '\n| Col A | Col B |\n| --- | --- |\n| cell | cell |\n' ) ) } > {t('Advanced lab tb pipeTable')} run((v) => labInsertRaw(v, sliceRef, '\n[^1]\n'))}> {t('Advanced lab tb footnoteRef')} run((v) => labInsertRaw(v, sliceRef, '\n[^1]: Footnote text goes here.\n') ) } > {t('Advanced lab tb footnoteDef')} !o && setCodeFilter('')}>

{t('Advanced lab tb codeBlockHint')}

setCodeFilter(e.target.value)} placeholder={t('Advanced lab tb filterLanguages')} className="h-8 text-xs mb-2" />
{filteredLangs.map((lang) => ( ))}
{t('Advanced lab tb mathIntro')} run((v) => labInsertSnippet(v, sliceRef, '$', 'x^2 + y^2 = r^2', '$') ) } > {t('Advanced lab tb mathInline')} run((v) => labInsertSnippet(v, sliceRef, '\n$$\n', 'E = mc^2', '\n$$\n') ) } > {t('Advanced lab tb mathDisplay')} {t('Advanced lab tb mathCommon')} run((v) => labInsertRaw(v, sliceRef, '$\\frac{numerator}{denominator}$'))} > {t('Advanced lab tb katexFrac')} run((v) => labInsertRaw(v, sliceRef, '$\\sqrt{x}$'))}> {t('Advanced lab tb katexSqrt')} run((v) => labInsertRaw(v, sliceRef, '$\\sum_{i=1}^{n} i$'))} > {t('Advanced lab tb katexSum')} run((v) => labInsertRaw(v, sliceRef, '$\\int_{a}^{b} f(x)\\,dx$'))} > {t('Advanced lab tb katexInt')} run((v) => labInsertRaw( v, sliceRef, '$$\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}$$' ) ) } > {t('Advanced lab tb katexMatrix')} run((v) => labInsertRaw( v, sliceRef, '$$\\begin{cases} x & x > 0 \\\\ -x & x \\le 0 \\end{cases}$$' ) ) } > {t('Advanced lab tb katexCases')} {t('Advanced lab tb mathGreek')} run((v) => labInsertRaw(v, sliceRef, '$\\alpha$'))}> {'\\alpha'} {t('Advanced lab tb greekAlpha')} run((v) => labInsertRaw(v, sliceRef, '$\\beta$'))}> {'\\beta'} {t('Advanced lab tb greekBeta')} run((v) => labInsertRaw(v, sliceRef, '$\\gamma$'))}> {'\\gamma'} {t('Advanced lab tb greekGamma')} run((v) => labInsertRaw(v, sliceRef, '$\\delta$'))}> {'\\delta'} {t('Advanced lab tb greekDelta')} run((v) => labInsertRaw(v, sliceRef, '$\\pi$'))}> {'\\pi'} {t('Advanced lab tb greekPi')} run((v) => labInsertRaw(v, sliceRef, '$\\theta$'))}> {'\\theta'} {t('Advanced lab tb greekTheta')} run((v) => labInsertRaw(v, sliceRef, '$\\lambda$'))}> {'\\lambda'} {t('Advanced lab tb greekLambda')} run((v) => labInsertRaw(v, sliceRef, '$\\sigma$'))}> {'\\sigma'} {t('Advanced lab tb greekSigma')} run((v) => labInsertRaw(v, sliceRef, '$\\omega$'))}> {'\\omega'} {t('Advanced lab tb greekOmega')} run((v) => labInsertRaw(v, sliceRef, '$\\infty$'))}> {'\\infty'} {t('Advanced lab tb greekInfty')} {t('Advanced lab tb horizontalRules')} run((v) => labInsertRaw(v, sliceRef, '\n---\n'))}> {t('Advanced lab tb hrDashes')} run((v) => labInsertRaw(v, sliceRef, '\n***\n'))}> {t('Advanced lab tb hrAsterisks')} run((v) => labInsertRaw(v, sliceRef, '\n___\n'))}> {t('Advanced lab tb hrUnderscores')}
{citationPicker}
) } /* AsciiDoc */ return (
{t('Advanced lab tb markup tools')} {t('Advanced lab tb adocTitlesHint')} run((v) => labInsertRawWithOptionalBlockLeadNl(v, sliceRef, `= ${t('Advanced lab tb documentTitle')}\n`) ) } > {t('Advanced lab tb adocLevel0')} adocInsertFullHeader(`= ${t('Advanced lab tb documentTitle')}`)}> {t('Advanced lab tb adocLevel0WithHeader')} {( [ 'Advanced lab tb adocSection1', 'Advanced lab tb adocSection2', 'Advanced lab tb adocSection3', 'Advanced lab tb adocSection4', 'Advanced lab tb adocSection5' ] as const ).map((labelKey, i) => { const n = i + 1 return ( run((v) => labInsertRaw( v, sliceRef, `\n${'='.repeat(n + 1)} ${t('Advanced lab tb sectionTitle')}\n` ) ) } > {t(labelKey)} ) })} run((v) => labWrapOrSnippet(v, sliceRef, '*', 'bold'))}> {t('Advanced lab tb adocBold')} run((v) => labWrapOrSnippet(v, sliceRef, '_', 'italic'))}> {t('Advanced lab tb adocItalic')} run((v) => labWrapOrSnippet(v, sliceRef, '`', 'mono'))}> {t('Advanced lab tb adocMono')} run((v) => labInsertSnippet(v, sliceRef, 'link:https://example.com[', 'link text', ']') ) } > {t('Advanced lab tb adocLink')} run((v) => labInsertRaw( v, sliceRef, '\nimage::https://example.com/image.png[Alt text, width=640]\n' ) ) } > {t('Advanced lab tb adocImage')} run((v) => labInsertRaw(v, sliceRef, ' +\n'))}> {t('Advanced lab tb adocLineBreak')} run((v) => labWrapOrSnippet(v, sliceRef, '^', 'sup'))}> {t('Advanced lab tb adocSuperscript')} run((v) => labWrapOrSnippet(v, sliceRef, '~', 'sub'))}> {t('Advanced lab tb adocSubscript')} run((v) => labInsertSnippet(v, sliceRef, '+++', 'raw or HTML', '+++'))} > {t('Advanced lab tb adocPassthrough')} run((v) => labInsertRaw(v, sliceRef, 'footnote:[Footnote text]'))}> {t('Advanced lab tb adocFootnote')} {citationDropdown} run((v) => labInsertRaw(v, sliceRef, '\n* item one\n* item two\n'))}> {t('Advanced lab tb adocUnordered')} run((v) => labInsertRaw(v, sliceRef, '\n. first step\n. second step\n'))}> {t('Advanced lab tb adocOrdered')} run((v) => labInsertRaw(v, sliceRef, '\nterm:: definition line\n'))}> {t('Advanced lab tb adocLabeled')} run((v) => labInsertRaw( v, sliceRef, '\n[start=4]\n. fourth item\n. fifth item\n' ) ) } > {t('Advanced lab tb adocOrderedStart')} run((v) => labInsertSnippet(v, sliceRef, '\n____\n', 'Quoted paragraph', '\n____\n') ) } > {t('Advanced lab tb adocQuote')} run((v) => labInsertSnippet(v, sliceRef, '\n....\n', 'Literal monospace block', '\n....\n') ) } > {t('Advanced lab tb adocLiteral')} run((v) => labInsertSnippet( v, sliceRef, '\n[NOTE]\n====\n', 'Note body', '\n====\n' ) ) } > {t('Advanced lab tb adocNote')} run((v) => labInsertSnippet( v, sliceRef, '\n[TIP]\n====\n', 'Tip body', '\n====\n' ) ) } > {t('Advanced lab tb adocTip')} run((v) => labInsertSnippet( v, sliceRef, '\n[WARNING]\n====\n', 'Warning body', '\n====\n' ) ) } > {t('Advanced lab tb adocWarning')} run((v) => labInsertSnippet( v, sliceRef, '\n[IMPORTANT]\n====\n', 'Important body', '\n====\n' ) ) } > {t('Advanced lab tb adocImportant')} run((v) => labInsertSnippet( v, sliceRef, '\n[CAUTION]\n====\n', 'Caution body', '\n====\n' ) ) } > {t('Advanced lab tb adocCaution')} run((v) => labInsertSnippet( v, sliceRef, '\n[example]\n====\n', 'Example body', '\n====\n' ) ) } > {t('Advanced lab tb adocExampleBlock')} run((v) => labInsertSnippet(v, sliceRef, '\n****\n', 'Sidebar body', '\n****\n') ) } > {t('Advanced lab tb adocSidebar')} run((v) => labInsertSnippet( v, sliceRef, '\n[listing]\n----\n', 'Listing body (often line-oriented text)', '\n----\n' ) ) } > {t('Advanced lab tb adocListing')} run((v) => labInsertSnippet(v, sliceRef, '\n--\n', 'Open block body', '\n--\n') ) } > {t('Advanced lab tb adocOpenBlock')} {t('Advanced lab tb adocStructureHint')} run((v) => labInsertRaw( v, sliceRef, '\n|===\n|Column 1 |Column 2\n\n|Cell A |Cell B\n|===\n' ) ) } > {t('Advanced lab tb adocTable')} run((v) => labInsertRawWithOptionalBlockLeadNl(v, sliceRef, '[#section-anchor]\n')) } > {t('Advanced lab tb adocAnchor')} run((v) => labInsertSnippet(v, sliceRef, '<>') ) } > {t('Advanced lab tb adocXref')} run((v) => labInsertRaw(v, sliceRef, '\nvideo::https://example.com/video.mp4[width=640]\n') ) } > {t('Advanced lab tb adocVideo')} run((v) => labInsertRaw(v, sliceRef, '\naudio::https://example.com/audio.mp3[]\n') ) } > {t('Advanced lab tb adocAudio')} run((v) => labInsertRaw(v, sliceRef, '\n// Comment line\n'))}> {t('Advanced lab tb adocComment')} run((v) => labInsertRaw(v, sliceRef, 'kbd:[Ctrl+T]'))}> {t('Advanced lab tb adockbd')} run((v) => labInsertRaw(v, sliceRef, 'menu:View[Zoom > In]'))} > {t('Advanced lab tb adocMenu')} run((v) => labInsertRaw(v, sliceRef, 'btn:[OK]'))}> {t('Advanced lab tb adocBtn')} !o && setLangFilter('')}>

{t('Advanced lab tb adocSourceHint')}

setLangFilter(e.target.value)} placeholder={t('Advanced lab tb filterLanguages')} className="h-8 text-xs mb-2" />
{CODE_LANGUAGES.filter((id) => langFilter.trim() ? id.toLowerCase().includes(langFilter.trim().toLowerCase()) : true ).map((lang) => ( ))}
{t('Advanced lab tb adocStemHint')} run((v) => labInsertSnippet(v, sliceRef, 'stem:[', 'x^2 + y^2', ']'))} > {t('Advanced lab tb adocStemInline')} run((v) => labInsertSnippet(v, sliceRef, 'latexmath:[', 'x^2 + y^2', ']'))} > {t('Advanced lab tb adocLatexmathInline')} run((v) => labInsertSnippet(v, sliceRef, '\n[stem]\n++++\n', 'E = mc^2', '\n++++\n') ) } > {t('Advanced lab tb adocStemBlock')} run((v) => labInsertSnippet(v, sliceRef, 'stem:[', '\\frac{a}{b}', ']'))} > {t('Advanced lab tb katexFrac')} run((v) => labInsertSnippet(v, sliceRef, 'stem:[', '\\sqrt{x}', ']'))}> {t('Advanced lab tb katexSqrt')} run((v) => labInsertSnippet(v, sliceRef, 'stem:[', '\\sum_{i=1}^{n} i', ']'))} > {t('Advanced lab tb katexSum')} run((v) => labInsertSnippet(v, sliceRef, 'stem:[', '\\int_{a}^{b} f(x)\\,dx', ']'))} > {t('Advanced lab tb katexInt')} run((v) => labInsertRaw( v, sliceRef, '\n[stem]\n++++\n\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix}\n++++\n' ) ) } > {t('Advanced lab tb katexMatrix')} run((v) => labInsertRaw( v, sliceRef, '\n[stem]\n++++\n\\begin{cases} x & x > 0 \\\\ -x & x \\le 0 \\end{cases}\n++++\n' ) ) } > {t('Advanced lab tb katexCases')} {t('Advanced lab tb mathGreek')} run((v) => labInsertRaw(v, sliceRef, 'stem:[\\alpha]'))}> {'\\alpha'} {t('Advanced lab tb greekAlpha')} run((v) => labInsertRaw(v, sliceRef, 'stem:[\\beta]'))}> {'\\beta'} {t('Advanced lab tb greekBeta')} run((v) => labInsertRaw(v, sliceRef, 'stem:[\\pi]'))}> {'\\pi'} {t('Advanced lab tb greekPi')} run((v) => labInsertRaw(v, sliceRef, 'stem:[\\infty]'))}> {'\\infty'} {t('Advanced lab tb greekInfty')}
{citationPicker}
) }