reflex-components-code 0.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,866 @@
1
+ """Shiki syntax hghlighter component."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import dataclasses
6
+ import re
7
+ from collections import defaultdict
8
+ from dataclasses import dataclass
9
+ from typing import Any, Literal
10
+
11
+ from reflex_base.components.component import Component, ComponentNamespace, field
12
+ from reflex_base.components.props import NoExtrasAllowedProps
13
+ from reflex_base.event import run_script, set_clipboard
14
+ from reflex_base.style import Style
15
+ from reflex_base.utils.exceptions import VarTypeError
16
+ from reflex_base.utils.imports import ImportVar
17
+ from reflex_base.vars.base import LiteralVar, Var
18
+ from reflex_base.vars.function import FunctionStringVar
19
+ from reflex_base.vars.sequence import StringVar, string_replace_operation
20
+ from reflex_components_core.core.colors import color
21
+ from reflex_components_core.core.cond import color_mode_cond
22
+ from reflex_components_core.core.markdown_component_map import MarkdownComponentMap
23
+ from reflex_components_core.el.elements.forms import Button
24
+ from reflex_components_radix.themes.layout.box import Box
25
+
26
+
27
+ def copy_script() -> Any:
28
+ """Copy script for the code block and modify the child SVG element.
29
+
30
+ Returns:
31
+ Any: The result of calling the script.
32
+ """
33
+ return run_script(
34
+ """
35
+ // Event listener for the parent click
36
+ document.addEventListener('click', function(event) {
37
+ // Find the closest button (parent element)
38
+ const parent = event.target.closest('button');
39
+ // If the parent is found
40
+ if (parent) {
41
+ // Find the SVG element within the parent
42
+ const svgIcon = parent.querySelector('svg');
43
+ // If the SVG exists, proceed with the script
44
+ if (svgIcon) {
45
+ const originalPath = svgIcon.innerHTML;
46
+ const checkmarkPath = '<polyline points="20 6 9 17 4 12"></polyline>'; // Checkmark SVG path
47
+ function transition(element, scale, opacity) {
48
+ element.style.transform = `scale(${scale})`;
49
+ element.style.opacity = opacity;
50
+ }
51
+ // Animate the SVG
52
+ transition(svgIcon, 0, '0');
53
+ setTimeout(() => {
54
+ svgIcon.innerHTML = checkmarkPath; // Replace content with checkmark
55
+ svgIcon.setAttribute('viewBox', '0 0 24 24'); // Adjust viewBox if necessary
56
+ transition(svgIcon, 1, '1');
57
+ setTimeout(() => {
58
+ transition(svgIcon, 0, '0');
59
+ setTimeout(() => {
60
+ svgIcon.innerHTML = originalPath; // Restore original SVG content
61
+ transition(svgIcon, 1, '1');
62
+ }, 125);
63
+ }, 600);
64
+ }, 125);
65
+ } else {
66
+ // console.error('SVG element not found within the parent.');
67
+ }
68
+ } else {
69
+ // console.error('Parent element not found.');
70
+ }
71
+ })
72
+ """
73
+ )
74
+
75
+
76
+ SHIKIJS_TRANSFORMER_FNS = {
77
+ "transformerNotationDiff",
78
+ "transformerNotationHighlight",
79
+ "transformerNotationWordHighlight",
80
+ "transformerNotationFocus",
81
+ "transformerNotationErrorLevel",
82
+ "transformerRenderWhitespace",
83
+ "transformerMetaHighlight",
84
+ "transformerMetaWordHighlight",
85
+ "transformerCompactLineOptions",
86
+ # TODO: this transformer when included adds a weird behavior which removes other code lines. Need to figure out why.
87
+ # "transformerRemoveLineBreak",
88
+ "transformerRemoveNotationEscape",
89
+ }
90
+ LINE_NUMBER_STYLING = {
91
+ "code": {
92
+ "counter-reset": "step",
93
+ "counter-increment": "step 0",
94
+ "display": "grid",
95
+ "line-height": "1.7",
96
+ "font-size": "0.875em",
97
+ },
98
+ "code .line::before": {
99
+ "content": "counter(step)",
100
+ "counter-increment": "step",
101
+ "width": "1rem",
102
+ "margin-right": "1.5rem",
103
+ "display": "inline-block",
104
+ "text-align": "right",
105
+ "color": "rgba(115,138,148,.4)",
106
+ },
107
+ }
108
+ BOX_PARENT_STYLING = {
109
+ "pre": {
110
+ "margin": "0",
111
+ "padding": "24px",
112
+ "background": "transparent",
113
+ "overflow-x": "auto",
114
+ "border-radius": "6px",
115
+ },
116
+ }
117
+
118
+ THEME_MAPPING = {
119
+ "light": "one-light",
120
+ "dark": "one-dark-pro",
121
+ "a11y-dark": "github-dark",
122
+ }
123
+ LANGUAGE_MAPPING = {"bash": "shellscript"}
124
+ LiteralCodeLanguage = Literal[
125
+ "abap",
126
+ "actionscript-3",
127
+ "ada",
128
+ "angular-html",
129
+ "angular-ts",
130
+ "apache",
131
+ "apex",
132
+ "apl",
133
+ "applescript",
134
+ "ara",
135
+ "asciidoc",
136
+ "asm",
137
+ "astro",
138
+ "awk",
139
+ "ballerina",
140
+ "bat",
141
+ "beancount",
142
+ "berry",
143
+ "bibtex",
144
+ "bicep",
145
+ "blade",
146
+ "c",
147
+ "cadence",
148
+ "clarity",
149
+ "clojure",
150
+ "cmake",
151
+ "cobol",
152
+ "codeowners",
153
+ "codeql",
154
+ "coffee",
155
+ "common-lisp",
156
+ "coq",
157
+ "cpp",
158
+ "crystal",
159
+ "csharp",
160
+ "css",
161
+ "csv",
162
+ "cue",
163
+ "cypher",
164
+ "d",
165
+ "dart",
166
+ "dax",
167
+ "desktop",
168
+ "diff",
169
+ "docker",
170
+ "dotenv",
171
+ "dream-maker",
172
+ "edge",
173
+ "elixir",
174
+ "elm",
175
+ "emacs-lisp",
176
+ "erb",
177
+ "erlang",
178
+ "fennel",
179
+ "fish",
180
+ "fluent",
181
+ "fortran-fixed-form",
182
+ "fortran-free-form",
183
+ "fsharp",
184
+ "gdresource",
185
+ "gdscript",
186
+ "gdshader",
187
+ "genie",
188
+ "gherkin",
189
+ "git-commit",
190
+ "git-rebase",
191
+ "gleam",
192
+ "glimmer-js",
193
+ "glimmer-ts",
194
+ "glsl",
195
+ "gnuplot",
196
+ "go",
197
+ "graphql",
198
+ "groovy",
199
+ "hack",
200
+ "haml",
201
+ "handlebars",
202
+ "haskell",
203
+ "haxe",
204
+ "hcl",
205
+ "hjson",
206
+ "hlsl",
207
+ "html",
208
+ "html-derivative",
209
+ "http",
210
+ "hxml",
211
+ "hy",
212
+ "imba",
213
+ "ini",
214
+ "java",
215
+ "javascript",
216
+ "jinja",
217
+ "jison",
218
+ "json",
219
+ "json5",
220
+ "jsonc",
221
+ "jsonl",
222
+ "jsonnet",
223
+ "jssm",
224
+ "jsx",
225
+ "julia",
226
+ "kotlin",
227
+ "kusto",
228
+ "latex",
229
+ "lean",
230
+ "less",
231
+ "liquid",
232
+ "log",
233
+ "logo",
234
+ "lua",
235
+ "luau",
236
+ "make",
237
+ "markdown",
238
+ "marko",
239
+ "matlab",
240
+ "mdc",
241
+ "mdx",
242
+ "mermaid",
243
+ "mojo",
244
+ "move",
245
+ "narrat",
246
+ "nextflow",
247
+ "nginx",
248
+ "nim",
249
+ "nix",
250
+ "nushell",
251
+ "objective-c",
252
+ "objective-cpp",
253
+ "ocaml",
254
+ "pascal",
255
+ "perl",
256
+ "php",
257
+ "plain",
258
+ "plsql",
259
+ "po",
260
+ "postcss",
261
+ "powerquery",
262
+ "powershell",
263
+ "prisma",
264
+ "prolog",
265
+ "proto",
266
+ "pug",
267
+ "puppet",
268
+ "purescript",
269
+ "python",
270
+ "qml",
271
+ "qmldir",
272
+ "qss",
273
+ "r",
274
+ "racket",
275
+ "raku",
276
+ "razor",
277
+ "reg",
278
+ "regexp",
279
+ "rel",
280
+ "riscv",
281
+ "rst",
282
+ "ruby",
283
+ "rust",
284
+ "sas",
285
+ "sass",
286
+ "scala",
287
+ "scheme",
288
+ "scss",
289
+ "shaderlab",
290
+ "shellscript",
291
+ "shellsession",
292
+ "smalltalk",
293
+ "solidity",
294
+ "soy",
295
+ "sparql",
296
+ "splunk",
297
+ "sql",
298
+ "ssh-config",
299
+ "stata",
300
+ "stylus",
301
+ "svelte",
302
+ "swift",
303
+ "system-verilog",
304
+ "systemd",
305
+ "tasl",
306
+ "tcl",
307
+ "templ",
308
+ "terraform",
309
+ "tex",
310
+ "toml",
311
+ "ts-tags",
312
+ "tsv",
313
+ "tsx",
314
+ "turtle",
315
+ "twig",
316
+ "typescript",
317
+ "typespec",
318
+ "typst",
319
+ "v",
320
+ "vala",
321
+ "vb",
322
+ "verilog",
323
+ "vhdl",
324
+ "viml",
325
+ "vue",
326
+ "vue-html",
327
+ "vyper",
328
+ "wasm",
329
+ "wenyan",
330
+ "wgsl",
331
+ "wikitext",
332
+ "wolfram",
333
+ "xml",
334
+ "xsl",
335
+ "yaml",
336
+ "zenscript",
337
+ "zig",
338
+ ]
339
+ LiteralCodeTheme = Literal[
340
+ "andromeeda",
341
+ "aurora-x",
342
+ "ayu-dark",
343
+ "catppuccin-frappe",
344
+ "catppuccin-latte",
345
+ "catppuccin-macchiato",
346
+ "catppuccin-mocha",
347
+ "dark-plus",
348
+ "dracula",
349
+ "dracula-soft",
350
+ "everforest-dark",
351
+ "everforest-light",
352
+ "github-dark",
353
+ "github-dark-default",
354
+ "github-dark-dimmed",
355
+ "github-dark-high-contrast",
356
+ "github-light",
357
+ "github-light-default",
358
+ "github-light-high-contrast",
359
+ "houston",
360
+ "laserwave",
361
+ "light-plus",
362
+ "material-theme",
363
+ "material-theme-darker",
364
+ "material-theme-lighter",
365
+ "material-theme-ocean",
366
+ "material-theme-palenight",
367
+ "min-dark",
368
+ "min-light",
369
+ "monokai",
370
+ "night-owl",
371
+ "nord",
372
+ "one-dark-pro",
373
+ "one-light",
374
+ "plastic",
375
+ "poimandres",
376
+ "red",
377
+ # rose-pine themes dont work with the current version of shikijs transformers
378
+ # https://github.com/shikijs/shiki/issues/730
379
+ "rose-pine",
380
+ "rose-pine-dawn",
381
+ "rose-pine-moon",
382
+ "slack-dark",
383
+ "slack-ochin",
384
+ "snazzy-light",
385
+ "solarized-dark",
386
+ "solarized-light",
387
+ "synthwave-84",
388
+ "tokyo-night",
389
+ "vesper",
390
+ "vitesse-black",
391
+ "vitesse-dark",
392
+ "vitesse-light",
393
+ ]
394
+
395
+
396
+ class Position(NoExtrasAllowedProps):
397
+ """Position of the decoration."""
398
+
399
+ line: int
400
+ character: int
401
+
402
+
403
+ class ShikiDecorations(NoExtrasAllowedProps):
404
+ """Decorations for the code block."""
405
+
406
+ start: int | Position
407
+ end: int | Position
408
+ tag_name: str = "span"
409
+ properties: dict[str, Any] = {}
410
+ always_wrap: bool = False
411
+
412
+
413
+ @dataclass(kw_only=True)
414
+ class ShikiBaseTransformers:
415
+ """Base for creating transformers."""
416
+
417
+ library: str = ""
418
+ fns: list[FunctionStringVar] = dataclasses.field(default_factory=list)
419
+ style: Style | None = dataclasses.field(default=None)
420
+
421
+
422
+ @dataclass(kw_only=True)
423
+ class ShikiJsTransformer(ShikiBaseTransformers):
424
+ """A Wrapped shikijs transformer."""
425
+
426
+ library: str = "@shikijs/transformers@3.3.0"
427
+ fns: list[FunctionStringVar] = dataclasses.field(
428
+ default_factory=lambda: [
429
+ FunctionStringVar.create(fn) for fn in SHIKIJS_TRANSFORMER_FNS
430
+ ]
431
+ )
432
+ style: Style | None = dataclasses.field(
433
+ default_factory=lambda: Style({
434
+ "code": {
435
+ "line-height": "1.7",
436
+ "font-size": "0.875em",
437
+ "display": "grid",
438
+ },
439
+ # Diffs
440
+ ".diff": {
441
+ "margin": "0 -24px",
442
+ "padding": "0 24px",
443
+ "width": "calc(100% + 48px)",
444
+ "display": "inline-block",
445
+ },
446
+ ".diff.add": {
447
+ "background-color": "rgba(16, 185, 129, .14)",
448
+ "position": "relative",
449
+ },
450
+ ".diff.remove": {
451
+ "background-color": "rgba(244, 63, 94, .14)",
452
+ "opacity": "0.7",
453
+ "position": "relative",
454
+ },
455
+ ".diff.remove:after": {
456
+ "position": "absolute",
457
+ "left": "10px",
458
+ "content": "'-'",
459
+ "color": "#b34e52",
460
+ },
461
+ ".diff.add:after": {
462
+ "position": "absolute",
463
+ "left": "10px",
464
+ "content": "'+'",
465
+ "color": "#18794e",
466
+ },
467
+ # Highlight
468
+ ".highlighted": {
469
+ "background-color": "rgba(142, 150, 170, .14)",
470
+ "margin": "0 -24px",
471
+ "padding": "0 24px",
472
+ "width": "calc(100% + 48px)",
473
+ "display": "inline-block",
474
+ },
475
+ ".highlighted.error": {
476
+ "background-color": "rgba(244, 63, 94, .14)",
477
+ },
478
+ ".highlighted.warning": {
479
+ "background-color": "rgba(234, 179, 8, .14)",
480
+ },
481
+ # Highlighted Word
482
+ ".highlighted-word": {
483
+ "background-color": color("gray", 2),
484
+ "border": f"1px solid {color('gray', 5)}",
485
+ "padding": "1px 3px",
486
+ "margin": "-1px -3px",
487
+ "border-radius": "4px",
488
+ },
489
+ # Focused Lines
490
+ ".has-focused .line:not(.focused)": {
491
+ "opacity": "0.7",
492
+ "filter": "blur(0.095rem)",
493
+ "transition": "filter .35s, opacity .35s",
494
+ },
495
+ ".has-focused:hover .line:not(.focused)": {
496
+ "opacity": "1",
497
+ "filter": "none",
498
+ },
499
+ # White Space
500
+ # ".tab, .space": {
501
+ # "position": "relative", # noqa: ERA001
502
+ # },
503
+ # ".tab::before": {
504
+ # "content": "'⇥'", # noqa: ERA001
505
+ # "position": "absolute", # noqa: ERA001
506
+ # "opacity": "0.3",# noqa: ERA001
507
+ # },
508
+ # ".space::before": {
509
+ # "content": "'·'", # noqa: ERA001
510
+ # "position": "absolute", # noqa: ERA001
511
+ # "opacity": "0.3", # noqa: ERA001
512
+ # },
513
+ })
514
+ )
515
+
516
+ def __init__(self, **kwargs):
517
+ """Initialize the transformer.
518
+
519
+ Args:
520
+ kwargs: Kwargs to initialize the props.
521
+
522
+ """
523
+ fns = kwargs.pop("fns", None)
524
+ style = kwargs.pop("style", None)
525
+ if fns:
526
+ kwargs["fns"] = [
527
+ (
528
+ FunctionStringVar.create(x)
529
+ if not isinstance(x, FunctionStringVar)
530
+ else x
531
+ )
532
+ for x in fns
533
+ ]
534
+
535
+ if style:
536
+ kwargs["style"] = Style(style)
537
+ super().__init__(**kwargs)
538
+
539
+
540
+ class ShikiCodeBlock(Component, MarkdownComponentMap):
541
+ """A Code block."""
542
+
543
+ library = "/components/shiki/code"
544
+
545
+ tag = "Code"
546
+
547
+ alias = "ShikiCode"
548
+
549
+ lib_dependencies: list[str] = ["shiki@3.3.0"]
550
+
551
+ language: Var[LiteralCodeLanguage] = field(
552
+ default=Var.create("python"), doc="The language to use."
553
+ )
554
+
555
+ theme: Var[LiteralCodeTheme] = field(
556
+ default=Var.create("one-light"), doc='The theme to use ("light" or "dark").'
557
+ )
558
+
559
+ themes: Var[list[dict[str, Any]] | dict[str, str]] = field(
560
+ doc="The set of themes to use for different modes."
561
+ )
562
+
563
+ code: Var[str] = field(doc="The code to display.")
564
+
565
+ transformers: Var[list[ShikiBaseTransformers | dict[str, Any]]] = field(
566
+ default=Var.create([]),
567
+ doc="The transformers to use for the syntax highlighter.",
568
+ )
569
+
570
+ decorations: Var[list[ShikiDecorations]] = field(
571
+ default=Var.create([]), doc="The decorations to use for the syntax highlighter."
572
+ )
573
+
574
+ @classmethod
575
+ def create(
576
+ cls,
577
+ *children,
578
+ **props,
579
+ ) -> Component:
580
+ """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/).
581
+
582
+ Args:
583
+ *children: The children of the component.
584
+ **props: The props to pass to the component.
585
+
586
+ Returns:
587
+ The code block component.
588
+ """
589
+ # Separate props for the code block and the wrapper
590
+ code_block_props = {}
591
+ code_wrapper_props = {}
592
+ decorations = props.pop("decorations", [])
593
+
594
+ class_props = cls.get_props()
595
+
596
+ # Distribute props between the code block and wrapper
597
+ for key, value in props.items():
598
+ (code_block_props if key in class_props else code_wrapper_props)[key] = (
599
+ value
600
+ )
601
+
602
+ # cast decorations into ShikiDecorations.
603
+ decorations = [
604
+ ShikiDecorations(**decoration)
605
+ if not isinstance(decoration, ShikiDecorations)
606
+ else decoration
607
+ for decoration in decorations
608
+ ]
609
+ code_block_props["decorations"] = decorations
610
+
611
+ code_block_props["code"] = children[0]
612
+ code_block = super().create(**code_block_props)
613
+
614
+ transformer_styles = {}
615
+ # Collect styles from transformers and wrapper
616
+ for transformer in code_block.transformers._var_value: # pyright: ignore [reportAttributeAccessIssue]
617
+ if isinstance(transformer, ShikiBaseTransformers) and transformer.style:
618
+ transformer_styles.update(transformer.style)
619
+ transformer_styles.update(code_wrapper_props.pop("style", {}))
620
+
621
+ return Box.create(
622
+ code_block,
623
+ *children[1:],
624
+ style=Style({**transformer_styles, **BOX_PARENT_STYLING}),
625
+ **code_wrapper_props,
626
+ )
627
+
628
+ def add_imports(self) -> dict[str, list[str]]:
629
+ """Add the necessary imports.
630
+ We add all referenced transformer functions as imports from their corresponding
631
+ libraries.
632
+
633
+ Returns:
634
+ Imports for the component.
635
+
636
+ Raises:
637
+ ValueError: If the transformers are not of type LiteralVar.
638
+ """
639
+ imports = defaultdict(list)
640
+ if not isinstance(self.transformers, LiteralVar):
641
+ msg = f"transformers should be a LiteralVar type. Got {type(self.transformers)} instead."
642
+ raise ValueError(msg)
643
+ for transformer in self.transformers._var_value:
644
+ if isinstance(transformer, ShikiBaseTransformers):
645
+ imports[transformer.library].extend([
646
+ ImportVar(tag=str(fn)) for fn in transformer.fns
647
+ ])
648
+ if transformer.library not in self.lib_dependencies:
649
+ self.lib_dependencies.append(transformer.library)
650
+ return imports
651
+
652
+ @classmethod
653
+ def create_transformer(cls, library: str, fns: list[str]) -> ShikiBaseTransformers:
654
+ """Create a transformer from a third party library.
655
+
656
+ Args:
657
+ library: The name of the library.
658
+ fns: The str names of the functions/callables to invoke from the library.
659
+
660
+ Returns:
661
+ A transformer for the specified library.
662
+
663
+ Raises:
664
+ ValueError: If a supplied function name is not valid str.
665
+ """
666
+ if any(not isinstance(fn_name, str) for fn_name in fns):
667
+ msg = f"the function names should be str names of functions in the specified transformer: {library!r}"
668
+ raise ValueError(msg)
669
+ return ShikiBaseTransformers(
670
+ library=library,
671
+ fns=[FunctionStringVar.create(fn) for fn in fns], # pyright: ignore [reportCallIssue]
672
+ )
673
+
674
+ def _render(self, props: dict[str, Any] | None = None):
675
+ """Renders the component with the given properties, processing transformers if present.
676
+
677
+ Args:
678
+ props: Optional properties to pass to the render function.
679
+
680
+ Returns:
681
+ Rendered component output.
682
+ """
683
+ # Ensure props is initialized from class attributes if not provided
684
+ props = props or {
685
+ attr.rstrip("_"): getattr(self, attr) for attr in self.get_props()
686
+ }
687
+
688
+ # Extract transformers and apply transformations
689
+ transformers = props.get("transformers")
690
+ if transformers is not None:
691
+ transformed_values = self._process_transformers(transformers._var_value)
692
+ props["transformers"] = LiteralVar.create(transformed_values)
693
+
694
+ return super()._render(props)
695
+
696
+ def _process_transformers(self, transformer_list: list) -> list:
697
+ """Processes a list of transformers, applying transformations where necessary.
698
+
699
+ Args:
700
+ transformer_list: List of transformer objects or values.
701
+
702
+ Returns:
703
+ list: A list of transformed values.
704
+ """
705
+ processed = []
706
+
707
+ for transformer in transformer_list:
708
+ if isinstance(transformer, ShikiBaseTransformers):
709
+ processed.extend(fn.call() for fn in transformer.fns)
710
+ else:
711
+ processed.append(transformer)
712
+
713
+ return processed
714
+
715
+
716
+ class ShikiHighLevelCodeBlock(ShikiCodeBlock):
717
+ """High level component for the shiki syntax highlighter."""
718
+
719
+ use_transformers: Var[bool] = field(
720
+ doc="If this is enabled, the default transformers(shikijs transformer) will be used."
721
+ )
722
+
723
+ show_line_numbers: Var[bool] = field(
724
+ doc="If this is enabled line numbers will be shown next to the code block."
725
+ )
726
+
727
+ can_copy: bool = field(
728
+ doc="Whether a copy button should appear.",
729
+ default=False,
730
+ is_javascript_property=False,
731
+ )
732
+
733
+ # copy_button: A custom copy button to override the default one.
734
+ copy_button: Component | bool | None = field(
735
+ default=None, is_javascript_property=False
736
+ )
737
+
738
+ @classmethod
739
+ def create(
740
+ cls,
741
+ *children,
742
+ **props,
743
+ ) -> Component:
744
+ """Create a code block component using [shiki syntax highlighter](https://shiki.matsu.io/).
745
+
746
+ Args:
747
+ *children: The children of the component.
748
+ **props: The props to pass to the component.
749
+
750
+ Returns:
751
+ The code block component.
752
+ """
753
+ from reflex_components_lucide.icon import Icon
754
+
755
+ use_transformers = props.pop("use_transformers", False)
756
+ show_line_numbers = props.pop("show_line_numbers", False)
757
+ language = props.pop("language", None)
758
+ can_copy = props.pop("can_copy", False)
759
+ copy_button = props.pop("copy_button", None)
760
+
761
+ if use_transformers:
762
+ props["transformers"] = [ShikiJsTransformer()]
763
+
764
+ if language is not None:
765
+ props["language"] = cls._map_languages(language)
766
+
767
+ # line numbers are generated via css
768
+ if show_line_numbers:
769
+ props["style"] = {**LINE_NUMBER_STYLING, **props.get("style", {})}
770
+
771
+ theme = props.pop("theme", None)
772
+ props["theme"] = props["theme"] = (
773
+ cls._map_themes(theme)
774
+ if theme is not None
775
+ else color_mode_cond( # Default color scheme responds to global color mode.
776
+ light="one-light",
777
+ dark="one-dark-pro",
778
+ )
779
+ )
780
+
781
+ if can_copy:
782
+ code = children[0]
783
+ copy_button = (
784
+ copy_button
785
+ if copy_button is not None
786
+ else Button.create(
787
+ Icon.create(tag="copy", size=16, color=color("gray", 11)),
788
+ on_click=[
789
+ set_clipboard(cls._strip_transformer_triggers(code)),
790
+ copy_script(),
791
+ ],
792
+ style=Style({
793
+ "position": "absolute",
794
+ "top": "4px",
795
+ "right": "4px",
796
+ "background": color("gray", 3),
797
+ "border": "1px solid",
798
+ "border-color": color("gray", 5),
799
+ "border-radius": "6px",
800
+ "padding": "5px",
801
+ "opacity": "1",
802
+ "cursor": "pointer",
803
+ "_hover": {
804
+ "background": color("gray", 4),
805
+ },
806
+ "transition": "background 0.250s ease-out",
807
+ "&>svg": {
808
+ "transition": "transform 0.250s ease-out, opacity 0.250s ease-out",
809
+ },
810
+ "_active": {
811
+ "background": color("gray", 5),
812
+ },
813
+ }),
814
+ )
815
+ )
816
+
817
+ if copy_button:
818
+ return ShikiCodeBlock.create(
819
+ children[0], copy_button, position="relative", **props
820
+ )
821
+ return ShikiCodeBlock.create(children[0], **props)
822
+
823
+ @staticmethod
824
+ def _map_themes(theme: str) -> str:
825
+ if isinstance(theme, str) and theme in THEME_MAPPING:
826
+ return THEME_MAPPING[theme]
827
+ return theme
828
+
829
+ @staticmethod
830
+ def _map_languages(language: str) -> str:
831
+ if isinstance(language, str) and language in LANGUAGE_MAPPING:
832
+ return LANGUAGE_MAPPING[language]
833
+ return language
834
+
835
+ @staticmethod
836
+ def _strip_transformer_triggers(code: str | StringVar) -> StringVar | str:
837
+ if not isinstance(code, (StringVar, str)):
838
+ msg = f"code should be string literal or a StringVar type. Got {type(code)} instead."
839
+ raise VarTypeError(msg)
840
+ regex_pattern = r"[\/#]+ *\[!code.*?\]"
841
+
842
+ if isinstance(code, Var):
843
+ return string_replace_operation(
844
+ code, StringVar(_js_expr=f"/{regex_pattern}/g", _var_type=str), ""
845
+ )
846
+ if isinstance(code, str):
847
+ return re.sub(regex_pattern, "", code)
848
+ return None
849
+
850
+
851
+ class TransformerNamespace(ComponentNamespace):
852
+ """Namespace for the Transformers."""
853
+
854
+ shikijs = ShikiJsTransformer
855
+
856
+
857
+ class CodeblockNamespace(ComponentNamespace):
858
+ """Namespace for the CodeBlock component."""
859
+
860
+ root = staticmethod(ShikiCodeBlock.create)
861
+ create_transformer = staticmethod(ShikiCodeBlock.create_transformer)
862
+ transformers = TransformerNamespace()
863
+ __call__ = staticmethod(ShikiHighLevelCodeBlock.create)
864
+
865
+
866
+ code_block = CodeblockNamespace()