From 9025d72dad253b936a86018833dd54d01356eb24 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 30 Sep 2024 15:21:46 +0000 Subject: [PATCH 01/40] Shiki Code block Experimental --- .../.templates/web/components/shiki/code.js | 23 ++ reflex/components/datadisplay/code.py | 1 + .../datadisplay/shiki_code_block.py | 381 ++++++++++++++++++ reflex/experimental/__init__.py | 2 + 4 files changed, 407 insertions(+) create mode 100644 reflex/.templates/web/components/shiki/code.js create mode 100644 reflex/components/datadisplay/shiki_code_block.py diff --git a/reflex/.templates/web/components/shiki/code.js b/reflex/.templates/web/components/shiki/code.js new file mode 100644 index 00000000000..f4535cdd51c --- /dev/null +++ b/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 ( +
+ ) +} diff --git a/reflex/components/datadisplay/code.py b/reflex/components/datadisplay/code.py index 3b4fa39d1b2..bcf5d62f83e 100644 --- a/reflex/components/datadisplay/code.py +++ b/reflex/components/datadisplay/code.py @@ -18,6 +18,7 @@ from reflex.vars.base import LiteralVar, Var, VarData LiteralCodeLanguage = Literal[ + "ts", "abap", "abnf", "actionscript", diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py new file mode 100644 index 00000000000..d0bf80f926b --- /dev/null +++ b/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 diff --git a/reflex/experimental/__init__.py b/reflex/experimental/__init__.py index 0c11deb859d..164790fe570 100644 --- a/reflex/experimental/__init__.py +++ b/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 @@ def register_component_warning(component_name: str): layout=layout, PropsBase=PropsBase, run_in_thread=run_in_thread, + code_block=code_block, ) From 955c53add3905bb429ec50f3e40fb2487b842746 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 14:37:54 +0000 Subject: [PATCH 02/40] refactor --- .../datadisplay/shiki_code_block.py | 497 ++++++++++-------- 1 file changed, 292 insertions(+), 205 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index d0bf80f926b..6fd0c7e649d 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -1,17 +1,18 @@ from collections import defaultdict from typing import Any, Literal, Optional, Union -from reflex.components.component import Component +from reflex.base import Base +from reflex.components.component import Component, ComponentNamespace 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.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar -COMMON_TRANSFORMERS = { +SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", "transformerNotationHighlight", "transformerNotationWordHighlight", @@ -25,224 +26,162 @@ "transformerRemoveNotationEscape", } LiteralCodeLanguage = Literal[ - "ts", "abap", - "abnf", - "actionscript", + "actionscript-3", "ada", - "agda", - "al", - "antlr4", - "apacheconf", + "angular-html", + "angular-ts", + "apache", "apex", "apl", "applescript", - "aql", - "arduino", - "arff", + "ara", "asciidoc", - "asm6502", - "asmatmel", - "aspnet", - "autohotkey", - "autoit", - "avisynth", - "avro-idl", - "bash", - "basic", - "batch", - "bbcode", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", "bicep", - "birb", - "bison", - "bnf", - "brainfuck", - "brightscript", - "bro", - "bsl", + "blade", "c", - "cfscript", - "chaiscript", - "cil", - "clike", + "cadence", + "clarity", "clojure", "cmake", "cobol", - "coffeescript", - "concurnas", + "codeowners", + "codeql", + "coffee", + "common-lisp", "coq", - "core", "cpp", "crystal", "csharp", - "cshtml", - "csp", "css", - "css-extras", "csv", + "cue", "cypher", "d", "dart", - "dataweave", "dax", - "dhall", + "desktop", "diff", - "django", - "dns-zone-file", "docker", - "dot", - "ebnf", - "editorconfig", - "eiffel", - "ejs", + "dotenv", + "dream-maker", + "edge", "elixir", "elm", + "emacs-lisp", "erb", "erlang", - "etlua", - "excel-formula", - "factor", - "false", - "firestore-security-rules", - "flow", - "fortran", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", "fsharp", - "ftl", - "gap", - "gcode", + "gdresource", "gdscript", - "gedcom", + "gdshader", + "genie", "gherkin", - "git", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", "glsl", - "gml", - "gn", + "gnuplot", "go", - "go-module", "graphql", "groovy", + "hack", "haml", "handlebars", "haskell", "haxe", "hcl", + "hjson", "hlsl", - "hoon", - "hpkp", - "hsts", + "html", + "html-derivative", "http", - "ichigojam", - "icon", - "icu-message-format", - "idris", - "iecst", - "ignore", - "index", - "inform7", + "hxml", + "hy", + "imba", "ini", - "io", - "j", "java", - "javadoc", - "javadoclike", "javascript", - "javastacktrace", - "jexl", - "jolie", - "jq", - "js-extras", - "js-templates", - "jsdoc", + "jinja", + "jison", "json", "json5", - "jsonp", - "jsstacktrace", + "jsonc", + "jsonl", + "jsonnet", + "jssm", "jsx", "julia", - "keepalived", - "keyman", "kotlin", - "kumir", "kusto", "latex", - "latte", + "lean", "less", - "lilypond", "liquid", - "lisp", - "livescript", - "llvm", "log", - "lolcode", + "logo", "lua", - "magma", - "makefile", + "luau", + "make", "markdown", - "markup", - "markup-templating", + "marko", "matlab", - "maxscript", - "mel", + "mdc", + "mdx", "mermaid", - "mizar", - "mongodb", - "monkey", - "moonscript", - "n1ql", - "n4js", - "nand2tetris-hdl", - "naniscript", - "nasm", - "neon", - "nevod", + "mojo", + "move", + "narrat", + "nextflow", "nginx", "nim", "nix", - "nsis", - "objectivec", + "nushell", + "objective-c", + "objective-cpp", "ocaml", - "opencl", - "openqasm", - "oz", - "parigp", - "parser", "pascal", - "pascaligo", - "pcaxis", - "peoplecode", "perl", "php", - "php-extras", - "phpdoc", "plsql", + "po", + "postcss", "powerquery", "powershell", - "processing", + "prisma", "prolog", - "promql", - "properties", - "protobuf", - "psl", + "proto", "pug", "puppet", - "pure", - "purebasic", "purescript", "python", - "q", "qml", - "qore", - "qsharp", + "qmldir", + "qss", "r", "racket", - "reason", - "regex", - "rego", - "renpy", - "rest", - "rip", - "roboconf", - "robotframework", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", "ruby", "rust", "sas", @@ -250,73 +189,174 @@ "scala", "scheme", "scss", - "shell-session", - "smali", + "shaderlab", + "shellscript", + "shellsession", "smalltalk", - "smarty", - "sml", "solidity", - "solution-file", "soy", "sparql", - "splunk-spl", - "sqf", + "splunk", "sql", - "squirrel", - "stan", + "ssh-config", + "stata", "stylus", + "svelte", "swift", + "system-verilog", "systemd", - "t4-cs", - "t4-templating", - "t4-vb", - "tap", + "tasl", "tcl", - "textile", + "templ", + "terraform", + "tex", "toml", - "tremor", + "ts-tags", + "tsv", "tsx", - "tt2", "turtle", "twig", "typescript", - "typoscript", - "unrealscript", - "uorazor", - "uri", + "typespec", + "typst", "v", "vala", - "vbnet", - "velocity", + "vb", "verilog", "vhdl", - "vim", - "visual-basic", - "warpscript", + "viml", + "vue", + "vue-html", + "vyper", "wasm", - "web-idl", - "wiki", + "wenyan", + "wgsl", + "wikitext", "wolfram", - "wren", - "xeora", - "xml-doc", - "xojo", - "xquery", + "xml", + "xsl", "yaml", - "yang", + "zenscript", "zig", ] +LiteralCodeTheme = Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", +] + + +class ShikiBaseTransformers(Base): + library: str + fns: list[FunctionStringVar] + style: Style | None + + +class ShikiJsTransformer(ShikiBaseTransformers): + library: str = "@shikijs/transformers" + fns: list[FunctionStringVar] = [ + FunctionStringVar.create(x) for x in SHIKIJS_TRANSFORMER_FNS + ] + style: Style | None = Style( + { + ".line": {"display": "inline", "padding-bottom": "0"}, + ".diff": { + "display": "inline-block", + "width": "100vw", + "margin": "0 -12px", + "padding": "0 12px", + }, + ".diff.add": {"background-color": "#0505"}, + ".diff.remove": {"background-color": "#8005"}, + ".diff:before": {"position": "absolute", "left": "40px"}, + ".has-focused .line": {"filter": "blur(0.095rem)"}, + ".has-focused .focused": {"filter": "blur(0)"}, + "code": {"counter-reset": "step", "counter-increment": "step 0"}, + "code .line::before": { + "content": "counter(step)", + "counter-increment": "step", + "width": "1rem", + "margin-right": "1.5rem", + "display": "inline-block", + "text-align": "right", + "color": "rgba(115,138,148,.4)", + }, + } + ) + + def __init__(self, **kwargs): + fns = kwargs.pop("fns", None) + style = kwargs.pop("style", None) + if fns: + kwargs["fns"] = [ + FunctionStringVar.create(x) + if not isinstance(x, FunctionStringVar) + else x + for x in fns + ] + + if style: + kwargs["style"] = Style(style) + super().__init__(**kwargs) class ShikiCodeBlock(Component): library = "/utils/code" tag = "Code" alias = "ShikiCode" - language: Var[LiteralCodeLanguage] = "python" - theme: Var[str] = "min-dark" + language: Var[LiteralCodeLanguage] = Var.create("python") + theme: Var[LiteralCodeTheme] = Var.create("github-dark") themes: Var[list[dict[str, Any]] | dict[str, str]] code: Var[str] - transformers: Var[list] = [] + transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = [] @classmethod def create( @@ -342,40 +382,87 @@ def create( 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) + transformer_styles = {} + for transformer in code_block.transformers._var_value: + if isinstance(transformer, ShikiBaseTransformers) and transformer.style: + transformer_styles.update(transformer.style) if copy_button: - return Box.create(code_block, copy_button, position="relative") + return Box.create( + code_block, + copy_button, + position="relative", + style=Style(transformer_styles), + ) else: - return code_block + return Box.create(code_block, style=Style(transformer_styles)) 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) + if isinstance(transformer, ShikiBaseTransformers): + imports[transformer.library].extend( + [ImportVar(tag=str(fn)) for fn in transformer.fns] ) self.lib_dependencies.append( - "@shikijs/transformers" - ) if "@shikijs/transformers" not in self.lib_dependencies else None - + transformer.library + ) if transformer.library not in self.lib_dependencies else None return imports + @classmethod + def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers: + return ShikiBaseTransformers( + library=library, fns=[FunctionStringVar.create(fn) for fn in fns] + ) + + def _render(self, props: dict[str, Any] | None = None): + """Renders the component with the given properties, processing transformers if present. + + Args: + props: Optional properties to pass to the render function. + + Returns: + Rendered component output. + """ + # Ensure props is initialized from class attributes if not provided + props = props or { + attr.rstrip("_"): getattr(self, attr) for attr in self.get_props() + } + + # Extract transformers and apply transformations + transformers = props.get("transformers") + if transformers is not None: + transformed_values = self._process_transformers(transformers._var_value) + props["transformers"] = LiteralVar.create(transformed_values) + + return super()._render(props) + + def _process_transformers(self, transformer_list: list) -> list: + """Processes a list of transformers, applying transformations where necessary. + + Args: + transformer_list: List of transformer objects or values. + + Returns: + list: A list of transformed values. + """ + processed = [] + + for transformer in transformer_list: + if isinstance(transformer, ShikiBaseTransformers): + processed.extend(fn.call() for fn in transformer.fns) + else: + processed.append(transformer) + + return processed + + +class CodeblockNamespace(ComponentNamespace): + """Namespace for the CodeBlock component.""" + + create_transformer = ShikiCodeBlock.create_transformer + __call__ = ShikiCodeBlock.create + -code_block = ShikiCodeBlock.create +code_block = CodeblockNamespace() From d1e2d7c5f1d4eb61e3d14da47a0c578979448b41 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 14:42:57 +0000 Subject: [PATCH 03/40] update code --- reflex/.templates/web/components/shiki/code.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/reflex/.templates/web/components/shiki/code.js b/reflex/.templates/web/components/shiki/code.js index f4535cdd51c..322096563da 100644 --- a/reflex/.templates/web/components/shiki/code.js +++ b/reflex/.templates/web/components/shiki/code.js @@ -1,23 +1,23 @@ import { useEffect, useState } from "react" import { codeToHtml} from "shiki" -export function Code ({code, theme, language, themes, transformers}) { +export function Code ({code, theme, language, transformers, ...divProps}) { const [codeResult, setCodeResult] = useState("") useEffect(() => { async function fetchCode() { const result = await codeToHtml(code, { - lang: language || "plaintext", - theme: theme || "nord", - transformers: transformers || [] + lang: language, + theme, + transformers }); setCodeResult(result); } fetchCode(); - }, [code, language, theme, themes, transformers] + }, [code, language, theme, transformers] ) - + console.log(divProps) return ( -
+
) } From 48609783edd089cbb1fc8cdca85c6bc1c7ded0bf Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 14:43:23 +0000 Subject: [PATCH 04/40] remove console.log --- reflex/.templates/web/components/shiki/code.js | 1 - 1 file changed, 1 deletion(-) diff --git a/reflex/.templates/web/components/shiki/code.js b/reflex/.templates/web/components/shiki/code.js index 322096563da..0a2b5d3d97e 100644 --- a/reflex/.templates/web/components/shiki/code.js +++ b/reflex/.templates/web/components/shiki/code.js @@ -16,7 +16,6 @@ export function Code ({code, theme, language, transformers, ...divProps}) { }, [code, language, theme, transformers] ) - console.log(divProps) return (
) From 5ffa09fb8d111cf0993d14d75fa15bb0f04054c3 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 14:57:05 +0000 Subject: [PATCH 05/40] add transformers to namespace --- reflex/components/datadisplay/shiki_code_block.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 6fd0c7e649d..64414152585 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -458,10 +458,15 @@ def _process_transformers(self, transformer_list: list) -> list: return processed +class TransformerNamespace(ComponentNamespace): + shikijs = ShikiJsTransformer + + class CodeblockNamespace(ComponentNamespace): """Namespace for the CodeBlock component.""" create_transformer = ShikiCodeBlock.create_transformer + transformers = TransformerNamespace() __call__ = ShikiCodeBlock.create From 1443c67206523af9375af970f91d0ba4119b9b9f Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 15:33:43 +0000 Subject: [PATCH 06/40] some validations --- reflex/components/datadisplay/code.py | 1 - reflex/components/datadisplay/shiki_code_block.py | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/code.py b/reflex/components/datadisplay/code.py index bcf5d62f83e..3b4fa39d1b2 100644 --- a/reflex/components/datadisplay/code.py +++ b/reflex/components/datadisplay/code.py @@ -18,7 +18,6 @@ from reflex.vars.base import LiteralVar, Var, VarData LiteralCodeLanguage = Literal[ - "ts", "abap", "abnf", "actionscript", diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 64414152585..56f8a485cf7 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -4,6 +4,7 @@ from reflex.base import Base from reflex.components.component import Component, ComponentNamespace from reflex.components.lucide.icon import Icon +from reflex.components.core.cond import color_mode_cond from reflex.components.radix.themes.components.button import Button from reflex.components.radix.themes.layout.box import Box from reflex.event import set_clipboard @@ -368,6 +369,14 @@ def create( ) -> Component: props["code"] = children[0] + if "theme" not in props: + # Default color scheme responds to global color mode. + # TODO: we can use themes arg for this + props["theme"] = color_mode_cond( + light="one-light", + dark="one-dark-pro", + ) + if can_copy: code = children[0] copy_button = ( # type: ignore @@ -412,6 +421,8 @@ def add_imports(self) -> ImportDict | list[ImportDict]: @classmethod def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers: + if any(not isinstance(fn_name, str) for fn_name in fns): + raise ValueError(f"the function names should be str names of functions in the specified transformer: {library!r}") return ShikiBaseTransformers( library=library, fns=[FunctionStringVar.create(fn) for fn in fns] ) From e5d9a560d8ee4cd32fb560620d5f35174339aa7b Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 16:27:08 +0000 Subject: [PATCH 07/40] fix components paths --- reflex/components/datadisplay/shiki_code_block.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 56f8a485cf7..f71137d9b1f 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -350,11 +350,11 @@ def __init__(self, **kwargs): class ShikiCodeBlock(Component): - library = "/utils/code" + library = "/components/shiki/code" tag = "Code" alias = "ShikiCode" language: Var[LiteralCodeLanguage] = Var.create("python") - theme: Var[LiteralCodeTheme] = Var.create("github-dark") + theme: Var[LiteralCodeTheme] = Var.create("one-light") themes: Var[list[dict[str, Any]] | dict[str, str]] code: Var[str] transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = [] From 03158b62416424a23c68fbe2cc285e4013aa7d31 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 1 Oct 2024 16:59:49 +0000 Subject: [PATCH 08/40] fix ruff --- .../datadisplay/shiki_code_block.py | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index f71137d9b1f..6a27f862086 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -1,10 +1,12 @@ +"""Shiki syntax hghlighter component.""" + from collections import defaultdict from typing import Any, Literal, Optional, Union from reflex.base import Base from reflex.components.component import Component, ComponentNamespace -from reflex.components.lucide.icon import Icon from reflex.components.core.cond import color_mode_cond +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 @@ -296,12 +298,16 @@ class ShikiBaseTransformers(Base): + """Base for creating transformers.""" + library: str fns: list[FunctionStringVar] style: Style | None class ShikiJsTransformer(ShikiBaseTransformers): + """A Wrapped shikijs transformer.""" + library: str = "@shikijs/transformers" fns: list[FunctionStringVar] = [ FunctionStringVar.create(x) for x in SHIKIJS_TRANSFORMER_FNS @@ -334,6 +340,12 @@ class ShikiJsTransformer(ShikiBaseTransformers): ) def __init__(self, **kwargs): + """Initialize the transformer. + + Args: + kwargs: Kwargs to initialize the props. + + """ fns = kwargs.pop("fns", None) style = kwargs.pop("style", None) if fns: @@ -350,6 +362,8 @@ def __init__(self, **kwargs): class ShikiCodeBlock(Component): + """A Code block.""" + library = "/components/shiki/code" tag = "Code" alias = "ShikiCode" @@ -367,6 +381,17 @@ def create( copy_button: Optional[Union[bool, Component]] = None, **props, ) -> Component: + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + can_copy: Whether a copy button should appears. + copy_button: A custom copy button to override the default one. + **props: The props to pass to the component. + + Returns: + The code block component. + """ props["code"] = children[0] if "theme" not in props: @@ -408,6 +433,13 @@ def create( return Box.create(code_block, style=Style(transformer_styles)) def add_imports(self) -> ImportDict | list[ImportDict]: + """Add the necessary imports. + We add all referenced transformer functions as imports from their corresponding + libraries. + + Returns: + Imports for the component. + """ imports = defaultdict(list) for transformer in self.transformers._var_value: if isinstance(transformer, ShikiBaseTransformers): @@ -421,8 +453,22 @@ def add_imports(self) -> ImportDict | list[ImportDict]: @classmethod def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers: + """Create a transformer from a third party library. + + Args: + library: The name of the library. + fns: The str names of the functions/callables to invoke from the library. + + Returns: + A transformer for the specified library. + + Raises: + ValueError: If a supplied function name is not valid str. + """ if any(not isinstance(fn_name, str) for fn_name in fns): - raise ValueError(f"the function names should be str names of functions in the specified transformer: {library!r}") + raise ValueError( + f"the function names should be str names of functions in the specified transformer: {library!r}" + ) return ShikiBaseTransformers( library=library, fns=[FunctionStringVar.create(fn) for fn in fns] ) @@ -470,6 +516,8 @@ def _process_transformers(self, transformer_list: list) -> list: class TransformerNamespace(ComponentNamespace): + """Namespace for the Transformers.""" + shikijs = ShikiJsTransformer From a896a2f340960e19993a334f963269da991b542d Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 10:37:18 +0000 Subject: [PATCH 09/40] add a high-level component --- .../datadisplay/shiki_code_block.py | 174 +- .../datadisplay/shiki_code_block.pyi | 2239 +++++++++++++++++ 2 files changed, 2365 insertions(+), 48 deletions(-) create mode 100644 reflex/components/datadisplay/shiki_code_block.pyi diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 6a27f862086..9b80c308ad6 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -25,9 +25,24 @@ "transformerMetaHighlight", "transformerMetaWordHighlight", "transformerCompactLineOptions", - "transformerRemoveLineBreak", + # TODO: this transformer when included adds a weird behavior which removes other code lines. Need to figure out why. + # "transformerRemoveLineBreak", "transformerRemoveNotationEscape", } +LINE_NUMBER_STYLING = { + "code": {"counter-reset": "step", "counter-increment": "step 0"}, + "code .line::before": { + "content": "counter(step)", + "counter-increment": "step", + "width": "1rem", + "margin-right": "1.5rem", + "display": "inline-block", + "text-align": "right", + "color": "rgba(115,138,148,.4)", + }, +} + +THEME_MAPPING = {"light": "one-light", "dark": "one-dark"} LiteralCodeLanguage = Literal[ "abap", "actionscript-3", @@ -310,7 +325,7 @@ class ShikiJsTransformer(ShikiBaseTransformers): library: str = "@shikijs/transformers" fns: list[FunctionStringVar] = [ - FunctionStringVar.create(x) for x in SHIKIJS_TRANSFORMER_FNS + FunctionStringVar.create(fn) for fn in SHIKIJS_TRANSFORMER_FNS ] style: Style | None = Style( { @@ -326,16 +341,6 @@ class ShikiJsTransformer(ShikiBaseTransformers): ".diff:before": {"position": "absolute", "left": "40px"}, ".has-focused .line": {"filter": "blur(0.095rem)"}, ".has-focused .focused": {"filter": "blur(0)"}, - "code": {"counter-reset": "step", "counter-increment": "step 0"}, - "code .line::before": { - "content": "counter(step)", - "counter-increment": "step", - "width": "1rem", - "margin-right": "1.5rem", - "display": "inline-block", - "text-align": "right", - "color": "rgba(115,138,148,.4)", - }, } ) @@ -367,70 +372,65 @@ class ShikiCodeBlock(Component): library = "/components/shiki/code" tag = "Code" alias = "ShikiCode" + + # The language to use. language: Var[LiteralCodeLanguage] = Var.create("python") + + # The theme to use ("light" or "dark"). theme: Var[LiteralCodeTheme] = Var.create("one-light") + + # The set of themes to use for different modes. themes: Var[list[dict[str, Any]] | dict[str, str]] + + # The code to display. code: Var[str] + + # The transformers to use for the syntax highlighter. transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = [] @classmethod def create( cls, *children, - can_copy: Optional[bool] = False, - copy_button: Optional[Union[bool, Component]] = None, **props, ) -> Component: """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). Args: *children: The children of the component. - can_copy: Whether a copy button should appears. - copy_button: A custom copy button to override the default one. **props: The props to pass to the component. Returns: The code block component. """ - props["code"] = children[0] + # Separate props for the code block and the wrapper + code_block_props = {} + code_wrapper_props = {} - if "theme" not in props: - # Default color scheme responds to global color mode. - # TODO: we can use themes arg for this - props["theme"] = color_mode_cond( - light="one-light", - dark="one-dark-pro", - ) + class_props = cls.get_props() - 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"}), - ) + # Distribute props between the code block and wrapper + for key, value in props.items(): + (code_block_props if key in class_props else code_wrapper_props)[key] = ( + value ) - else: - copy_button = None - code_block = super().create(**props) + code_block_props["code"] = children[0] + code_block = super().create(**code_block_props) + transformer_styles = {} + # Collect styles from transformers and wrapper for transformer in code_block.transformers._var_value: if isinstance(transformer, ShikiBaseTransformers) and transformer.style: transformer_styles.update(transformer.style) + transformer_styles.update(code_wrapper_props.pop("style", {})) - if copy_button: - return Box.create( - code_block, - copy_button, - position="relative", - style=Style(transformer_styles), - ) - else: - return Box.create(code_block, style=Style(transformer_styles)) + return Box.create( + code_block, + *children[1:], + style=Style(transformer_styles), + **code_wrapper_props, + ) def add_imports(self) -> ImportDict | list[ImportDict]: """Add the necessary imports. @@ -515,6 +515,83 @@ def _process_transformers(self, transformer_list: list) -> list: return processed +class ShikiHighLevelCodeBlock(ShikiCodeBlock): + """High level component for the shiki syntax highlighter.""" + + # If this is enabled, the default transformer(shikijs transformer) will be used. + use_transformer: Var[bool] = False + + # If this is enabled line numbers will be shown next to the code block. + show_line_numbers: Var[bool] + + @classmethod + def create( + cls, + *children, + can_copy: Optional[bool] = False, + copy_button: Optional[Union[bool, Component]] = None, + **props, + ) -> Component: + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + can_copy: Whether a copy button should appear. + copy_button: A custom copy button to override the default one. + **props: The props to pass to the component. + + Returns: + The code block component. + """ + use_transformer = props.pop("use_transformer", False) + show_line_numbers = props.pop("show_line_numbers", False) + + if use_transformer: + props["transformers"] = [ShikiJsTransformer()] + + if show_line_numbers: + line_style = LINE_NUMBER_STYLING.copy() + line_style.update(props.get("style", {})) + props["style"] = line_style + + theme = props.pop("theme", None) + props["theme"] = props["theme"] = ( + cls._map_themes(theme) + if theme + else color_mode_cond( # Default color scheme responds to global color mode. + light="one-light", + dark="one-dark-pro", + ) + ) + + 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 + + if copy_button: + return ShikiCodeBlock.create( + children[0], copy_button, position="relative", **props + ) + else: + return ShikiCodeBlock.create(children[0], **props) + + @staticmethod + def _map_themes(theme: str) -> str: + if isinstance(theme, str) and theme in THEME_MAPPING: + return THEME_MAPPING[theme] + return theme + + class TransformerNamespace(ComponentNamespace): """Namespace for the Transformers.""" @@ -524,9 +601,10 @@ class TransformerNamespace(ComponentNamespace): class CodeblockNamespace(ComponentNamespace): """Namespace for the CodeBlock component.""" - create_transformer = ShikiCodeBlock.create_transformer + root = staticmethod(ShikiCodeBlock.create) + create_transformer = staticmethod(ShikiCodeBlock.create_transformer) transformers = TransformerNamespace() - __call__ = ShikiCodeBlock.create + __call__ = staticmethod(ShikiHighLevelCodeBlock.create) code_block = CodeblockNamespace() diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi new file mode 100644 index 00000000000..bd494c734d6 --- /dev/null +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -0,0 +1,2239 @@ +"""Stub file for reflex/components/datadisplay/shiki_code_block.py""" + +# ------------------- DO NOT EDIT ---------------------- +# This file was generated by `reflex/utils/pyi_generator.py`! +# ------------------------------------------------------ +from typing import Any, Callable, Dict, Literal, Optional, Union, overload + +from reflex.base import Base +from reflex.components.component import Component, ComponentNamespace +from reflex.event import EventHandler, EventSpec +from reflex.style import Style +from reflex.utils.imports import ImportDict +from reflex.vars.base import Var +from reflex.vars.function import FunctionStringVar + +SHIKIJS_TRANSFORMER_FNS = { + "transformerNotationDiff", + "transformerNotationHighlight", + "transformerNotationWordHighlight", + "transformerNotationFocus", + "transformerNotationErrorLevel", + "transformerRenderWhitespace", + "transformerMetaHighlight", + "transformerMetaWordHighlight", + "transformerCompactLineOptions", + "transformerRemoveNotationEscape", +} +LINE_NUMBER_STYLING = { + "code": {"counter-reset": "step", "counter-increment": "step 0"}, + "code .line::before": { + "content": "counter(step)", + "counter-increment": "step", + "width": "1rem", + "margin-right": "1.5rem", + "display": "inline-block", + "text-align": "right", + "color": "rgba(115,138,148,.4)", + }, +} +THEME_MAPPING = {"light": "one-light", "dark": "one-dark"} +LiteralCodeLanguage = Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", +] +LiteralCodeTheme = Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", +] + +class ShikiBaseTransformers(Base): + library: str + fns: list[FunctionStringVar] + style: Style | None + +class ShikiJsTransformer(ShikiBaseTransformers): + library: str + fns: list[FunctionStringVar] + style: Style | None + +class ShikiCodeBlock(Component): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + language: Optional[ + Union[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ], + Var[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ] + ], + ] + ] = None, + theme: Optional[ + Union[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ], + Var[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ] + ], + ] + ] = None, + themes: Optional[ + Union[ + Var[Union[dict[str, str], list[dict[str, Any]]]], + dict[str, str], + list[dict[str, Any]], + ] + ] = None, + code: Optional[Union[Var[str], str]] = None, + transformers: Optional[ + Union[ + Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]], + list[Union[ShikiBaseTransformers, dict[str, Any]]], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + **props, + ) -> "ShikiCodeBlock": + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + language: The language to use. + theme: The theme to use ("light" or "dark"). + themes: The set of themes to use for different modes. + code: The code to display. + transformers: The transformers to use for the syntax highlighter. + style: The style of the component. + key: A unique key for the component. + id: The id for the component. + class_name: The class name for the component. + autofocus: Whether the component should take the focus once the page is loaded + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The code block component. + """ + ... + + def add_imports(self) -> ImportDict | list[ImportDict]: ... + @classmethod + def create_transformer( + cls, library: str, fns: list[str] + ) -> ShikiBaseTransformers: ... + +class ShikiHighLevelCodeBlock(ShikiCodeBlock): + @overload + @classmethod + def create( # type: ignore + cls, + *children, + can_copy: Optional[bool] = False, + copy_button: Optional[Union[Component, bool]] = None, + use_transformer: Optional[Union[Var[bool], bool]] = None, + show_line_numbers: Optional[Union[Var[bool], bool]] = None, + language: Optional[ + Union[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ], + Var[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ] + ], + ] + ] = None, + theme: Optional[ + Union[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ], + Var[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ] + ], + ] + ] = None, + themes: Optional[ + Union[ + Var[Union[dict[str, str], list[dict[str, Any]]]], + dict[str, str], + list[dict[str, Any]], + ] + ] = None, + code: Optional[Union[Var[str], str]] = None, + transformers: Optional[ + Union[ + Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]], + list[Union[ShikiBaseTransformers, dict[str, Any]]], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + **props, + ) -> "ShikiHighLevelCodeBlock": + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + can_copy: Whether a copy button should appear. + copy_button: A custom copy button to override the default one. + use_transformer: If this is enabled, the default transformer(shikijs transformer) will be used. + show_line_numbers: If this is enabled line numbers will be shown next to the code block. + language: The language to use. + theme: The theme to use ("light" or "dark"). + themes: The set of themes to use for different modes. + code: The code to display. + transformers: The transformers to use for the syntax highlighter. + style: The style of the component. + key: A unique key for the component. + id: The id for the component. + class_name: The class name for the component. + autofocus: Whether the component should take the focus once the page is loaded + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The code block component. + """ + ... + +class TransformerNamespace(ComponentNamespace): + shikijs = ShikiJsTransformer + +class CodeblockNamespace(ComponentNamespace): + root = staticmethod(ShikiCodeBlock.create) + create_transformer = staticmethod(ShikiCodeBlock.create_transformer) + transformers = TransformerNamespace() + + @staticmethod + def __call__( + *children, + can_copy: Optional[bool] = False, + copy_button: Optional[Union[Component, bool]] = None, + use_transformer: Optional[Union[Var[bool], bool]] = None, + show_line_numbers: Optional[Union[Var[bool], bool]] = None, + language: Optional[ + Union[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ], + Var[ + Literal[ + "abap", + "actionscript-3", + "ada", + "angular-html", + "angular-ts", + "apache", + "apex", + "apl", + "applescript", + "ara", + "asciidoc", + "asm", + "astro", + "awk", + "ballerina", + "bat", + "beancount", + "berry", + "bibtex", + "bicep", + "blade", + "c", + "cadence", + "clarity", + "clojure", + "cmake", + "cobol", + "codeowners", + "codeql", + "coffee", + "common-lisp", + "coq", + "cpp", + "crystal", + "csharp", + "css", + "csv", + "cue", + "cypher", + "d", + "dart", + "dax", + "desktop", + "diff", + "docker", + "dotenv", + "dream-maker", + "edge", + "elixir", + "elm", + "emacs-lisp", + "erb", + "erlang", + "fennel", + "fish", + "fluent", + "fortran-fixed-form", + "fortran-free-form", + "fsharp", + "gdresource", + "gdscript", + "gdshader", + "genie", + "gherkin", + "git-commit", + "git-rebase", + "gleam", + "glimmer-js", + "glimmer-ts", + "glsl", + "gnuplot", + "go", + "graphql", + "groovy", + "hack", + "haml", + "handlebars", + "haskell", + "haxe", + "hcl", + "hjson", + "hlsl", + "html", + "html-derivative", + "http", + "hxml", + "hy", + "imba", + "ini", + "java", + "javascript", + "jinja", + "jison", + "json", + "json5", + "jsonc", + "jsonl", + "jsonnet", + "jssm", + "jsx", + "julia", + "kotlin", + "kusto", + "latex", + "lean", + "less", + "liquid", + "log", + "logo", + "lua", + "luau", + "make", + "markdown", + "marko", + "matlab", + "mdc", + "mdx", + "mermaid", + "mojo", + "move", + "narrat", + "nextflow", + "nginx", + "nim", + "nix", + "nushell", + "objective-c", + "objective-cpp", + "ocaml", + "pascal", + "perl", + "php", + "plsql", + "po", + "postcss", + "powerquery", + "powershell", + "prisma", + "prolog", + "proto", + "pug", + "puppet", + "purescript", + "python", + "qml", + "qmldir", + "qss", + "r", + "racket", + "raku", + "razor", + "reg", + "regexp", + "rel", + "riscv", + "rst", + "ruby", + "rust", + "sas", + "sass", + "scala", + "scheme", + "scss", + "shaderlab", + "shellscript", + "shellsession", + "smalltalk", + "solidity", + "soy", + "sparql", + "splunk", + "sql", + "ssh-config", + "stata", + "stylus", + "svelte", + "swift", + "system-verilog", + "systemd", + "tasl", + "tcl", + "templ", + "terraform", + "tex", + "toml", + "ts-tags", + "tsv", + "tsx", + "turtle", + "twig", + "typescript", + "typespec", + "typst", + "v", + "vala", + "vb", + "verilog", + "vhdl", + "viml", + "vue", + "vue-html", + "vyper", + "wasm", + "wenyan", + "wgsl", + "wikitext", + "wolfram", + "xml", + "xsl", + "yaml", + "zenscript", + "zig", + ] + ], + ] + ] = None, + theme: Optional[ + Union[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ], + Var[ + Literal[ + "andromeeda", + "aurora-x", + "ayu-dark", + "catppuccin-frappe", + "catppuccin-latte", + "catppuccin-macchiato", + "catppuccin-mocha", + "dark-plus", + "dracula", + "dracula-soft", + "everforest-dark", + "everforest-light", + "github-dark", + "github-dark-default", + "github-dark-dimmed", + "github-dark-high-contrast", + "github-light", + "github-light-default", + "github-light-high-contrast", + "houston", + "laserwave", + "light-plus", + "material-theme", + "material-theme-darker", + "material-theme-lighter", + "material-theme-ocean", + "material-theme-palenight", + "min-dark", + "min-light", + "monokai", + "night-owl", + "nord", + "one-dark-pro", + "one-light", + "plastic", + "poimandres", + "red", + "rose-pine", + "rose-pine-dawn", + "rose-pine-moon", + "slack-dark", + "slack-ochin", + "snazzy-light", + "solarized-dark", + "solarized-light", + "synthwave-84", + "tokyo-night", + "vesper", + "vitesse-black", + "vitesse-dark", + "vitesse-light", + ] + ], + ] + ] = None, + themes: Optional[ + Union[ + Var[Union[dict[str, str], list[dict[str, Any]]]], + dict[str, str], + list[dict[str, Any]], + ] + ] = None, + code: Optional[Union[Var[str], str]] = None, + transformers: Optional[ + Union[ + Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]], + list[Union[ShikiBaseTransformers, dict[str, Any]]], + ] + ] = None, + style: Optional[Style] = None, + key: Optional[Any] = None, + id: Optional[Any] = None, + class_name: Optional[Any] = None, + autofocus: Optional[bool] = None, + custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, + on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_context_menu: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_double_click: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_mouse_down: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_enter: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_leave: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_move: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_out: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_over: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_mouse_up: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, + on_unmount: Optional[ + Union[EventHandler, EventSpec, list, Callable, Var] + ] = None, + **props, + ) -> "ShikiHighLevelCodeBlock": + """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). + + Args: + *children: The children of the component. + can_copy: Whether a copy button should appear. + copy_button: A custom copy button to override the default one. + use_transformer: If this is enabled, the default transformer(shikijs transformer) will be used. + show_line_numbers: If this is enabled line numbers will be shown next to the code block. + language: The language to use. + theme: The theme to use ("light" or "dark"). + themes: The set of themes to use for different modes. + code: The code to display. + transformers: The transformers to use for the syntax highlighter. + style: The style of the component. + key: A unique key for the component. + id: The id for the component. + class_name: The class name for the component. + autofocus: Whether the component should take the focus once the page is loaded + custom_attrs: custom attribute + **props: The props to pass to the component. + + Returns: + The code block component. + """ + ... + +code_block = CodeblockNamespace() From fd16a49aa29d57224b054a14df949c2265d6a4f9 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 10:38:40 +0000 Subject: [PATCH 10/40] fix mapping --- reflex/components/datadisplay/shiki_code_block.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 9b80c308ad6..ea4c03f1f20 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -42,7 +42,7 @@ }, } -THEME_MAPPING = {"light": "one-light", "dark": "one-dark"} +THEME_MAPPING = {"light": "one-light", "dark": "one-dark-pro"} LiteralCodeLanguage = Literal[ "abap", "actionscript-3", From b0f6e9f626e05f95387ce05f07ba4f4324db5b49 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 10:38:58 +0000 Subject: [PATCH 11/40] fix mapping --- reflex/components/datadisplay/shiki_code_block.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index bd494c734d6..98c6b6749ab 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -37,7 +37,7 @@ LINE_NUMBER_STYLING = { "color": "rgba(115,138,148,.4)", }, } -THEME_MAPPING = {"light": "one-light", "dark": "one-dark"} +THEME_MAPPING = {"light": "one-light", "dark": "one-dark-pro"} LiteralCodeLanguage = Literal[ "abap", "actionscript-3", From 46c80966459155b0946823ed5350ff9fdcbf7621 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 10:57:27 +0000 Subject: [PATCH 12/40] python 3.9+ --- reflex/components/datadisplay/shiki_code_block.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index ea4c03f1f20..8d681d1117e 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -1,7 +1,9 @@ """Shiki syntax hghlighter component.""" +from __future__ import annotations + from collections import defaultdict -from typing import Any, Literal, Optional, Union +from typing import Any, Literal from reflex.base import Base from reflex.components.component import Component, ComponentNamespace @@ -528,8 +530,8 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): def create( cls, *children, - can_copy: Optional[bool] = False, - copy_button: Optional[Union[bool, Component]] = None, + can_copy: bool | None = False, + copy_button: bool | Component | None = None, **props, ) -> Component: """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). From 801ed8419f22eb5427610a7662435e01d7cd732a Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 11:29:31 +0000 Subject: [PATCH 13/40] see if this fixes the tests --- .../components/datadisplay/shiki_code_block.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 8d681d1117e..cf04e6f9add 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Any, Literal +from typing import Any, Literal, Optional from reflex.base import Base from reflex.components.component import Component, ComponentNamespace @@ -13,7 +13,7 @@ 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.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar @@ -319,7 +319,7 @@ class ShikiBaseTransformers(Base): library: str fns: list[FunctionStringVar] - style: Style | None + style: Optional[Style] class ShikiJsTransformer(ShikiBaseTransformers): @@ -329,7 +329,7 @@ class ShikiJsTransformer(ShikiBaseTransformers): fns: list[FunctionStringVar] = [ FunctionStringVar.create(fn) for fn in SHIKIJS_TRANSFORMER_FNS ] - style: Style | None = Style( + style: Optional[Style] = Style( { ".line": {"display": "inline", "padding-bottom": "0"}, ".diff": { @@ -388,7 +388,7 @@ class ShikiCodeBlock(Component): code: Var[str] # The transformers to use for the syntax highlighter. - transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = [] + transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = Var.create([]) @classmethod def create( @@ -422,7 +422,7 @@ def create( transformer_styles = {} # Collect styles from transformers and wrapper - for transformer in code_block.transformers._var_value: + for transformer in code_block.transformers._var_value: # type: ignore if isinstance(transformer, ShikiBaseTransformers) and transformer.style: transformer_styles.update(transformer.style) transformer_styles.update(code_wrapper_props.pop("style", {})) @@ -434,7 +434,7 @@ def create( **code_wrapper_props, ) - def add_imports(self) -> ImportDict | list[ImportDict]: + def add_imports(self) -> dict[str, list[str]]: """Add the necessary imports. We add all referenced transformer functions as imports from their corresponding libraries. @@ -471,7 +471,7 @@ def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransforme raise ValueError( f"the function names should be str names of functions in the specified transformer: {library!r}" ) - return ShikiBaseTransformers( + return ShikiBaseTransformers( # type: ignore library=library, fns=[FunctionStringVar.create(fn) for fn in fns] ) @@ -521,7 +521,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): """High level component for the shiki syntax highlighter.""" # If this is enabled, the default transformer(shikijs transformer) will be used. - use_transformer: Var[bool] = False + use_transformer: Var[bool] # If this is enabled line numbers will be shown next to the code block. show_line_numbers: Var[bool] From 3c892f842d53afdea296aa11c734549c8abb0924 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 11:42:08 +0000 Subject: [PATCH 14/40] fix pyi and annotations --- reflex/components/datadisplay/shiki_code_block.py | 12 ++++++------ reflex/components/datadisplay/shiki_code_block.pyi | 7 +++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index cf04e6f9add..4d47f4c19be 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -3,7 +3,7 @@ from __future__ import annotations from collections import defaultdict -from typing import Any, Literal, Optional +from typing import Any, Literal, Optional, Union from reflex.base import Base from reflex.components.component import Component, ComponentNamespace @@ -382,13 +382,15 @@ class ShikiCodeBlock(Component): theme: Var[LiteralCodeTheme] = Var.create("one-light") # The set of themes to use for different modes. - themes: Var[list[dict[str, Any]] | dict[str, str]] + themes: Var[Union[list[dict[str, Any]], dict[str, str]]] # The code to display. code: Var[str] # The transformers to use for the syntax highlighter. - transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = Var.create([]) + transformers: Var[list[Union[ShikiBaseTransformers, dict[str, Any]]]] = Var.create( + [] + ) @classmethod def create( @@ -552,9 +554,7 @@ def create( props["transformers"] = [ShikiJsTransformer()] if show_line_numbers: - line_style = LINE_NUMBER_STYLING.copy() - line_style.update(props.get("style", {})) - props["style"] = line_style + props["style"] = {**LINE_NUMBER_STYLING, **props.get("style", {})} theme = props.pop("theme", None) props["theme"] = props["theme"] = ( diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index 98c6b6749ab..e642cddac7d 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -9,7 +9,6 @@ from reflex.base import Base from reflex.components.component import Component, ComponentNamespace from reflex.event import EventHandler, EventSpec from reflex.style import Style -from reflex.utils.imports import ImportDict from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar @@ -309,12 +308,12 @@ LiteralCodeTheme = Literal[ class ShikiBaseTransformers(Base): library: str fns: list[FunctionStringVar] - style: Style | None + style: Optional[Style] class ShikiJsTransformer(ShikiBaseTransformers): library: str fns: list[FunctionStringVar] - style: Style | None + style: Optional[Style] class ShikiCodeBlock(Component): @overload @@ -947,7 +946,7 @@ class ShikiCodeBlock(Component): """ ... - def add_imports(self) -> ImportDict | list[ImportDict]: ... + def add_imports(self) -> dict[str, list[str]]: ... @classmethod def create_transformer( cls, library: str, fns: list[str] From f6495a64487f1b2c22c36a86f598fda1593f7fca Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 14:38:57 +0000 Subject: [PATCH 15/40] minimal update of theme and language map --- .../datadisplay/shiki_code_block.py | 30 +++++++++++++++---- .../datadisplay/shiki_code_block.pyi | 15 ++++++---- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 4d47f4c19be..4ba4db2052d 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -44,7 +44,12 @@ }, } -THEME_MAPPING = {"light": "one-light", "dark": "one-dark-pro"} +THEME_MAPPING = { + "light": "one-light", + "dark": "one-dark-pro", + "a11y-dark": "github-dark", +} +LANGUAGE_MAPPING = {"bash": "shellscript"} LiteralCodeLanguage = Literal[ "abap", "actionscript-3", @@ -372,9 +377,13 @@ class ShikiCodeBlock(Component): """A Code block.""" library = "/components/shiki/code" + tag = "Code" + alias = "ShikiCode" + lib_dependencies: list[str] = ["shiki"] + # The language to use. language: Var[LiteralCodeLanguage] = Var.create("python") @@ -522,8 +531,8 @@ def _process_transformers(self, transformer_list: list) -> list: class ShikiHighLevelCodeBlock(ShikiCodeBlock): """High level component for the shiki syntax highlighter.""" - # If this is enabled, the default transformer(shikijs transformer) will be used. - use_transformer: Var[bool] + # If this is enabled, the default transformers(shikijs transformer) will be used. + use_transformers: Var[bool] # If this is enabled line numbers will be shown next to the code block. show_line_numbers: Var[bool] @@ -547,12 +556,17 @@ def create( Returns: The code block component. """ - use_transformer = props.pop("use_transformer", False) + use_transformers = props.pop("use_transformers", False) show_line_numbers = props.pop("show_line_numbers", False) + language = props.pop("language", None) - if use_transformer: + if use_transformers: props["transformers"] = [ShikiJsTransformer()] + if language is not None: + props["language"] = cls._map_languages(language) + + # line numbers are generated via css if show_line_numbers: props["style"] = {**LINE_NUMBER_STYLING, **props.get("style", {})} @@ -593,6 +607,12 @@ def _map_themes(theme: str) -> str: return THEME_MAPPING[theme] return theme + @staticmethod + def _map_languages(language: str) -> str: + if isinstance(language, str) and language in LANGUAGE_MAPPING: + return LANGUAGE_MAPPING[language] + return language + class TransformerNamespace(ComponentNamespace): """Namespace for the Transformers.""" diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index e642cddac7d..3353e004b8b 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -36,7 +36,12 @@ LINE_NUMBER_STYLING = { "color": "rgba(115,138,148,.4)", }, } -THEME_MAPPING = {"light": "one-light", "dark": "one-dark-pro"} +THEME_MAPPING = { + "light": "one-light", + "dark": "one-dark-pro", + "a11y-dark": "github-dark", +} +LANGUAGE_MAPPING = {"bash": "shellscript"} LiteralCodeLanguage = Literal[ "abap", "actionscript-3", @@ -960,7 +965,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): *children, can_copy: Optional[bool] = False, copy_button: Optional[Union[Component, bool]] = None, - use_transformer: Optional[Union[Var[bool], bool]] = None, + use_transformers: Optional[Union[Var[bool], bool]] = None, show_line_numbers: Optional[Union[Var[bool], bool]] = None, language: Optional[ Union[ @@ -1571,7 +1576,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): *children: The children of the component. can_copy: Whether a copy button should appear. copy_button: A custom copy button to override the default one. - use_transformer: If this is enabled, the default transformer(shikijs transformer) will be used. + use_transformers: If this is enabled, the default transformers(shikijs transformer) will be used. show_line_numbers: If this is enabled line numbers will be shown next to the code block. language: The language to use. theme: The theme to use ("light" or "dark"). @@ -1604,7 +1609,7 @@ class CodeblockNamespace(ComponentNamespace): *children, can_copy: Optional[bool] = False, copy_button: Optional[Union[Component, bool]] = None, - use_transformer: Optional[Union[Var[bool], bool]] = None, + use_transformers: Optional[Union[Var[bool], bool]] = None, show_line_numbers: Optional[Union[Var[bool], bool]] = None, language: Optional[ Union[ @@ -2215,7 +2220,7 @@ class CodeblockNamespace(ComponentNamespace): *children: The children of the component. can_copy: Whether a copy button should appear. copy_button: A custom copy button to override the default one. - use_transformer: If this is enabled, the default transformer(shikijs transformer) will be used. + use_transformers: If this is enabled, the default transformers(shikijs transformer) will be used. show_line_numbers: If this is enabled line numbers will be shown next to the code block. language: The language to use. theme: The theme to use ("light" or "dark"). From 10e144ddce024c4b17cf34afc747b0c49ebbd738 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 14:52:23 +0000 Subject: [PATCH 16/40] add hack for reflex-web/flexdown --- reflex/.templates/web/components/shiki/code.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/reflex/.templates/web/components/shiki/code.js b/reflex/.templates/web/components/shiki/code.js index 0a2b5d3d97e..c655c3a6076 100644 --- a/reflex/.templates/web/components/shiki/code.js +++ b/reflex/.templates/web/components/shiki/code.js @@ -5,7 +5,14 @@ export function Code ({code, theme, language, transformers, ...divProps}) { const [codeResult, setCodeResult] = useState("") useEffect(() => { async function fetchCode() { - const result = await codeToHtml(code, { + let final_code; + + if (Array.isArray(code)) { + final_code = code[0]; + } else { + final_code = code; + } + const result = await codeToHtml(final_code, { lang: language, theme, transformers From e3cbe79c532b03739614ebe5febdf28e46d85252 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 2 Oct 2024 15:00:18 +0000 Subject: [PATCH 17/40] unit test file commit --- tests/units/components/datadisplay/test_shiki_code.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/units/components/datadisplay/test_shiki_code.py diff --git a/tests/units/components/datadisplay/test_shiki_code.py b/tests/units/components/datadisplay/test_shiki_code.py new file mode 100644 index 00000000000..e69de29bb2d From e6cb4185b251f14a023dfaf93684b12d36d54a6f Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 3 Oct 2024 19:55:19 +0200 Subject: [PATCH 18/40] [ENG-3895] [ENG-3896] Update styling for shiki code block --- .../datadisplay/shiki_code_block.py | 199 ++++++++++++++++-- .../datadisplay/shiki_code_block.pyi | 19 +- 2 files changed, 195 insertions(+), 23 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 4ba4db2052d..d57ee36b7ab 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -7,16 +7,62 @@ from reflex.base import Base from reflex.components.component import Component, ComponentNamespace +from reflex.components.core.colors import color from reflex.components.core.cond import color_mode_cond +from reflex.components.el.elements.forms import Button 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.event import call_script from reflex.style import Style from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar + +def copy_script(id: str, code: str) -> Any: + """Copy script for the code block. + + Args: + id (str): The ID of the button element. + code (str): The code to be copied. + + Returns: + Any: The result of calling the script. + """ + return call_script( + f""" +const button = document.getElementById('{id}'); +const icon = button.querySelector('svg'); +const originalPath = icon.innerHTML; +const checkmarkPath = ''; + +function transition(element, scale, opacity) {{ + element.style.transform = `scale(${{scale}})`; + element.style.opacity = opacity; +}} + +navigator.clipboard.writeText(`{code}`).then(() => {{ + transition(icon, 0, '0'); + + setTimeout(() => {{ + icon.innerHTML = checkmarkPath; + icon.setAttribute('viewBox', '0 0 24 24'); + transition(icon, 1, '1'); + setTimeout(() => {{ + transition(icon, 0, '0'); + setTimeout(() => {{ + icon.innerHTML = originalPath; + transition(icon, 1, '1'); + }}, 125); + }}, 600); + }}, 125); +}}).catch(err => {{ + console.error('Failed to copy text: ', err); +}}); +""" + ) + + SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", "transformerNotationHighlight", @@ -32,7 +78,13 @@ "transformerRemoveNotationEscape", } LINE_NUMBER_STYLING = { - "code": {"counter-reset": "step", "counter-increment": "step 0"}, + "code": { + "counter-reset": "step", + "counter-increment": "step 0", + "display": "grid", + "line-height": "1.7", + "font-size": "0.875em", + }, "code .line::before": { "content": "counter(step)", "counter-increment": "step", @@ -43,6 +95,15 @@ "color": "rgba(115,138,148,.4)", }, } +BOX_PARENT_STYLING = { + "pre": { + "margin": "0", + "padding": "24px", + "background": "transparent", + "overflow-x": "auto", + "border-radius": "6px", + }, +} THEME_MAPPING = { "light": "one-light", @@ -336,18 +397,81 @@ class ShikiJsTransformer(ShikiBaseTransformers): ] style: Optional[Style] = Style( { - ".line": {"display": "inline", "padding-bottom": "0"}, + "code": {"line-height": "1.7", "font-size": "0.875em", "display": "grid"}, + # Diffs ".diff": { + "margin": "0 -24px", + "padding": "0 24px", + "width": "calc(100% + 48px)", "display": "inline-block", - "width": "100vw", - "margin": "0 -12px", - "padding": "0 12px", }, - ".diff.add": {"background-color": "#0505"}, - ".diff.remove": {"background-color": "#8005"}, - ".diff:before": {"position": "absolute", "left": "40px"}, - ".has-focused .line": {"filter": "blur(0.095rem)"}, - ".has-focused .focused": {"filter": "blur(0)"}, + ".diff.add": { + "background-color": "rgba(16, 185, 129, .14)", + "position": "relative", + }, + ".diff.remove": { + "background-color": "rgba(244, 63, 94, .14)", + "opacity": "0.7", + "position": "relative", + }, + ".diff.remove:after": { + "position": "absolute", + "left": "10px", + "content": "'-'", + "color": "#b34e52", + }, + ".diff.add:after": { + "position": "absolute", + "left": "10px", + "content": "'+'", + "color": "#18794e", + }, + # Highlight + ".highlighted": { + "background-color": "rgba(142, 150, 170, .14)", + "margin": "0 -24px", + "padding": "0 24px", + "width": "calc(100% + 48px)", + "display": "inline-block", + }, + ".highlighted.error": { + "background-color": "rgba(244, 63, 94, .14)", + }, + ".highlighted.warning": { + "background-color": "rgba(234, 179, 8, .14)", + }, + # Highlighted Word + ".highlighted-word": { + "background-color": color("gray", 2), + "border": f"1px solid {color('gray', 5)}", + "padding": "1px 3px", + "margin": "-1px -3px", + "border-radius": "4px", + }, + # Focused Lines + ".has-focused .line:not(.focused)": { + "opacity": "0.7", + "filter": "blur(0.095rem)", + "transition": "filter .35s, opacity .35s", + }, + ".has-focused:hover .line:not(.focused)": { + "opacity": "1", + "filter": "none", + }, + # White Space + # ".tab, .space": { + # "position": "relative", + # }, + # ".tab::before": { + # "content": "'⇥'", + # "position": "absolute", + # "opacity": "0.3", + # }, + # ".space::before": { + # "content": "'·'", + # "position": "absolute", + # "opacity": "0.3", + # }, } ) @@ -362,9 +486,11 @@ def __init__(self, **kwargs): style = kwargs.pop("style", None) if fns: kwargs["fns"] = [ - FunctionStringVar.create(x) - if not isinstance(x, FunctionStringVar) - else x + ( + FunctionStringVar.create(x) + if not isinstance(x, FunctionStringVar) + else x + ) for x in fns ] @@ -441,7 +567,7 @@ def create( return Box.create( code_block, *children[1:], - style=Style(transformer_styles), + style=Style({**transformer_styles, **BOX_PARENT_STYLING}), **code_wrapper_props, ) @@ -459,9 +585,11 @@ def add_imports(self) -> dict[str, list[str]]: imports[transformer.library].extend( [ImportVar(tag=str(fn)) for fn in transformer.fns] ) - self.lib_dependencies.append( - transformer.library - ) if transformer.library not in self.lib_dependencies else None + ( + self.lib_dependencies.append(transformer.library) + if transformer.library not in self.lib_dependencies + else None + ) return imports @classmethod @@ -582,13 +710,40 @@ def create( if can_copy: code = children[0] + button_id = ( + f"copy-button-{hash(code)}" # Generate a unique ID for each button + ) 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"}), + Icon.create(tag="copy", size=16, color=color("gray", 11)), + id=button_id, + on_click=copy_script(button_id, code), + style=Style( + { + "position": "absolute", + "top": "4px", + "right": "4px", + "background": color("gray", 3), + "border": "1px solid", + "border-color": color("gray", 5), + "border-radius": "6px", + "padding": "5px", + "opacity": "1", + "cursor": "pointer", + "_hover": { + "background": color("gray", 4), + }, + "transition": "background 0.250s ease-out", + "&>svg": { + "transition": "transform 0.250s ease-out, opacity 0.250s ease-out", + }, + "_active": { + "background": color("gray", 5), + }, + } + ), ) ) else: diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index 3353e004b8b..91d8044c4e0 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -12,6 +12,8 @@ from reflex.style import Style from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar +def copy_script(id: str, code: str) -> Any: ... + SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", "transformerNotationHighlight", @@ -25,7 +27,13 @@ SHIKIJS_TRANSFORMER_FNS = { "transformerRemoveNotationEscape", } LINE_NUMBER_STYLING = { - "code": {"counter-reset": "step", "counter-increment": "step 0"}, + "code": { + "counter-reset": "step", + "counter-increment": "step 0", + "display": "grid", + "line-height": "1.7", + "font-size": "0.875em", + }, "code .line::before": { "content": "counter(step)", "counter-increment": "step", @@ -36,6 +44,15 @@ LINE_NUMBER_STYLING = { "color": "rgba(115,138,148,.4)", }, } +BOX_PARENT_STYLING = { + "pre": { + "margin": "0", + "padding": "24px", + "background": "transparent", + "overflow-x": "auto", + "border-radius": "6px", + } +} THEME_MAPPING = { "light": "one-light", "dark": "one-dark-pro", From 860449f814436fd6f706ad1663e872beff9b9785 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 09:56:04 +0000 Subject: [PATCH 19/40] strip transformer triggers --- .../datadisplay/shiki_code_block.py | 21 ++++++++++++++++++- reflex/vars/sequence.py | 20 ++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index d57ee36b7ab..9b2be4beb32 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -2,6 +2,7 @@ from __future__ import annotations +import re from collections import defaultdict from typing import Any, Literal, Optional, Union @@ -17,6 +18,7 @@ from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar +from reflex.vars.sequence import StringVar, string_replace_operation def copy_script(id: str, code: str) -> Any: @@ -719,7 +721,9 @@ def create( else Button.create( Icon.create(tag="copy", size=16, color=color("gray", 11)), id=button_id, - on_click=copy_script(button_id, code), + on_click=copy_script( + button_id, cls._strip_transformer_triggers(code) + ), style=Style( { "position": "absolute", @@ -768,6 +772,21 @@ def _map_languages(language: str) -> str: return LANGUAGE_MAPPING[language] return language + @staticmethod + def _strip_transformer_triggers(code: str | Var) -> StringVar | str: + if not isinstance(code, (Var, str)): + raise ValueError( + f"code should be string literal or a Var type. Got {type(code)} instead." + ) + + if isinstance(code, Var): + return string_replace_operation( + code, StringVar(_js_expr=r"/\/\/ \[!code.*?\]/g", _var_type=str), "" + ) + if isinstance(code, str): + cleaned_code = re.sub(r"// \[!code.*?\]", "", code) + return cleaned_code + class TransformerNamespace(ComponentNamespace): """Namespace for the Transformers.""" diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 1493000282f..39139ce3f74 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -529,6 +529,26 @@ def array_join_operation(array: ArrayVar, sep: StringVar[Any] | str = ""): return var_operation_return(js_expression=f"{array}.join({sep})", var_type=str) +@var_operation +def string_replace_operation( + string: StringVar, search_value: StringVar | str, new_value: StringVar | str +): + """Replace a string with a value. + + Args: + string: The string. + search_value: The string to search. + new_value: The value to be replaced with. + + Returns: + The string replace operation. + """ + return var_operation_return( + js_expression=f"{string}.replace({search_value}, {new_value})", + var_type=str, + ) + + # Compile regex for finding reflex var tags. _decode_var_pattern_re = ( rf"{constants.REFLEX_VAR_OPENING_TAG}(.*?){constants.REFLEX_VAR_CLOSING_TAG}" From 38f396677d7a8514b65f52f864f1f319d88c4b3c Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 10:10:13 +0000 Subject: [PATCH 20/40] minor refactor --- reflex/components/datadisplay/shiki_code_block.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 9b2be4beb32..31931b9ed14 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -784,8 +784,8 @@ def _strip_transformer_triggers(code: str | Var) -> StringVar | str: code, StringVar(_js_expr=r"/\/\/ \[!code.*?\]/g", _var_type=str), "" ) if isinstance(code, str): - cleaned_code = re.sub(r"// \[!code.*?\]", "", code) - return cleaned_code + return re.sub(r"// \[!code.*?\]", "", code) + class TransformerNamespace(ComponentNamespace): From 8e0044d8564fe4ba5da2c8fa05995c6cae38cd40 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 10:11:20 +0000 Subject: [PATCH 21/40] linter --- reflex/components/datadisplay/shiki_code_block.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 31931b9ed14..71cd9a441ad 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -787,7 +787,6 @@ def _strip_transformer_triggers(code: str | Var) -> StringVar | str: return re.sub(r"// \[!code.*?\]", "", code) - class TransformerNamespace(ComponentNamespace): """Namespace for the Transformers.""" From d1cd68d16de63ca9170a81d3aba7fbec41243cc1 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 11:05:21 +0000 Subject: [PATCH 22/40] fix pyright --- reflex/components/datadisplay/shiki_code_block.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 71cd9a441ad..14638ea13a1 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -15,13 +15,14 @@ from reflex.components.radix.themes.layout.box import Box from reflex.event import call_script from reflex.style import Style +from reflex.utils.exceptions import VarTypeError from reflex.utils.imports import ImportVar from reflex.vars.base import LiteralVar, Var from reflex.vars.function import FunctionStringVar from reflex.vars.sequence import StringVar, string_replace_operation -def copy_script(id: str, code: str) -> Any: +def copy_script(id: str, code: StringVar | str) -> Any: """Copy script for the code block. Args: @@ -773,10 +774,10 @@ def _map_languages(language: str) -> str: return language @staticmethod - def _strip_transformer_triggers(code: str | Var) -> StringVar | str: - if not isinstance(code, (Var, str)): - raise ValueError( - f"code should be string literal or a Var type. Got {type(code)} instead." + def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: + if not isinstance(code, (StringVar, str)): + raise VarTypeError( + f"code should be string literal or a StringVar type. Got {type(code)} instead." ) if isinstance(code, Var): From fa9b192b41cc416702fc0294d21d2715330f6448 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 11:06:38 +0000 Subject: [PATCH 23/40] pyi fix --- reflex/components/datadisplay/shiki_code_block.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index 91d8044c4e0..5c6f28abc6a 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -11,8 +11,9 @@ from reflex.event import EventHandler, EventSpec from reflex.style import Style from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar +from reflex.vars.sequence import StringVar -def copy_script(id: str, code: str) -> Any: ... +def copy_script(id: str, code: StringVar | str) -> Any: ... SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", From f20b32b129cded35ce7fe9528278c882468fb22f Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 13:33:26 +0000 Subject: [PATCH 24/40] add unit tests --- .../components/datadisplay/test_shiki_code.py | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/tests/units/components/datadisplay/test_shiki_code.py b/tests/units/components/datadisplay/test_shiki_code.py index e69de29bb2d..296a486b6e4 100644 --- a/tests/units/components/datadisplay/test_shiki_code.py +++ b/tests/units/components/datadisplay/test_shiki_code.py @@ -0,0 +1,168 @@ +import pytest + +from reflex.components.datadisplay.shiki_code_block import ( + ShikiBaseTransformers, + ShikiCodeBlock, + ShikiHighLevelCodeBlock, + ShikiJsTransformer, +) +from reflex.components.el.elements.forms import Button +from reflex.components.lucide.icon import Icon +from reflex.components.radix.themes.layout.box import Box +from reflex.vars import Var + + +@pytest.mark.parametrize( + "library, fns, expected_output, raises_exception", + [ + ("some_library", ["function_one"], ["function_one"], False), + ("some_library", [123], None, True), + ("some_library", [], [], False), + ( + "some_library", + ["function_one", "function_two"], + ["function_one", "function_two"], + False, + ), + ("", ["function_one"], ["function_one"], False), + ("some_library", ["function_one", 789], None, True), + ("", [], [], False), + ], +) +def test_create_transformer(library, fns, expected_output, raises_exception): + if raises_exception: + # Ensure ValueError is raised for invalid cases + with pytest.raises(ValueError): + ShikiCodeBlock.create_transformer(library, fns) + else: + transformer = ShikiCodeBlock.create_transformer(library, fns) + assert isinstance(transformer, ShikiBaseTransformers) + assert transformer.library == library + + # Verify that the functions are correctly wrapped in FunctionStringVar + function_names = [str(fn) for fn in transformer.fns] + assert function_names == expected_output + + +@pytest.mark.parametrize( + "code_block, children, props, expected_first_child, expected_styles", + [ + ("print('Hello')", ["print('Hello')"], {}, "print('Hello')", {}), + ( + "print('Hello')", + ["print('Hello')", "More content"], + {}, + "print('Hello')", + {}, + ), + ( + "print('Hello')", + ["print('Hello')"], + { + "transformers": [ + ShikiBaseTransformers(library="lib", fns=[], style={"color": "red"}) + ] + }, + "print('Hello')", + {"color": "red"}, + ), + ( + "print('Hello')", + ["print('Hello')"], + { + "transformers": [ + ShikiBaseTransformers(library="lib", fns=[], style={"color": "red"}) + ], + "style": {"background": "blue"}, + }, + "print('Hello')", + {"color": "red", "background": "blue"}, + ), + ], +) +def test_create_shiki_code_block( + code_block, children, props, expected_first_child, expected_styles +): + component = ShikiCodeBlock.create(code_block, *children, **props) + + # Test that the created component is a Box + assert isinstance(component, Box) + + # Test that the first child is the code + code_block_component = component.children[0] + print(code_block_component.code._var_value) + assert code_block_component.code._var_value == expected_first_child + + applied_styles = component.style + for key, value in expected_styles.items(): + assert Var.create(applied_styles[key])._var_value == value + + +@pytest.mark.parametrize( + "children, props, expected_transformers, expected_button_type", + [ + (["print('Hello')"], {"use_transformers": True}, [ShikiJsTransformer], None), + (["print('Hello')"], {"can_copy": True}, None, Button), + ( + ["print('Hello')"], + { + "can_copy": True, + "copy_button": Button.create(Icon.create(tag="a_arrow_down")), + }, + None, + Button, + ), + ], +) +def test_create_shiki_high_level_code_block( + children, props, expected_transformers, expected_button_type +): + component = ShikiHighLevelCodeBlock.create(*children, **props) + + # Test that the created component is a Box + assert isinstance(component, Box) + + # Test that the first child is the code block component + code_block_component = component.children[0] + assert code_block_component.code._var_value == children[0] + + # Check if the transformer is set correctly if expected + if expected_transformers: + exp_trans_names = [t.__name__ for t in expected_transformers] + for transformer in code_block_component.transformers._var_value: + assert type(transformer).__name__ in exp_trans_names + + # Check if the second child is the copy button if can_copy is True + if props.get("can_copy", False): + if props.get("copy_button"): + assert isinstance(component.children[1], expected_button_type) + assert component.children[1] == props["copy_button"] + else: + assert isinstance(component.children[1], expected_button_type) + else: + assert len(component.children) == 1 + + +@pytest.mark.parametrize( + "children, props", + [ + (["print('Hello')"], {"theme": "dark"}), + (["print('Hello')"], {"language": "javascript"}), + ], +) +def test_shiki_high_level_code_block_theme_language_mapping(children, props): + component = ShikiHighLevelCodeBlock.create(*children, **props) + + # Test that the theme is mapped correctly + if "theme" in props: + assert component.children[ + 0 + ].theme._var_value == ShikiHighLevelCodeBlock._map_themes(props["theme"]) + + # Test that the language is mapped correctly + if "language" in props: + assert component.children[ + 0 + ].language._var_value == ShikiHighLevelCodeBlock._map_languages( + props["language"] + ) From 4ba277cea52b5355c49b8d6829a6df6a2286d0ae Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 13:42:12 +0000 Subject: [PATCH 25/40] sneaky pyright ignore --- .../components/datadisplay/test_shiki_code.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/units/components/datadisplay/test_shiki_code.py b/tests/units/components/datadisplay/test_shiki_code.py index 296a486b6e4..eb473ba06d6 100644 --- a/tests/units/components/datadisplay/test_shiki_code.py +++ b/tests/units/components/datadisplay/test_shiki_code.py @@ -9,6 +9,7 @@ from reflex.components.el.elements.forms import Button from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.layout.box import Box +from reflex.style import Style from reflex.vars import Var @@ -60,7 +61,9 @@ def test_create_transformer(library, fns, expected_output, raises_exception): ["print('Hello')"], { "transformers": [ - ShikiBaseTransformers(library="lib", fns=[], style={"color": "red"}) + ShikiBaseTransformers( + library="lib", fns=[], style=Style({"color": "red"}) + ) ] }, "print('Hello')", @@ -71,7 +74,9 @@ def test_create_transformer(library, fns, expected_output, raises_exception): ["print('Hello')"], { "transformers": [ - ShikiBaseTransformers(library="lib", fns=[], style={"color": "red"}) + ShikiBaseTransformers( + library="lib", fns=[], style=Style({"color": "red"}) + ) ], "style": {"background": "blue"}, }, @@ -90,8 +95,7 @@ def test_create_shiki_code_block( # Test that the first child is the code code_block_component = component.children[0] - print(code_block_component.code._var_value) - assert code_block_component.code._var_value == expected_first_child + assert code_block_component.code._var_value == expected_first_child # type: ignore applied_styles = component.style for key, value in expected_styles.items(): @@ -124,12 +128,12 @@ def test_create_shiki_high_level_code_block( # Test that the first child is the code block component code_block_component = component.children[0] - assert code_block_component.code._var_value == children[0] + assert code_block_component.code._var_value == children[0] # type: ignore # Check if the transformer is set correctly if expected if expected_transformers: exp_trans_names = [t.__name__ for t in expected_transformers] - for transformer in code_block_component.transformers._var_value: + for transformer in code_block_component.transformers._var_value: # type: ignore assert type(transformer).__name__ in exp_trans_names # Check if the second child is the copy button if can_copy is True @@ -157,12 +161,12 @@ def test_shiki_high_level_code_block_theme_language_mapping(children, props): if "theme" in props: assert component.children[ 0 - ].theme._var_value == ShikiHighLevelCodeBlock._map_themes(props["theme"]) + ].theme._var_value == ShikiHighLevelCodeBlock._map_themes(props["theme"]) # type: ignore # Test that the language is mapped correctly if "language" in props: assert component.children[ 0 - ].language._var_value == ShikiHighLevelCodeBlock._map_languages( + ].language._var_value == ShikiHighLevelCodeBlock._map_languages( # type: ignore props["language"] ) From 89b3891be854dcecfd5c0c6d50b5c98198f846d0 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 16:36:13 +0000 Subject: [PATCH 26/40] the transformer trigger regex should remove the language comment character --- reflex/components/datadisplay/shiki_code_block.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 14638ea13a1..f34bb51c6b9 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -782,10 +782,10 @@ def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: if isinstance(code, Var): return string_replace_operation( - code, StringVar(_js_expr=r"/\/\/ \[!code.*?\]/g", _var_type=str), "" + code, StringVar(_js_expr=r"/[\/#]+ *\[!code.*?\]/g", _var_type=str), "" ) if isinstance(code, str): - return re.sub(r"// \[!code.*?\]", "", code) + return re.sub(r"[\/#]+ *\[!code.*?\]", "", code) class TransformerNamespace(ComponentNamespace): From 4b356804092dd102b5cb2308b46774c30529940e Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 17:21:42 +0000 Subject: [PATCH 27/40] minor refactor --- reflex/components/datadisplay/shiki_code_block.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index f34bb51c6b9..0f95d1b541a 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -779,10 +779,11 @@ def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: raise VarTypeError( f"code should be string literal or a StringVar type. Got {type(code)} instead." ) + regex_pattern = r"[\/#]+ *\[!code.*?\]" if isinstance(code, Var): return string_replace_operation( - code, StringVar(_js_expr=r"/[\/#]+ *\[!code.*?\]/g", _var_type=str), "" + code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), "" ) if isinstance(code, str): return re.sub(r"[\/#]+ *\[!code.*?\]", "", code) From e178cf7c0a8b4259e9c39683ccd819666ee878b3 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 4 Oct 2024 17:22:43 +0000 Subject: [PATCH 28/40] fix silly mistake --- reflex/components/datadisplay/shiki_code_block.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 0f95d1b541a..555167deb15 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -786,7 +786,7 @@ def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str: code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), "" ) if isinstance(code, str): - return re.sub(r"[\/#]+ *\[!code.*?\]", "", code) + return re.sub(regex_pattern, "", code) class TransformerNamespace(ComponentNamespace): From 3743492b9ffacea1a1357d5bc1c93fa11249c74f Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 15 Oct 2024 16:15:29 +0000 Subject: [PATCH 29/40] component mapping in markdown should use the first child for codeblock --- reflex/components/markdown/markdown.py | 5 ++++- reflex/vars/function.py | 1 + reflex/vars/sequence.py | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index 1665144fd32..27d8034c6f3 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -20,6 +20,9 @@ from reflex.utils import types from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import LiteralVar, Var +from reflex.vars.sequence import string_ternary_operation +from reflex.vars.function import ARRAY_ISARRAY + # Special vars used in the component map. _CHILDREN = Var(_js_expr="children", _var_type=str) @@ -199,7 +202,7 @@ def get_component(self, tag: str, **props) -> Component: raise ValueError(f"No markdown component found for tag: {tag}.") special_props = [_PROPS_IN_TAG] - children = [_CHILDREN] + children = [_CHILDREN if not tag == "codeblock" else string_ternary_operation(ARRAY_ISARRAY.call(_CHILDREN), _CHILDREN.to(LiteralVar)[0], _CHILDREN)] # For certain tags, the props from the markdown renderer are not actually valid for the component. if tag in NO_PROPS_TAGS: diff --git a/reflex/vars/function.py b/reflex/vars/function.py index a1f7fb7bd69..9d734a4582b 100644 --- a/reflex/vars/function.py +++ b/reflex/vars/function.py @@ -180,6 +180,7 @@ def create( JSON_STRINGIFY = FunctionStringVar.create("JSON.stringify") +ARRAY_ISARRAY = FunctionStringVar.create("Array.isArray") PROTOTYPE_TO_STRING = FunctionStringVar.create( "((__to_string) => __to_string.toString())" ) diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 39139ce3f74..675d15d88a1 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -548,6 +548,24 @@ def string_replace_operation( var_type=str, ) +def string_ternary_operation(condition, true_operation, false_operation): + """ + This function generates the JavaScript ternary operation as a string. + + Parameters: + condition: The condition for the ternary operation. + true_operation: The operation if the condition is true. + false_operation: The operation if the condition is false. + + Returns: + A string representing the JavaScript ternary operation. + """ + return var_operation_return( + js_expression=f"({condition} ? {true_operation} : {false_operation})", + var_type=str, + ) + + # Compile regex for finding reflex var tags. _decode_var_pattern_re = ( From 665179309e32554f6bc358d8ea659e7985ef7866 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 16 Oct 2024 12:51:02 +0000 Subject: [PATCH 30/40] use ternary operator in numbers.py, move code block args to class for docs discoverability --- .../components/datadisplay/shiki_code_block.py | 17 ++++++++++------- reflex/components/markdown/markdown.py | 4 ++-- reflex/vars/sequence.py | 18 ------------------ 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 555167deb15..37c75053c8b 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -363,6 +363,7 @@ def copy_script(id: str, code: StringVar | str) -> Any: "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -668,20 +669,22 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): # If this is enabled line numbers will be shown next to the code block. show_line_numbers: Var[bool] + # Whether a copy button should appear. + can_copy: Var[bool] = False + + # copy_button: A custom copy button to override the default one. + copy_button: Var[Optional[Union[Component | bool]]] = None + @classmethod def create( cls, *children, - can_copy: bool | None = False, - copy_button: bool | Component | None = None, **props, ) -> Component: """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). Args: *children: The children of the component. - can_copy: Whether a copy button should appear. - copy_button: A custom copy button to override the default one. **props: The props to pass to the component. Returns: @@ -690,6 +693,8 @@ def create( use_transformers = props.pop("use_transformers", False) show_line_numbers = props.pop("show_line_numbers", False) language = props.pop("language", None) + can_copy = props.pop("can_copy", False) + copy_button = props.pop("copy_button", None) if use_transformers: props["transformers"] = [ShikiJsTransformer()] @@ -704,7 +709,7 @@ def create( theme = props.pop("theme", None) props["theme"] = props["theme"] = ( cls._map_themes(theme) - if theme + if theme is not None else color_mode_cond( # Default color scheme responds to global color mode. light="one-light", dark="one-dark-pro", @@ -751,8 +756,6 @@ def create( ), ) ) - else: - copy_button = None if copy_button: return ShikiCodeBlock.create( diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index 27d8034c6f3..03df804f21a 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -20,7 +20,7 @@ from reflex.utils import types from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import LiteralVar, Var -from reflex.vars.sequence import string_ternary_operation +from reflex.vars.number import ternary_operation from reflex.vars.function import ARRAY_ISARRAY @@ -202,7 +202,7 @@ def get_component(self, tag: str, **props) -> Component: raise ValueError(f"No markdown component found for tag: {tag}.") special_props = [_PROPS_IN_TAG] - children = [_CHILDREN if not tag == "codeblock" else string_ternary_operation(ARRAY_ISARRAY.call(_CHILDREN), _CHILDREN.to(LiteralVar)[0], _CHILDREN)] + children = [_CHILDREN if not tag == "codeblock" else ternary_operation(ARRAY_ISARRAY.call(_CHILDREN), _CHILDREN.to(LiteralVar)[0], _CHILDREN).to(str)] # For certain tags, the props from the markdown renderer are not actually valid for the component. if tag in NO_PROPS_TAGS: diff --git a/reflex/vars/sequence.py b/reflex/vars/sequence.py index 675d15d88a1..39139ce3f74 100644 --- a/reflex/vars/sequence.py +++ b/reflex/vars/sequence.py @@ -548,24 +548,6 @@ def string_replace_operation( var_type=str, ) -def string_ternary_operation(condition, true_operation, false_operation): - """ - This function generates the JavaScript ternary operation as a string. - - Parameters: - condition: The condition for the ternary operation. - true_operation: The operation if the condition is true. - false_operation: The operation if the condition is false. - - Returns: - A string representing the JavaScript ternary operation. - """ - return var_operation_return( - js_expression=f"({condition} ? {true_operation} : {false_operation})", - var_type=str, - ) - - # Compile regex for finding reflex var tags. _decode_var_pattern_re = ( From a18a5b021dabd5797c50ad354e973b38457e3c2c Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 16 Oct 2024 15:47:03 +0000 Subject: [PATCH 31/40] precommit --- .../datadisplay/shiki_code_block.py | 4 +-- .../datadisplay/shiki_code_block.pyi | 27 +++++++++++++------ reflex/components/markdown/markdown.py | 11 +++++--- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 37c75053c8b..71e603021fc 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -670,10 +670,10 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): show_line_numbers: Var[bool] # Whether a copy button should appear. - can_copy: Var[bool] = False + can_copy: Var[bool] = Var.create(False) # copy_button: A custom copy button to override the default one. - copy_button: Var[Optional[Union[Component | bool]]] = None + copy_button: Var[Optional[Union[Component, bool]]] = Var.create(None) @classmethod def create( diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index 5c6f28abc6a..bee4906af2f 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -309,6 +309,7 @@ LiteralCodeTheme = Literal[ "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -815,6 +816,7 @@ class ShikiCodeBlock(Component): "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -869,6 +871,7 @@ class ShikiCodeBlock(Component): "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -981,10 +984,12 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): def create( # type: ignore cls, *children, - can_copy: Optional[bool] = False, - copy_button: Optional[Union[Component, bool]] = None, use_transformers: Optional[Union[Var[bool], bool]] = None, show_line_numbers: Optional[Union[Var[bool], bool]] = None, + can_copy: Optional[Union[Var[bool], bool]] = None, + copy_button: Optional[ + Union[Component, Var[Optional[Union[Component, bool]]], bool] + ] = None, language: Optional[ Union[ Literal[ @@ -1456,6 +1461,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -1510,6 +1516,7 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -1592,10 +1599,10 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): Args: *children: The children of the component. - can_copy: Whether a copy button should appear. - copy_button: A custom copy button to override the default one. use_transformers: If this is enabled, the default transformers(shikijs transformer) will be used. show_line_numbers: If this is enabled line numbers will be shown next to the code block. + can_copy: Whether a copy button should appear. + copy_button: copy_button: A custom copy button to override the default one. language: The language to use. theme: The theme to use ("light" or "dark"). themes: The set of themes to use for different modes. @@ -1625,10 +1632,12 @@ class CodeblockNamespace(ComponentNamespace): @staticmethod def __call__( *children, - can_copy: Optional[bool] = False, - copy_button: Optional[Union[Component, bool]] = None, use_transformers: Optional[Union[Var[bool], bool]] = None, show_line_numbers: Optional[Union[Var[bool], bool]] = None, + can_copy: Optional[Union[Var[bool], bool]] = None, + copy_button: Optional[ + Union[Component, Var[Optional[Union[Component, bool]]], bool] + ] = None, language: Optional[ Union[ Literal[ @@ -2100,6 +2109,7 @@ class CodeblockNamespace(ComponentNamespace): "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -2154,6 +2164,7 @@ class CodeblockNamespace(ComponentNamespace): "nord", "one-dark-pro", "one-light", + "plain", "plastic", "poimandres", "red", @@ -2236,10 +2247,10 @@ class CodeblockNamespace(ComponentNamespace): Args: *children: The children of the component. - can_copy: Whether a copy button should appear. - copy_button: A custom copy button to override the default one. use_transformers: If this is enabled, the default transformers(shikijs transformer) will be used. show_line_numbers: If this is enabled line numbers will be shown next to the code block. + can_copy: Whether a copy button should appear. + copy_button: copy_button: A custom copy button to override the default one. language: The language to use. theme: The theme to use ("light" or "dark"). themes: The set of themes to use for different modes. diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index 03df804f21a..eec71a8f08b 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -20,9 +20,8 @@ from reflex.utils import types from reflex.utils.imports import ImportDict, ImportVar from reflex.vars.base import LiteralVar, Var -from reflex.vars.number import ternary_operation from reflex.vars.function import ARRAY_ISARRAY - +from reflex.vars.number import ternary_operation # Special vars used in the component map. _CHILDREN = Var(_js_expr="children", _var_type=str) @@ -202,7 +201,13 @@ def get_component(self, tag: str, **props) -> Component: raise ValueError(f"No markdown component found for tag: {tag}.") special_props = [_PROPS_IN_TAG] - children = [_CHILDREN if not tag == "codeblock" else ternary_operation(ARRAY_ISARRAY.call(_CHILDREN), _CHILDREN.to(LiteralVar)[0], _CHILDREN).to(str)] + children = [ + _CHILDREN + if tag != "codeblock" + else ternary_operation( + ARRAY_ISARRAY.call(_CHILDREN), _CHILDREN.to(list)[0], _CHILDREN + ).to(str) + ] # For certain tags, the props from the markdown renderer are not actually valid for the component. if tag in NO_PROPS_TAGS: From a634a266f4f02c40095423e44d72402cb467f18f Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 16 Oct 2024 15:56:42 +0000 Subject: [PATCH 32/40] pyright fix --- reflex/components/markdown/markdown.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index eec71a8f08b..e3688790e9a 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -205,7 +205,9 @@ def get_component(self, tag: str, **props) -> Component: _CHILDREN if tag != "codeblock" else ternary_operation( - ARRAY_ISARRAY.call(_CHILDREN), _CHILDREN.to(list)[0], _CHILDREN + ARRAY_ISARRAY.call(_CHILDREN), + _CHILDREN.to(list)[0], + _CHILDREN, # type: ignore ).to(str) ] From 8bf79bdeff09e4ae906bc8b09932c48a4d5223fb Mon Sep 17 00:00:00 2001 From: Carlos Date: Wed, 16 Oct 2024 21:48:49 +0200 Subject: [PATCH 33/40] remove id on copy button animation --- .../datadisplay/shiki_code_block.py | 71 ++++++++++--------- .../datadisplay/shiki_code_block.pyi | 2 +- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 71e603021fc..7c46d8f23dd 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -22,11 +22,10 @@ from reflex.vars.sequence import StringVar, string_replace_operation -def copy_script(id: str, code: StringVar | str) -> Any: - """Copy script for the code block. +def copy_script(code: StringVar | str) -> Any: + """Copy script for the code block and modify the child SVG element. Args: - id (str): The ID of the button element. code (str): The code to be copied. Returns: @@ -34,33 +33,41 @@ def copy_script(id: str, code: StringVar | str) -> Any: """ return call_script( f""" -const button = document.getElementById('{id}'); -const icon = button.querySelector('svg'); -const originalPath = icon.innerHTML; -const checkmarkPath = ''; - -function transition(element, scale, opacity) {{ - element.style.transform = `scale(${{scale}})`; - element.style.opacity = opacity; -}} - -navigator.clipboard.writeText(`{code}`).then(() => {{ - transition(icon, 0, '0'); - - setTimeout(() => {{ - icon.innerHTML = checkmarkPath; - icon.setAttribute('viewBox', '0 0 24 24'); - transition(icon, 1, '1'); - setTimeout(() => {{ - transition(icon, 0, '0'); +// Event listener for the parent click +document.addEventListener('click', function(event) {{ + const parent = event.target.closest('div'); // Assumes the parent is a
or any container + if (parent) {{ + const svgIcon = parent.querySelector('svg'); // Always targets the child element + const originalPath = svgIcon.innerHTML; + const checkmarkPath = ''; // Checkmark SVG path + + function transition(element, scale, opacity) {{ + element.style.transform = `scale(${{scale}})`; + element.style.opacity = opacity; + }} + + // Copy the code to clipboard + navigator.clipboard.writeText(`{code}`).then(() => {{ + // Animate the SVG + transition(svgIcon, 0, '0'); + setTimeout(() => {{ - icon.innerHTML = originalPath; - transition(icon, 1, '1'); + svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark + svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary + transition(svgIcon, 1, '1'); + + setTimeout(() => {{ + transition(svgIcon, 0, '0'); + setTimeout(() => {{ + svgIcon.innerHTML = originalPath; // Restore original SVG content + transition(svgIcon, 1, '1'); + }}, 125); + }}, 600); }}, 125); - }}, 600); - }}, 125); -}}).catch(err => {{ - console.error('Failed to copy text: ', err); + }}).catch(err => {{ + console.error('Failed to copy text: ', err); + }}); + }} }}); """ ) @@ -718,18 +725,12 @@ def create( if can_copy: code = children[0] - button_id = ( - f"copy-button-{hash(code)}" # Generate a unique ID for each button - ) copy_button = ( # type: ignore copy_button if copy_button is not None else Button.create( Icon.create(tag="copy", size=16, color=color("gray", 11)), - id=button_id, - on_click=copy_script( - button_id, cls._strip_transformer_triggers(code) - ), + on_click=copy_script(cls._strip_transformer_triggers(code)), style=Style( { "position": "absolute", diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index bee4906af2f..fda167e1ad5 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -13,7 +13,7 @@ from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar from reflex.vars.sequence import StringVar -def copy_script(id: str, code: StringVar | str) -> Any: ... +def copy_script(code: StringVar | str) -> Any: ... SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", From 340699125f19b268dad57391a0c03d6dec3c41b0 Mon Sep 17 00:00:00 2001 From: Elijah Date: Wed, 16 Oct 2024 20:17:22 +0000 Subject: [PATCH 34/40] pyright fix for real --- reflex/components/markdown/markdown.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index e3688790e9a..1b7d9f28531 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -205,9 +205,9 @@ def get_component(self, tag: str, **props) -> Component: _CHILDREN if tag != "codeblock" else ternary_operation( - ARRAY_ISARRAY.call(_CHILDREN), + ARRAY_ISARRAY.call(_CHILDREN), # type: ignore _CHILDREN.to(list)[0], - _CHILDREN, # type: ignore + _CHILDREN, ).to(str) ] From ea2c5797be56f861156ebd871e1d91ed3682fea0 Mon Sep 17 00:00:00 2001 From: Elijah Date: Thu, 17 Oct 2024 13:35:28 +0000 Subject: [PATCH 35/40] pyi fix --- .../datadisplay/shiki_code_block.pyi | 154 ++++++------------ 1 file changed, 47 insertions(+), 107 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index fda167e1ad5..d799230dc41 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -3,11 +3,11 @@ # ------------------- DO NOT EDIT ---------------------- # This file was generated by `reflex/utils/pyi_generator.py`! # ------------------------------------------------------ -from typing import Any, Callable, Dict, Literal, Optional, Union, overload +from typing import Any, Dict, Literal, Optional, Union, overload from reflex.base import Base from reflex.components.component import Component, ComponentNamespace -from reflex.event import EventHandler, EventSpec +from reflex.event import EventType from reflex.style import Style from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar @@ -913,41 +913,21 @@ class ShikiCodeBlock(Component): class_name: Optional[Any] = None, autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, - on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_context_menu: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_double_click: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_mouse_down: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_enter: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_leave: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_move: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_out: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_over: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_up: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_unmount: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, + on_blur: Optional[EventType[[]]] = None, + on_click: Optional[EventType[[]]] = None, + on_context_menu: Optional[EventType[[]]] = None, + on_double_click: Optional[EventType[[]]] = None, + on_focus: Optional[EventType[[]]] = None, + on_mount: Optional[EventType[[]]] = None, + on_mouse_down: Optional[EventType[[]]] = None, + on_mouse_enter: Optional[EventType[[]]] = None, + on_mouse_leave: Optional[EventType[[]]] = None, + on_mouse_move: Optional[EventType[[]]]= None, + on_mouse_out: Optional[EventType[[]]] = None, + on_mouse_over: Optional[EventType[[]]]= None, + on_mouse_up: Optional[EventType[[]]] = None, + on_scroll: Optional[EventType[[]]] = None, + on_unmount: Optional[EventType[[]]] = None, **props, ) -> "ShikiCodeBlock": """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). @@ -1558,41 +1538,21 @@ class ShikiHighLevelCodeBlock(ShikiCodeBlock): class_name: Optional[Any] = None, autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, - on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_context_menu: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_double_click: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_mouse_down: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_enter: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_leave: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_move: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_out: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_over: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_up: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_unmount: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, + on_blur: Optional[EventType[[]]] = None, + on_click: Optional[EventType[[]]] = None, + on_context_menu: Optional[EventType[[]]] = None, + on_double_click: Optional[EventType[[]]] = None, + on_focus: Optional[EventType[[]]] = None, + on_mount: Optional[EventType[[]]] = None, + on_mouse_down: Optional[EventType[[]]] = None, + on_mouse_enter: Optional[EventType[[]]] = None, + on_mouse_leave: Optional[EventType[[]]] = None, + on_mouse_move: Optional[EventType[[]]] = None, + on_mouse_out: Optional[EventType[[]]] = None, + on_mouse_over: Optional[EventType[[]]] = None, + on_mouse_up: Optional[EventType[[]]] = None, + on_scroll: Optional[EventType[[]]] = None, + on_unmount: Optional[EventType[[]]] = None, **props, ) -> "ShikiHighLevelCodeBlock": """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). @@ -2206,41 +2166,21 @@ class CodeblockNamespace(ComponentNamespace): class_name: Optional[Any] = None, autofocus: Optional[bool] = None, custom_attrs: Optional[Dict[str, Union[Var, str]]] = None, - on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_context_menu: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_double_click: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_mouse_down: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_enter: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_leave: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_move: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_out: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_over: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_mouse_up: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, - on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None, - on_unmount: Optional[ - Union[EventHandler, EventSpec, list, Callable, Var] - ] = None, + on_blur: Optional[EventType[[]]] = None, + on_click: Optional[EventType[[]]] = None, + on_context_menu: Optional[EventType[[]]] = None, + on_double_click: Optional[EventType[[]]] = None, + on_focus: Optional[EventType[[]]] = None, + on_mount: Optional[EventType[[]]] = None, + on_mouse_down: Optional[EventType[[]]] = None, + on_mouse_enter: Optional[EventType[[]]] = None, + on_mouse_leave: Optional[EventType[[]]] = None, + on_mouse_move: Optional[EventType[[]]] = None, + on_mouse_out: Optional[EventType[[]]] = None, + on_mouse_over: Optional[EventType[[]]] = None, + on_mouse_up: Optional[EventType[[]]] = None, + on_scroll: Optional[EventType[[]]] = None, + on_unmount: Optional[EventType[[]]] = None, **props, ) -> "ShikiHighLevelCodeBlock": """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/). From 73590040dfca012032990e91bf3311a160d3c4ad Mon Sep 17 00:00:00 2001 From: Elijah Date: Thu, 17 Oct 2024 13:41:05 +0000 Subject: [PATCH 36/40] pyi fix fr --- reflex/components/datadisplay/shiki_code_block.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index d799230dc41..a80c636ef57 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -922,9 +922,9 @@ class ShikiCodeBlock(Component): on_mouse_down: Optional[EventType[[]]] = None, on_mouse_enter: Optional[EventType[[]]] = None, on_mouse_leave: Optional[EventType[[]]] = None, - on_mouse_move: Optional[EventType[[]]]= None, + on_mouse_move: Optional[EventType[[]]] = None, on_mouse_out: Optional[EventType[[]]] = None, - on_mouse_over: Optional[EventType[[]]]= None, + on_mouse_over: Optional[EventType[[]]] = None, on_mouse_up: Optional[EventType[[]]] = None, on_scroll: Optional[EventType[[]]] = None, on_unmount: Optional[EventType[[]]] = None, From d692a055b926505eb8ca688b2e74e187e9c644e8 Mon Sep 17 00:00:00 2001 From: Carlos Date: Thu, 17 Oct 2024 22:58:35 +0200 Subject: [PATCH 37/40] check if svg exists --- .../datadisplay/shiki_code_block.py | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 7c46d8f23dd..75a4f26c253 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -35,38 +35,44 @@ def copy_script(code: StringVar | str) -> Any: f""" // Event listener for the parent click document.addEventListener('click', function(event) {{ - const parent = event.target.closest('div'); // Assumes the parent is a
or any container + // Find the closest div (parent element) + const parent = event.target.closest('div'); + // If the parent is found if (parent) {{ - const svgIcon = parent.querySelector('svg'); // Always targets the child element - const originalPath = svgIcon.innerHTML; - const checkmarkPath = ''; // Checkmark SVG path - - function transition(element, scale, opacity) {{ - element.style.transform = `scale(${{scale}})`; - element.style.opacity = opacity; - }} - - // Copy the code to clipboard - navigator.clipboard.writeText(`{code}`).then(() => {{ - // Animate the SVG - transition(svgIcon, 0, '0'); - - setTimeout(() => {{ - svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark - svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary - transition(svgIcon, 1, '1'); - + // Find the SVG element within the parent + const svgIcon = parent.querySelector('svg'); + // If the SVG exists, proceed with the script + if (svgIcon) {{ + const originalPath = svgIcon.innerHTML; + const checkmarkPath = ''; // Checkmark SVG path + function transition(element, scale, opacity) {{ + element.style.transform = `scale(${{scale}})`; + element.style.opacity = opacity; + }} + // Copy the code to clipboard + navigator.clipboard.writeText(`{code}`).then(() => {{ + // Animate the SVG + transition(svgIcon, 0, '0'); setTimeout(() => {{ - transition(svgIcon, 0, '0'); + svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark + svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary + transition(svgIcon, 1, '1'); setTimeout(() => {{ - svgIcon.innerHTML = originalPath; // Restore original SVG content - transition(svgIcon, 1, '1'); - }}, 125); - }}, 600); - }}, 125); - }}).catch(err => {{ - console.error('Failed to copy text: ', err); - }}); + transition(svgIcon, 0, '0'); + setTimeout(() => {{ + svgIcon.innerHTML = originalPath; // Restore original SVG content + transition(svgIcon, 1, '1'); + }}, 125); + }}, 600); + }}, 125); + }}).catch(err => {{ + console.error('Failed to copy text: ', err); + }}); + }} else {{ + console.error('SVG element not found within the parent.'); + }} + }} else {{ + console.error('Parent element not found.'); }} }}); """ From 9f6c26637ac4965b87dd40277f8e466469de7b6e Mon Sep 17 00:00:00 2001 From: Carlos Date: Fri, 18 Oct 2024 12:06:39 +0200 Subject: [PATCH 38/40] copy event chain --- .../datadisplay/shiki_code_block.py | 44 +++++++++---------- .../datadisplay/shiki_code_block.pyi | 3 +- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/reflex/components/datadisplay/shiki_code_block.py b/reflex/components/datadisplay/shiki_code_block.py index 75a4f26c253..46199a6e4c6 100644 --- a/reflex/components/datadisplay/shiki_code_block.py +++ b/reflex/components/datadisplay/shiki_code_block.py @@ -13,7 +13,7 @@ from reflex.components.el.elements.forms import Button from reflex.components.lucide.icon import Icon from reflex.components.radix.themes.layout.box import Box -from reflex.event import call_script +from reflex.event import call_script, set_clipboard from reflex.style import Style from reflex.utils.exceptions import VarTypeError from reflex.utils.imports import ImportVar @@ -22,11 +22,9 @@ from reflex.vars.sequence import StringVar, string_replace_operation -def copy_script(code: StringVar | str) -> Any: +def copy_script() -> Any: """Copy script for the code block and modify the child SVG element. - Args: - code (str): The code to be copied. Returns: Any: The result of calling the script. @@ -49,30 +47,25 @@ def copy_script(code: StringVar | str) -> Any: element.style.transform = `scale(${{scale}})`; element.style.opacity = opacity; }} - // Copy the code to clipboard - navigator.clipboard.writeText(`{code}`).then(() => {{ - // Animate the SVG - transition(svgIcon, 0, '0'); + // Animate the SVG + transition(svgIcon, 0, '0'); + setTimeout(() => {{ + svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark + svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary + transition(svgIcon, 1, '1'); setTimeout(() => {{ - svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark - svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary - transition(svgIcon, 1, '1'); + transition(svgIcon, 0, '0'); setTimeout(() => {{ - transition(svgIcon, 0, '0'); - setTimeout(() => {{ - svgIcon.innerHTML = originalPath; // Restore original SVG content - transition(svgIcon, 1, '1'); - }}, 125); - }}, 600); - }}, 125); - }}).catch(err => {{ - console.error('Failed to copy text: ', err); - }}); + svgIcon.innerHTML = originalPath; // Restore original SVG content + transition(svgIcon, 1, '1'); + }}, 125); + }}, 600); + }}, 125); }} else {{ - console.error('SVG element not found within the parent.'); + // console.error('SVG element not found within the parent.'); }} }} else {{ - console.error('Parent element not found.'); + // console.error('Parent element not found.'); }} }}); """ @@ -736,7 +729,10 @@ def create( if copy_button is not None else Button.create( Icon.create(tag="copy", size=16, color=color("gray", 11)), - on_click=copy_script(cls._strip_transformer_triggers(code)), + on_click=[ + set_clipboard(cls._strip_transformer_triggers(code)), # type: ignore + copy_script(), + ], style=Style( { "position": "absolute", diff --git a/reflex/components/datadisplay/shiki_code_block.pyi b/reflex/components/datadisplay/shiki_code_block.pyi index a80c636ef57..bcf2719e925 100644 --- a/reflex/components/datadisplay/shiki_code_block.pyi +++ b/reflex/components/datadisplay/shiki_code_block.pyi @@ -11,9 +11,8 @@ from reflex.event import EventType from reflex.style import Style from reflex.vars.base import Var from reflex.vars.function import FunctionStringVar -from reflex.vars.sequence import StringVar -def copy_script(code: StringVar | str) -> Any: ... +def copy_script() -> Any: ... SHIKIJS_TRANSFORMER_FNS = { "transformerNotationDiff", From d2f3e6bb599db6fbcff1fa2e188e5717121f2416 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 18 Oct 2024 14:29:11 +0000 Subject: [PATCH 39/40] do a concatenation instead of first child --- reflex/components/markdown/markdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index 1b7d9f28531..fdf6ed3708f 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -206,7 +206,7 @@ def get_component(self, tag: str, **props) -> Component: if tag != "codeblock" else ternary_operation( ARRAY_ISARRAY.call(_CHILDREN), # type: ignore - _CHILDREN.to(list)[0], + _CHILDREN.to(list).join(""), _CHILDREN, ).to(str) ] From f3040ab1b6e7c864e56f3272a16dd7c6affb375b Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 21 Oct 2024 22:49:51 +0000 Subject: [PATCH 40/40] added comment --- reflex/components/markdown/markdown.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reflex/components/markdown/markdown.py b/reflex/components/markdown/markdown.py index fdf6ed3708f..b790bf7a121 100644 --- a/reflex/components/markdown/markdown.py +++ b/reflex/components/markdown/markdown.py @@ -204,9 +204,10 @@ def get_component(self, tag: str, **props) -> Component: children = [ _CHILDREN if tag != "codeblock" + # For codeblock, the mapping for some cases returns an array of elements. Let's join them into a string. else ternary_operation( ARRAY_ISARRAY.call(_CHILDREN), # type: ignore - _CHILDREN.to(list).join(""), + _CHILDREN.to(list).join("\n"), _CHILDREN, ).to(str) ]