Jelajahi Sumber

Shiki Code block Experimental

Elijah 8 bulan lalu
induk
melakukan
8725ae5619

+ 23 - 0
reflex/.templates/web/components/shiki/code.js

@@ -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>
+    )
+}

+ 1 - 0
reflex/components/datadisplay/code.py

@@ -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",

+ 381 - 0
reflex/components/datadisplay/shiki_code_block.py

@@ -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

+ 2 - 0
reflex/experimental/__init__.py

@@ -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,
 )