Skip to content

Commit cd59ab5

Browse files
[ENG-4010]Codeblock cleanup in markdown (#4233)
* Codeblock cleanup in markdown * Initial approach to getting this working with rx.memo and reflex web * abstract the map var logic * the tests are not valid + pyright fix * darglint fix * Add unit tests plus mix components * pyi run * rebase on main * fix darglint * testing different OS * revert * This should fix it. Right? * Fix tests * minor fn signature fix * use ArgsFunctionOperation * use destructured args and pass the tests * fix remaining unit tests * fix pyi files * rebase on main * move language regex on codeblock to markdown * fix tests --------- Co-authored-by: Khaleel Al-Adhami <[email protected]>
1 parent 3d85936 commit cd59ab5

File tree

27 files changed

+565
-164
lines changed

27 files changed

+565
-164
lines changed

reflex/.templates/jinja/web/pages/custom_component.js.jinja2

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,6 @@
88
{% endfor %}
99

1010
export const {{component.name}} = memo(({ {{-component.props|join(", ")-}} }) => {
11-
{% if component.name == "CodeBlock" and "language" in component.props %}
12-
if (language) {
13-
(async () => {
14-
try {
15-
const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${language}`);
16-
SyntaxHighlighter.registerLanguage(language, module.default);
17-
} catch (error) {
18-
console.error(`Error importing language module for ${language}:`, error);
19-
}
20-
})();
21-
22-
23-
}
24-
{% endif %}
2511
{% for hook in component.hooks %}
2612
{{ hook }}
2713
{% endfor %}

reflex/components/datadisplay/code.py

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
from reflex.components.component import Component, ComponentNamespace
99
from reflex.components.core.cond import color_mode_cond
1010
from reflex.components.lucide.icon import Icon
11+
from reflex.components.markdown.markdown import _LANGUAGE, MarkdownComponentMap
1112
from reflex.components.radix.themes.components.button import Button
1213
from reflex.components.radix.themes.layout.box import Box
1314
from reflex.constants.colors import Color
1415
from reflex.event import set_clipboard
1516
from reflex.style import Style
1617
from reflex.utils import console, format
17-
from reflex.utils.imports import ImportDict, ImportVar
18+
from reflex.utils.imports import ImportVar
1819
from reflex.vars.base import LiteralVar, Var, VarData
1920

2021
LiteralCodeLanguage = Literal[
@@ -378,7 +379,7 @@ class Theme:
378379
setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme))
379380

380381

381-
class CodeBlock(Component):
382+
class CodeBlock(Component, MarkdownComponentMap):
382383
"""A code block."""
383384

384385
library = "[email protected]"
@@ -417,39 +418,6 @@ class CodeBlock(Component):
417418
# A custom copy button to override the default one.
418419
copy_button: Optional[Union[bool, Component]] = None
419420

420-
def add_imports(self) -> ImportDict:
421-
"""Add imports for the CodeBlock component.
422-
423-
Returns:
424-
The import dict.
425-
"""
426-
imports_: ImportDict = {}
427-
428-
if (
429-
self.language is not None
430-
and (language_without_quotes := str(self.language).replace('"', ""))
431-
in LiteralCodeLanguage.__args__ # type: ignore
432-
):
433-
imports_[
434-
f"react-syntax-highlighter/dist/cjs/languages/prism/{language_without_quotes}"
435-
] = [
436-
ImportVar(
437-
tag=format.to_camel_case(language_without_quotes),
438-
is_default=True,
439-
install=False,
440-
)
441-
]
442-
443-
return imports_
444-
445-
def _get_custom_code(self) -> Optional[str]:
446-
if (
447-
self.language is not None
448-
and (language_without_quotes := str(self.language).replace('"', ""))
449-
in LiteralCodeLanguage.__args__ # type: ignore
450-
):
451-
return f"{self.alias}.registerLanguage('{language_without_quotes}', {format.to_camel_case(language_without_quotes)})"
452-
453421
@classmethod
454422
def create(
455423
cls,
@@ -534,15 +502,55 @@ def _render(self):
534502

535503
theme = self.theme
536504

537-
out.add_props(style=theme).remove_props("theme", "code").add_props(
538-
children=self.code
505+
out.add_props(style=theme).remove_props("theme", "code", "language").add_props(
506+
children=self.code, language=_LANGUAGE
539507
)
540508

541509
return out
542510

543511
def _exclude_props(self) -> list[str]:
544512
return ["can_copy", "copy_button"]
545513

514+
@classmethod
515+
def _get_language_registration_hook(cls) -> str:
516+
"""Get the hook to register the language.
517+
518+
Returns:
519+
The hook to register the language.
520+
"""
521+
return f"""
522+
if ({str(_LANGUAGE)}) {{
523+
(async () => {{
524+
try {{
525+
const module = await import(`react-syntax-highlighter/dist/cjs/languages/prism/${{{str(_LANGUAGE)}}}`);
526+
SyntaxHighlighter.registerLanguage({str(_LANGUAGE)}, module.default);
527+
}} catch (error) {{
528+
console.error(`Error importing language module for ${{{str(_LANGUAGE)}}}:`, error);
529+
}}
530+
}})();
531+
}}
532+
"""
533+
534+
@classmethod
535+
def get_component_map_custom_code(cls) -> str:
536+
"""Get the custom code for the component.
537+
538+
Returns:
539+
The custom code for the component.
540+
"""
541+
return cls._get_language_registration_hook()
542+
543+
def add_hooks(self) -> list[str | Var]:
544+
"""Add hooks for the component.
545+
546+
Returns:
547+
The hooks for the component.
548+
"""
549+
return [
550+
f"const {str(_LANGUAGE)} = {str(self.language)}",
551+
self._get_language_registration_hook(),
552+
]
553+
546554

547555
class CodeblockNamespace(ComponentNamespace):
548556
"""Namespace for the CodeBlock component."""

reflex/components/datadisplay/code.pyi

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import dataclasses
77
from typing import Any, ClassVar, Dict, Literal, Optional, Union, overload
88

99
from reflex.components.component import Component, ComponentNamespace
10+
from reflex.components.markdown.markdown import MarkdownComponentMap
1011
from reflex.constants.colors import Color
1112
from reflex.event import BASE_STATE, EventType
1213
from reflex.style import Style
13-
from reflex.utils.imports import ImportDict
1414
from reflex.vars.base import Var
1515

1616
LiteralCodeLanguage = Literal[
@@ -349,8 +349,7 @@ for theme_name in dir(Theme):
349349
continue
350350
setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme))
351351

352-
class CodeBlock(Component):
353-
def add_imports(self) -> ImportDict: ...
352+
class CodeBlock(Component, MarkdownComponentMap):
354353
@overload
355354
@classmethod
356355
def create( # type: ignore
@@ -984,6 +983,9 @@ class CodeBlock(Component):
984983
...
985984

986985
def add_style(self): ...
986+
@classmethod
987+
def get_component_map_custom_code(cls) -> str: ...
988+
def add_hooks(self) -> list[str | Var]: ...
987989

988990
class CodeblockNamespace(ComponentNamespace):
989991
themes = Theme

reflex/components/datadisplay/shiki_code_block.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from reflex.components.core.cond import color_mode_cond
1313
from reflex.components.el.elements.forms import Button
1414
from reflex.components.lucide.icon import Icon
15+
from reflex.components.markdown.markdown import MarkdownComponentMap
1516
from reflex.components.props import NoExtrasAllowedProps
1617
from reflex.components.radix.themes.layout.box import Box
1718
from reflex.event import run_script, set_clipboard
@@ -528,7 +529,7 @@ def __init__(self, **kwargs):
528529
super().__init__(**kwargs)
529530

530531

531-
class ShikiCodeBlock(Component):
532+
class ShikiCodeBlock(Component, MarkdownComponentMap):
532533
"""A Code block."""
533534

534535
library = "/components/shiki/code"

reflex/components/datadisplay/shiki_code_block.pyi

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ from typing import Any, Dict, Literal, Optional, Union, overload
77

88
from reflex.base import Base
99
from reflex.components.component import Component, ComponentNamespace
10+
from reflex.components.markdown.markdown import MarkdownComponentMap
1011
from reflex.components.props import NoExtrasAllowedProps
1112
from reflex.event import BASE_STATE, EventType
1213
from reflex.style import Style
@@ -350,7 +351,7 @@ class ShikiJsTransformer(ShikiBaseTransformers):
350351
fns: list[FunctionStringVar]
351352
style: Optional[Style]
352353

353-
class ShikiCodeBlock(Component):
354+
class ShikiCodeBlock(Component, MarkdownComponentMap):
354355
@overload
355356
@classmethod
356357
def create( # type: ignore

0 commit comments

Comments
 (0)