import { BundledLanguage, bundledLanguagesInfo, bundledThemesInfo, codeToHtml, } from "shiki"; import { useEntity, useReplicache } from "src/replicache"; import "katex/dist/katex.min.css"; import { BlockLayout, BlockProps } from "./Block"; import { useCallback, useLayoutEffect, useMemo, useState } from "react"; import { useUIState } from "src/useUIState"; import { BaseTextareaBlock } from "./BaseTextareaBlock"; import { useEntitySetContext } from "components/EntitySetProvider"; import { flushSync } from "react-dom"; import { elementId } from "src/utils/elementId"; import { LAST_USED_CODE_LANGUAGE_KEY } from "src/utils/codeLanguageStorage"; export function CodeBlock(props: BlockProps) { let { rep, rootEntity } = useReplicache(); let content = useEntity(props.entityID, "block/code"); let lang = useEntity(props.entityID, "block/code-language")?.data.value || "plaintext"; let theme = useEntity(rootEntity, "theme/code-theme")?.data.value || "github-light"; let focusedBlock = useUIState( (s) => s.focusedEntity?.entityID === props.entityID, ); let entity_set = useEntitySetContext(); let { permissions } = entity_set; const [html, setHTML] = useState(null); useLayoutEffect(() => { if (!content) return; void codeToHtml(content.data.value, { lang, theme, structure: "classic", }).then((h) => { setHTML(h.replaceAll("
", "\n")); }); }, [content, lang, theme]); const onClick = useCallback((e: React.MouseEvent) => { let selection = window.getSelection(); if (!selection || selection.rangeCount === 0) return; let range = selection.getRangeAt(0); if (!range) return; let length = range.toString().length; range.setStart(e.currentTarget, 0); let end = range.toString().length; let start = end - length; flushSync(() => { useUIState.getState().setSelectedBlock(props); useUIState.getState().setFocusedBlock({ entityType: "block", entityID: props.value, parent: props.parent, }); }); let el = document.getElementById( elementId.block(props.entityID).input, ) as HTMLTextAreaElement; if (!el) return; el.focus(); el.setSelectionRange(start, end); }, []); return (
{permissions.write && (
Theme:{" "}
)} {focusedBlock && permissions.write ? ( { // Update the entity with the new value await rep?.mutate.assertFact({ attribute: "block/code", entity: props.entityID, data: { type: "string", value: e.target.value }, }); }} /> ) : !html ? (
 e.stopPropagation()}
            className="codeBlockRendered overflow-auto! font-mono p-2 sm:p-3 w-full h-full"
          >
            {content?.data.value === "" || content?.data.value === undefined ? (
              
write some code…
) : ( content?.data.value )}
) : (
e.stopPropagation()} onClick={onClick} data-lang={lang} className="contents" dangerouslySetInnerHTML={{ __html: html || "" }} /> )}
); }