Shiki Code block Experimental

This commit is contained in:
Elijah 2024-09-30 15:21:46 +00:00
parent 9719f5d57e
commit 8725ae5619
4 changed files with 407 additions and 0 deletions

View File

@ -0,0 +1,23 @@
import { useEffect, useState } from "react"
import { codeToHtml} from "shiki"
export function Code ({code, theme, language, themes, transformers}) {
const [codeResult, setCodeResult] = useState("")
useEffect(() => {
async function fetchCode() {
const result = await codeToHtml(code, {
lang: language || "plaintext",
theme: theme || "nord",
transformers: transformers || []
});
setCodeResult(result);
}
fetchCode();
}, [code, language, theme, themes, transformers]
)
return (
<div dangerouslySetInnerHTML={{__html: codeResult}}></div>
)
}

View File

@ -18,6 +18,7 @@ from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars.base import LiteralVar, Var, VarData
LiteralCodeLanguage = Literal[
"ts",
"abap",
"abnf",
"actionscript",

View File

@ -0,0 +1,381 @@
from collections import defaultdict
from typing import Any, Literal, Optional, Union
from reflex.components.component import Component
from reflex.components.lucide.icon import Icon
from reflex.components.radix.themes.components.button import Button
from reflex.components.radix.themes.layout.box import Box
from reflex.event import set_clipboard
from reflex.style import Style
from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars.base import Var
from reflex.vars.function import FunctionStringVar
COMMON_TRANSFORMERS = {
"transformerNotationDiff",
"transformerNotationHighlight",
"transformerNotationWordHighlight",
"transformerNotationFocus",
"transformerNotationErrorLevel",
"transformerRenderWhitespace",
"transformerMetaHighlight",
"transformerMetaWordHighlight",
"transformerCompactLineOptions",
"transformerRemoveLineBreak",
"transformerRemoveNotationEscape",
}
LiteralCodeLanguage = Literal[
"ts",
"abap",
"abnf",
"actionscript",
"ada",
"agda",
"al",
"antlr4",
"apacheconf",
"apex",
"apl",
"applescript",
"aql",
"arduino",
"arff",
"asciidoc",
"asm6502",
"asmatmel",
"aspnet",
"autohotkey",
"autoit",
"avisynth",
"avro-idl",
"bash",
"basic",
"batch",
"bbcode",
"bicep",
"birb",
"bison",
"bnf",
"brainfuck",
"brightscript",
"bro",
"bsl",
"c",
"cfscript",
"chaiscript",
"cil",
"clike",
"clojure",
"cmake",
"cobol",
"coffeescript",
"concurnas",
"coq",
"core",
"cpp",
"crystal",
"csharp",
"cshtml",
"csp",
"css",
"css-extras",
"csv",
"cypher",
"d",
"dart",
"dataweave",
"dax",
"dhall",
"diff",
"django",
"dns-zone-file",
"docker",
"dot",
"ebnf",
"editorconfig",
"eiffel",
"ejs",
"elixir",
"elm",
"erb",
"erlang",
"etlua",
"excel-formula",
"factor",
"false",
"firestore-security-rules",
"flow",
"fortran",
"fsharp",
"ftl",
"gap",
"gcode",
"gdscript",
"gedcom",
"gherkin",
"git",
"glsl",
"gml",
"gn",
"go",
"go-module",
"graphql",
"groovy",
"haml",
"handlebars",
"haskell",
"haxe",
"hcl",
"hlsl",
"hoon",
"hpkp",
"hsts",
"http",
"ichigojam",
"icon",
"icu-message-format",
"idris",
"iecst",
"ignore",
"index",
"inform7",
"ini",
"io",
"j",
"java",
"javadoc",
"javadoclike",
"javascript",
"javastacktrace",
"jexl",
"jolie",
"jq",
"js-extras",
"js-templates",
"jsdoc",
"json",
"json5",
"jsonp",
"jsstacktrace",
"jsx",
"julia",
"keepalived",
"keyman",
"kotlin",
"kumir",
"kusto",
"latex",
"latte",
"less",
"lilypond",
"liquid",
"lisp",
"livescript",
"llvm",
"log",
"lolcode",
"lua",
"magma",
"makefile",
"markdown",
"markup",
"markup-templating",
"matlab",
"maxscript",
"mel",
"mermaid",
"mizar",
"mongodb",
"monkey",
"moonscript",
"n1ql",
"n4js",
"nand2tetris-hdl",
"naniscript",
"nasm",
"neon",
"nevod",
"nginx",
"nim",
"nix",
"nsis",
"objectivec",
"ocaml",
"opencl",
"openqasm",
"oz",
"parigp",
"parser",
"pascal",
"pascaligo",
"pcaxis",
"peoplecode",
"perl",
"php",
"php-extras",
"phpdoc",
"plsql",
"powerquery",
"powershell",
"processing",
"prolog",
"promql",
"properties",
"protobuf",
"psl",
"pug",
"puppet",
"pure",
"purebasic",
"purescript",
"python",
"q",
"qml",
"qore",
"qsharp",
"r",
"racket",
"reason",
"regex",
"rego",
"renpy",
"rest",
"rip",
"roboconf",
"robotframework",
"ruby",
"rust",
"sas",
"sass",
"scala",
"scheme",
"scss",
"shell-session",
"smali",
"smalltalk",
"smarty",
"sml",
"solidity",
"solution-file",
"soy",
"sparql",
"splunk-spl",
"sqf",
"sql",
"squirrel",
"stan",
"stylus",
"swift",
"systemd",
"t4-cs",
"t4-templating",
"t4-vb",
"tap",
"tcl",
"textile",
"toml",
"tremor",
"tsx",
"tt2",
"turtle",
"twig",
"typescript",
"typoscript",
"unrealscript",
"uorazor",
"uri",
"v",
"vala",
"vbnet",
"velocity",
"verilog",
"vhdl",
"vim",
"visual-basic",
"warpscript",
"wasm",
"web-idl",
"wiki",
"wolfram",
"wren",
"xeora",
"xml-doc",
"xojo",
"xquery",
"yaml",
"yang",
"zig",
]
class ShikiCodeBlock(Component):
library = "/utils/code"
tag = "Code"
alias = "ShikiCode"
language: Var[LiteralCodeLanguage] = "python"
theme: Var[str] = "min-dark"
themes: Var[list[dict[str, Any]] | dict[str, str]]
code: Var[str]
transformers: Var[list] = []
@classmethod
def create(
cls,
*children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[bool, Component]] = None,
**props,
) -> Component:
props["code"] = children[0]
if can_copy:
code = children[0]
copy_button = ( # type: ignore
copy_button
if copy_button is not None
else Button.create(
Icon.create(tag="copy"),
on_click=set_clipboard(code),
style=Style({"position": "absolute", "top": "0.5em", "right": "0"}),
)
)
else:
copy_button = None
transformers = props.pop("transformers", [])
trans_final = []
for transformer in transformers:
if transformer in COMMON_TRANSFORMERS:
trans_final.append(FunctionStringVar(f"{transformer}()"))
else:
trans_final.append(transformer)
if trans_final:
props["transformers"] = trans_final
code_block = super().create(**props)
if copy_button:
return Box.create(code_block, copy_button, position="relative")
else:
return code_block
def add_imports(self) -> ImportDict | list[ImportDict]:
imports = defaultdict(list)
for transformer in self.transformers._var_value:
if (
isinstance(transformer, FunctionStringVar)
and (transformer_import_str := str(transformer).strip("()"))
in COMMON_TRANSFORMERS
):
imports["@shikijs/transformers"].append(
ImportVar(tag=transformer_import_str)
)
self.lib_dependencies.append(
"@shikijs/transformers"
) if "@shikijs/transformers" not in self.lib_dependencies else None
return imports
code_block = ShikiCodeBlock.create

View File

@ -2,6 +2,7 @@
from types import SimpleNamespace
from reflex.components.datadisplay.shiki_code_block import code_block as code_block
from reflex.components.props import PropsBase
from reflex.components.radix.themes.components.progress import progress as progress
from reflex.components.sonner.toast import toast as toast
@ -67,4 +68,5 @@ _x = ExperimentalNamespace(
layout=layout,
PropsBase=PropsBase,
run_in_thread=run_in_thread,
code_block=code_block,
)