├── jsxloader ├── __init__.py ├── backends │ ├── __init__.py │ └── jsx.py ├── nodes │ ├── __init__.py │ ├── jsx_script.py │ ├── file_component.py │ ├── inline_component.py │ ├── jsx_syntax.py │ └── jsx.py ├── config.py ├── templatetags │ └── jsx_loader.py └── utils.py ├── tests └── test_config.py ├── Readme.md ├── .gitignore └── ReactLoader.excalidraw /jsxloader/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /jsxloader/backends/__init__.py: -------------------------------------------------------------------------------- 1 | from .jsx import JSXLoaderEngine -------------------------------------------------------------------------------- /jsxloader/nodes/__init__.py: -------------------------------------------------------------------------------- 1 | from .jsx import JsxNode, JSXNode 2 | from .file_component import JSXFileComponentNode 3 | from .inline_component import JSXInlineComponentNode 4 | from .jsx_syntax import JSXSyntaxNode 5 | from .jsx_script import JSXScriptNode -------------------------------------------------------------------------------- /jsxloader/nodes/jsx_script.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.template import Context 3 | from ..utils import hash_string 4 | 5 | class JSXScriptNode(template.Node): 6 | def render(self, context: Context) -> str: 7 | self.template_id = self.get_template_id(context.template.origin.name) 8 | context["jsx_loader"] = {} 9 | context["jsx_loader"]["_counter"] = 0 10 | return "" 11 | 12 | def get_template_id(self, template_path): 13 | return hash_string(template_path)[:6] -------------------------------------------------------------------------------- /jsxloader/nodes/file_component.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.template import Context 3 | from .jsx import JSXNode 4 | 5 | class JSXFileComponentNode(JSXNode): 6 | def __init__(self, path, *args, **kwargs): 7 | super(JSXFileComponentNode, self).__init__(*args, **kwargs) 8 | self.path = path 9 | 10 | def render(self, context: Context) -> str: 11 | super(JSXFileComponentNode, self).render(context) 12 | print(f"From JSX File Component Node: {self.id}") 13 | return self.path 14 | -------------------------------------------------------------------------------- /jsxloader/nodes/inline_component.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.template import Context 3 | from .jsx import JSXNode 4 | 5 | 6 | class JSXInlineComponentNode(JSXNode): 7 | def __init__(self, nodelist, *args, **kwargs): 8 | super(JSXInlineComponentNode, self).__init__(*args, **kwargs) 9 | self.nodelist = nodelist 10 | 11 | def render(self, context: Context) -> str: 12 | super(JSXInlineComponentNode, self).render(context) 13 | print(f"From JSX Inline Component Node: {self.id}") 14 | return self.nodelist.render(context) 15 | -------------------------------------------------------------------------------- /jsxloader/config.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from django.conf import settings 3 | 4 | __all__ = ["Config", "load_config"] 5 | 6 | @dataclass(kw_only=True) 7 | class Config: 8 | base_dir: str = "jsx_modules" 9 | pre_bundle_dir: str = "prebundle" 10 | post_bundle_dir: str = "postbundle" 11 | config_dir: str = "config" 12 | 13 | def load_config(): 14 | # check if "JSX_LOADER_CONFIG" is set in django's settings 15 | if hasattr(settings, "JSX_LOADER_CONFIG"): 16 | config_dict = settings.JSX_LOADER_CONFIG 17 | return dict_to_config(config_dict) 18 | else: 19 | return Config() 20 | 21 | def dict_to_config(config_dict: dict) -> Config: 22 | default_config = Config() 23 | config_dict = {key: value for key, value in config_dict.items() if hasattr(default_config, key)} 24 | return Config(**config_dict) 25 | -------------------------------------------------------------------------------- /jsxloader/nodes/jsx_syntax.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.template import Context 3 | from .jsx import JSXNode 4 | from ..utils import clean_js_variable_name, hash_string 5 | 6 | class JSXSyntaxNode(JSXNode): 7 | 8 | def __init__(self, nodelist, *args, **kwargs): 9 | super(JSXSyntaxNode, self).__init__(*args, **kwargs) 10 | self.nodelist = nodelist 11 | 12 | def generate_component_id(self, template_name, jsx): 13 | pass 14 | 15 | 16 | def wrap_jsx_in_component(self) -> str: 17 | # component_name = clean_js_variable_name(hash_string()) 18 | component_name = "" 19 | 20 | return """ 21 | import React from 'react'; 22 | import ReactDOM from 'react-dom'; 23 | 24 | const """ + component_name + """ = () => { 25 | return ( 26 | """ + self.jsx + """ 27 | ); 28 | } 29 | """ 30 | 31 | def render(self, context: Context) -> str: 32 | super(JSXSyntaxNode, self).render(context) 33 | print(f"From JSX Syntax Node: {self.id}") 34 | 35 | self.jsx = self.nodelist.render(context) 36 | return self.wrap_jsx_in_component() 37 | -------------------------------------------------------------------------------- /jsxloader/backends/jsx.py: -------------------------------------------------------------------------------- 1 | from django.template.backends.django import DjangoTemplates, reraise 2 | from django.template.context import make_context 3 | from django.template import TemplateDoesNotExist 4 | 5 | 6 | class JSXLoaderEngine(DjangoTemplates): 7 | def from_string(self, template_code): 8 | return JSXSupportedTemplate(self.engine.from_string(template_code), self) 9 | 10 | def get_template(self, template_name): 11 | try: 12 | return JSXSupportedTemplate(self.engine.get_template(template_name), self) 13 | except TemplateDoesNotExist as exc: 14 | reraise(exc, self) 15 | 16 | 17 | class JSXSupportedTemplate: 18 | def __init__(self, template, backend): 19 | self.template = template 20 | self.backend = backend 21 | 22 | @property 23 | def origin(self): 24 | return self.template.origin 25 | 26 | def render(self, context=None, request=None): 27 | context = make_context( 28 | context, request, autoescape=self.backend.engine.autoescape 29 | ) 30 | print(context) 31 | try: 32 | result = self.template.render(context) 33 | # build jsx bundle 34 | return result 35 | except TemplateDoesNotExist as exc: 36 | reraise(exc, self.backend) -------------------------------------------------------------------------------- /jsxloader/templatetags/jsx_loader.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from ..nodes import JsxNode, JSXFileComponentNode, JSXInlineComponentNode, JSXSyntaxNode, JSXScriptNode 3 | 4 | 5 | register = template.Library() 6 | 7 | @register.tag(name="JSXScript") 8 | def do_jsx_script(parser, token): 9 | return JSXScriptNode() 10 | 11 | @register.tag(name="JSX") 12 | def do_jsx(parser, token): 13 | nodelist = parser.parse(("endJSX",)) 14 | parser.delete_first_token() 15 | return JSXSyntaxNode(nodelist) 16 | 17 | @register.tag(name='JSXComponentFile') 18 | def do_jsx_component_file(parser, token): 19 | try: 20 | tag_name, path = token.split_contents() 21 | except ValueError: 22 | raise template.TemplateSyntaxError(f"{token.contents} tag requires a Component path.") 23 | 24 | # Ensure the path is properly quoted 25 | if not (path[0] == path[-1] and path[0] in ('"', "'")): 26 | raise template.TemplateSyntaxError(f"Component path must be enclosed in quotes: {path}") 27 | 28 | # Strip the quotes from the path 29 | path = path[1:-1] 30 | 31 | return JSXFileComponentNode(path) 32 | 33 | @register.tag(name="JSXComponent") 34 | def do_jsx_component(parser, token): 35 | nodelist = parser.parse(("endJSXComponent",)) 36 | parser.delete_first_token() 37 | return JSXInlineComponentNode(nodelist) 38 | 39 | 40 | # TODO: Add option in tag declaration for naming the JSX Component instead of using random. 41 | -------------------------------------------------------------------------------- /jsxloader/utils.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import re 3 | 4 | 5 | def clean_js_variable_name(var_name: str) -> str: 6 | """ 7 | Cleans a JavaScript variable name by removing any non-alphanumeric characters and ensuring it starts with a letter or underscore. 8 | 9 | Args: 10 | var_name (str): The variable name to be cleaned. 11 | 12 | Returns: 13 | str: The cleaned variable name. 14 | """ 15 | while var_name[0].isdigit(): 16 | var_name = "_" + var_name 17 | 18 | var_name = re.sub("[^0-9a-zA-Z_]+", "", var_name) 19 | 20 | if len(var_name) < 1: 21 | var_name = "_" 22 | 23 | return var_name 24 | 25 | 26 | def hash_string(string: str) -> str: 27 | """ 28 | Hashes a given string using SHA256 algorithm and returns the hash. 29 | 30 | Args: 31 | string (str): The string to be hashed. 32 | 33 | Returns: 34 | str: The alphanumeric characters of the hash. 35 | """ 36 | hash_object = hashlib.sha256(string.encode()) 37 | hex_digest = hash_object.hexdigest() 38 | hex_digest = str(hex_digest) 39 | 40 | # Keep only alphanumeric characters in the output 41 | result = "".join(char for char in hex_digest if char.isalnum()) 42 | 43 | return result 44 | 45 | 46 | def template_name_to_dotted_path(template_name: str) -> str: 47 | """ 48 | Converts a template name to a dotted path. 49 | 50 | Args: 51 | template_name (str): The template name to convert. 52 | 53 | Returns: 54 | str: The converted dotted path. 55 | """ 56 | template_name = template_name.strip("/.\\") 57 | template_name = template_name.split(".")[0] 58 | return template_name.replace("/", ".") 59 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from django.conf import settings 3 | settings.configure() 4 | from jsxloader.config import load_config, Config 5 | 6 | class ConfigTestCase(unittest.TestCase): 7 | def test_load_config_with_config_set(self): 8 | # Set the "JSX_LOADER_CONFIG" in Django's settings 9 | settings.JSX_LOADER_CONFIG = { 10 | "base_dir": "test_base_dir_value", 11 | "config_dir": "test_config_dir_value", 12 | } 13 | 14 | # Call the load_config function 15 | result = load_config() 16 | 17 | # Assert that the returned result is a Config object 18 | self.assertIsInstance(result, Config) 19 | 20 | # Assert that the Config object has the correct values 21 | self.assertEqual(result.base_dir, "test_base_dir_value") 22 | self.assertEqual(result.config_dir, "test_config_dir_value") 23 | 24 | def test_load_config_without_config_set(self): 25 | # Remove the "JSX_LOADER_CONFIG" from Django's settings 26 | if hasattr(settings, "JSX_LOADER_CONFIG"): 27 | del settings.JSX_LOADER_CONFIG 28 | 29 | # Call the load_config function 30 | result = load_config() 31 | 32 | # Assert that the returned result is a Config object 33 | self.assertIsInstance(result, Config, "load_config() should return a Config object") 34 | 35 | # Assert that the Config object is the default Config 36 | self.assertEqual(result, Config(), "Config object should be the default Config") 37 | 38 | def test_load_config_with_invalid_keys(self): 39 | # Set the "JSX_LOADER_CONFIG" in Django's settings 40 | settings.JSX_LOADER_CONFIG = { 41 | "base_dir": "test_base_dir_value", 42 | "config_dir": "test_config_dir_value", 43 | "invalid_key": "invalid_value", 44 | } 45 | 46 | # Call the load_config function 47 | result = load_config() 48 | 49 | # Assert that the returned result is a Config object 50 | self.assertIsInstance(result, Config) 51 | 52 | # Assert that the Config object has the correct values 53 | self.assertEqual(result.base_dir, "test_base_dir_value") 54 | self.assertEqual(result.config_dir, "test_config_dir_value") 55 | self.assertFalse(hasattr(result, "invalid_key"), "Config object should not have invalid keys.") 56 | 57 | if __name__ == "__main__": 58 | unittest.main() -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Install : 2 | ```bash 3 | pip install django-jsx-loader 4 | ``` 5 | 6 | install npm dependencies 7 | 8 | ```bash 9 | npm install --save-dev babel-loader @babel/preset-env @babel/preset-react webpack-cli react react-dom 10 | ``` 11 | ## Setup : 12 | ```python 13 | # settings.py 14 | INSTALLED_APPS = [ 15 | ... 16 | 'django_jsx_loader', 17 | ... 18 | ] 19 | 20 | ... 21 | 22 | TEMPLATES = [ 23 | { 24 | 'BACKEND': 'jsxloader.backends.JSXLoaderEngine', 25 | ... 26 | }, 27 | ] 28 | 29 | ``` 30 | 31 | ## Configuration : 32 | 33 | You can change configuration inside django's `settings.py` 34 | 35 | ***All paths are relative to the base directory of the project(where manage.py is)* 36 | 37 | 38 | ```python 39 | # settings.py 40 | 41 | JSX_LOADER = { 42 | 'base_dir': 'frontend', 43 | 'output_dir': 'output', 44 | ... 45 | } 46 | ``` 47 | 48 | ### Configuration Options : 49 | 50 | 51 | Configration | Type | Description | Default 52 | --- | --- | --- | --- 53 | base_dir | String | The name of the directory housing all other directories related to the jsx loader. | jsx_modules 54 | output_dir | String | The name of the directory where the output javascript files will be placed. | static 55 | 56 | 57 | ## Usage : 58 | 59 | - ### Inline JSX : 60 | ```html 61 | 62 | {% load jsx_loader %} 63 | {% JSX %} 64 |
65 |

Hi, I'm a React App

66 |
67 | {% endJSX %} 68 | ``` 69 | 70 | - ### Jsx Component File : 71 | 72 | ```html 73 | 74 | {% load jsx_loader %} 75 | {% JSXComponentFile 'components/counter.jsx' %} 76 | ``` 77 | 78 | - ### Inline Jsx Component : 79 | 80 | ```html 81 | 82 | {% load jsx_loader %} 83 | {% JSXComponent %} 84 | import React, { useState } from 'react'; 85 | 86 | const Counter = () => { 87 | const [count, setCount] = useState() 88 | return ( 89 |
90 |

Hi, I'm a React Counter

91 |

{count}

92 | 93 |
94 | ); 95 | } 96 | 97 | export default Counter; 98 | {% endJSXComponent %} 99 | ``` 100 | 101 | ## Loading Steps : 102 | - Tag Content is copied to a temp file 103 | ``` 104 | 105 | const componentID = () => { 106 | return ( 107 | 108 |
109 |

Hi, I'm a React App

110 |
111 | ); 112 | } 113 | 114 | export default componentID; 115 | ``` 116 | - Component Rendering Script Is Made 117 | ``` 118 | import JSXComponent from 'temp.component.file' 119 | import AnotherComponent from 'another.temp.file' 120 | import reactDOM from 'react-dom' 121 | 122 | reactDOM.render(, document.getElementById('component-id')) 123 | reactDOM.render(, document.getElementById('another-component-id')) 124 | ``` 125 | 126 | - Tags are replaced with placeholder HTML elements. 127 | ``` 128 |
129 | ``` 130 | - Webpack Config file is made with Rendering Script as entry point 131 | - webpack command is run 132 | - output script file is added to the template 133 | ``` 134 | 135 | ``` 136 | 137 | #### *****All the steps should be done manually in production by running a command except for tags replacement.*** -------------------------------------------------------------------------------- /jsxloader/nodes/jsx.py: -------------------------------------------------------------------------------- 1 | from ..config import Config 2 | from ..utils import template_name_to_dotted_path 3 | import subprocess 4 | from django import template 5 | from django.conf import settings 6 | import os 7 | import random 8 | import string 9 | from pathlib import Path 10 | 11 | 12 | class JSXNode(template.Node): 13 | def increase_node_count(self): 14 | self.context["jsx_loader"]["_counter"] += 1 15 | 16 | def get_index(self): 17 | return self.context["jsx_loader"]["_counter"] 18 | 19 | def get_template_path(self): 20 | return self.context.template.origin.name 21 | 22 | def get_template_name(self): 23 | return self.context.template.name 24 | 25 | def get_component_id(self): 26 | return ".".join( 27 | [template_name_to_dotted_path(self.template_name), str(self.index)] 28 | ) 29 | 30 | def render(self, context) -> str: 31 | self.context = context 32 | self.template_path = self.get_template_path() 33 | self.template_name = self.get_template_name() 34 | self.increase_node_count() 35 | self.index = self.get_index() 36 | self.id = self.get_component_id() 37 | 38 | 39 | class JsxNode(template.Node): 40 | def __init__(self, nodelist): 41 | self.nodelist = nodelist 42 | self.config = Config() 43 | 44 | self.base_dir = self.config.base_dir 45 | self.pre_bundle_dir = self.get_dir(self.config.pre_bundle_dir) 46 | self.post_bundle_dir = self.get_dir(self.config.post_bundle_dir) 47 | self.config_dir = self.get_dir(self.config.config_dir) 48 | 49 | self.id = self.generate_random_id() 50 | self.pre_bundle_file = self.pre_bundle_dir.joinpath(self.id + ".jsx") 51 | self.post_bundle_file = self.post_bundle_dir.joinpath(self.id + ".js") 52 | # self.content = self.nodelist.render({}) 53 | 54 | def write_file(self, path, content): 55 | try: 56 | with open(path, "w") as file: 57 | file.write(content) 58 | return path 59 | except FileNotFoundError: 60 | print(f"Error writing '{path}' file: File not found.") 61 | except PermissionError: 62 | print(f"Error writing '{path}' file: Permission denied.") 63 | except Exception as e: 64 | print(f"Error writing '{path}' file: {str(e)}") 65 | return False 66 | 67 | def get_dir(self, name): 68 | project_path = Path(settings.BASE_DIR) 69 | base_path = project_path.joinpath(self.base_dir) 70 | path = base_path.joinpath(name) 71 | 72 | if not path.exists: 73 | os.makedirs(path, exist_ok=True) 74 | 75 | return path 76 | 77 | def generate_random_id(self): 78 | id = "".join(random.choices(string.ascii_uppercase + string.digits, k=6)) 79 | return id 80 | 81 | def generate_placeholder_element(self): 82 | return f'
' 83 | 84 | def bundle_jsx_file(self): 85 | output_file = self.post_bundle_dir.joinpath(self.id + ".js") 86 | 87 | # command = f"npx webpack --mode development --entry {self.pre_bundle_file} --output-path {self.post_bundle_dir} --output-filename {self.id}.js --module-bind js=babel-loader" 88 | command = f'npx --yes webpack --mode development --config "{self.config_file}"' 89 | print(command) 90 | result = subprocess.run(command, shell=True, capture_output=True, text=True) 91 | print(result.stdout) 92 | try: 93 | result.check_returncode() 94 | except subprocess.CalledProcessError: 95 | print(f"Error bundling the jsx file #{self.id}: \n{result.stderr}") 96 | 97 | return output_file 98 | 99 | def generate_config_file(self): 100 | config_file = self.pre_bundle_dir.joinpath(f"{self.id}.config.js") 101 | 102 | config_content = ( 103 | """ 104 | module.exports = { 105 | entry: '""" 106 | + self.pre_bundle_file.as_posix() 107 | + """', 108 | output: { 109 | path: '""" 110 | + self.post_bundle_dir.as_posix() 111 | + """', 112 | filename: '""" 113 | + self.id 114 | + ".js" 115 | + """', 116 | }, 117 | module: { 118 | rules: [ 119 | { 120 | test: /\.jsx?$/, 121 | exclude: /(node_modules)/, 122 | use: { 123 | loader: 'babel-loader', 124 | options: { 125 | presets: ['@babel/preset-env', '@babel/preset-react'] 126 | } 127 | } 128 | } 129 | ] 130 | } 131 | }; 132 | """ 133 | ) 134 | return self.write_file(config_file, config_content) 135 | 136 | def generate_jsx_render_js_file(self, target_id, jsx_content): 137 | render_js_file = self.post_bundle_dir.joinpath(f"{self.id}.js") 138 | 139 | render_js_content = f""" 140 | import React from 'react'; 141 | import ReactDOM from 'react-dom'; 142 | import Component from '{self.post_bundle_file}'; 143 | 144 | ReactDOM.render(, document.getElementById('{target_id}')); 145 | """ 146 | return self.write_file(render_js_file, render_js_content) 147 | 148 | def render(self, context): 149 | self.content = self.nodelist.render(context) 150 | if self.write_file(self.pre_bundle_file, self.content): 151 | self.config_file = self.generate_config_file() 152 | self.bundle_jsx_file() 153 | 154 | return self.generate_placeholder_element() 155 | return "" 156 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | project/ 2 | app/ 3 | manage.py 4 | db.sqlite3 5 | prebundle/ 6 | postbundle/ 7 | config/ 8 | jsx_modules/ 9 | package.json 10 | package-lock.json 11 | 12 | # Byte-compiled / optimized / DLL files 13 | __pycache__/ 14 | *.py[cod] 15 | *$py.class 16 | 17 | # C extensions 18 | *.so 19 | 20 | # Distribution / packaging 21 | .Python 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | wheels/ 34 | share/python-wheels/ 35 | *.egg-info/ 36 | .installed.cfg 37 | *.egg 38 | MANIFEST 39 | 40 | # PyInstaller 41 | # Usually these files are written by a python script from a template 42 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 43 | *.manifest 44 | *.spec 45 | 46 | # Installer logs 47 | pip-log.txt 48 | pip-delete-this-directory.txt 49 | 50 | # Unit test / coverage reports 51 | htmlcov/ 52 | .tox/ 53 | .nox/ 54 | .coverage 55 | .coverage.* 56 | .cache 57 | nosetests.xml 58 | coverage.xml 59 | *.cover 60 | *.py,cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | cover/ 64 | 65 | # Translations 66 | *.mo 67 | *.pot 68 | 69 | # Django stuff: 70 | *.log 71 | local_settings.py 72 | db.sqlite3 73 | db.sqlite3-journal 74 | 75 | # Flask stuff: 76 | instance/ 77 | .webassets-cache 78 | 79 | # Scrapy stuff: 80 | .scrapy 81 | 82 | # Sphinx documentation 83 | docs/_build/ 84 | 85 | # PyBuilder 86 | .pybuilder/ 87 | target/ 88 | 89 | # Jupyter Notebook 90 | .ipynb_checkpoints 91 | 92 | # IPython 93 | profile_default/ 94 | ipython_config.py 95 | 96 | # pyenv 97 | # For a library or package, you might want to ignore these files since the code is 98 | # intended to run in multiple environments; otherwise, check them in: 99 | # .python-version 100 | 101 | # pipenv 102 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 103 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 104 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 105 | # install all needed dependencies. 106 | #Pipfile.lock 107 | 108 | # poetry 109 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 110 | # This is especially recommended for binary packages to ensure reproducibility, and is more 111 | # commonly ignored for libraries. 112 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 113 | #poetry.lock 114 | 115 | # pdm 116 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 117 | #pdm.lock 118 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 119 | # in version control. 120 | # https://pdm.fming.dev/#use-with-ide 121 | .pdm.toml 122 | 123 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 124 | __pypackages__/ 125 | 126 | # Celery stuff 127 | celerybeat-schedule 128 | celerybeat.pid 129 | 130 | # SageMath parsed files 131 | *.sage.py 132 | 133 | # Environments 134 | .env 135 | .venv 136 | env/ 137 | venv/ 138 | ENV/ 139 | env.bak/ 140 | venv.bak/ 141 | 142 | # Spyder project settings 143 | .spyderproject 144 | .spyproject 145 | 146 | # Rope project settings 147 | .ropeproject 148 | 149 | # mkdocs documentation 150 | /site 151 | 152 | # mypy 153 | .mypy_cache/ 154 | .dmypy.json 155 | dmypy.json 156 | 157 | # Pyre type checker 158 | .pyre/ 159 | 160 | # pytype static type analyzer 161 | .pytype/ 162 | 163 | # Cython debug symbols 164 | cython_debug/ 165 | 166 | # PyCharm 167 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 168 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 169 | # and can be added to the global gitignore or merged into this file. For a more nuclear 170 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 171 | #.idea/ 172 | 173 | # Logs 174 | logs 175 | *.log 176 | npm-debug.log* 177 | yarn-debug.log* 178 | yarn-error.log* 179 | lerna-debug.log* 180 | .pnpm-debug.log* 181 | 182 | # Diagnostic reports (https://nodejs.org/api/report.html) 183 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 184 | 185 | # Runtime data 186 | pids 187 | *.pid 188 | *.seed 189 | *.pid.lock 190 | 191 | # Directory for instrumented libs generated by jscoverage/JSCover 192 | lib-cov 193 | 194 | # Coverage directory used by tools like istanbul 195 | coverage 196 | *.lcov 197 | 198 | # nyc test coverage 199 | .nyc_output 200 | 201 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 202 | .grunt 203 | 204 | # Bower dependency directory (https://bower.io/) 205 | bower_components 206 | 207 | # node-waf configuration 208 | .lock-wscript 209 | 210 | # Compiled binary addons (https://nodejs.org/api/addons.html) 211 | build/Release 212 | 213 | # Dependency directories 214 | node_modules/ 215 | jspm_packages/ 216 | 217 | # Snowpack dependency directory (https://snowpack.dev/) 218 | web_modules/ 219 | 220 | # TypeScript cache 221 | *.tsbuildinfo 222 | 223 | # Optional npm cache directory 224 | .npm 225 | 226 | # Optional eslint cache 227 | .eslintcache 228 | 229 | # Optional stylelint cache 230 | .stylelintcache 231 | 232 | # Microbundle cache 233 | .rpt2_cache/ 234 | .rts2_cache_cjs/ 235 | .rts2_cache_es/ 236 | .rts2_cache_umd/ 237 | 238 | # Optional REPL history 239 | .node_repl_history 240 | 241 | # Output of 'npm pack' 242 | *.tgz 243 | 244 | # Yarn Integrity file 245 | .yarn-integrity 246 | 247 | # dotenv environment variable files 248 | .env 249 | .env.development.local 250 | .env.test.local 251 | .env.production.local 252 | .env.local 253 | 254 | # parcel-bundler cache (https://parceljs.org/) 255 | .cache 256 | .parcel-cache 257 | 258 | # Next.js build output 259 | .next 260 | out 261 | 262 | # Nuxt.js build / generate output 263 | .nuxt 264 | dist 265 | 266 | # Gatsby files 267 | .cache/ 268 | # Comment in the public line in if your project uses Gatsby and not Next.js 269 | # https://nextjs.org/blog/next-9-1#public-directory-support 270 | # public 271 | 272 | # vuepress build output 273 | .vuepress/dist 274 | 275 | # vuepress v2.x temp and cache directory 276 | .temp 277 | .cache 278 | 279 | # Docusaurus cache and generated files 280 | .docusaurus 281 | 282 | # Serverless directories 283 | .serverless/ 284 | 285 | # FuseBox cache 286 | .fusebox/ 287 | 288 | # DynamoDB Local files 289 | .dynamodb/ 290 | 291 | # TernJS port file 292 | .tern-port 293 | 294 | # Stores VSCode versions used for testing VSCode extensions 295 | .vscode-test 296 | 297 | # yarn v2 298 | .yarn/cache 299 | .yarn/unplugged 300 | .yarn/build-state.yml 301 | .yarn/install-state.gz 302 | .pnp.* 303 | -------------------------------------------------------------------------------- /ReactLoader.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", 5 | "elements": [ 6 | { 7 | "type": "rectangle", 8 | "version": 421, 9 | "versionNonce": 416024419, 10 | "isDeleted": false, 11 | "id": "48ddpPigwfhz59uGm416y", 12 | "fillStyle": "solid", 13 | "strokeWidth": 2, 14 | "strokeStyle": "solid", 15 | "roughness": 1, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 1327.9049700930693, 19 | "y": -477.5256748993881, 20 | "strokeColor": "#1e1e1e", 21 | "backgroundColor": "transparent", 22 | "width": 599.0082973010623, 23 | "height": 705.2475047469107, 24 | "seed": 1631885079, 25 | "groupIds": [ 26 | "EwIH6d5Rn8VaPDNlnMZhT" 27 | ], 28 | "frameId": null, 29 | "roundness": { 30 | "type": 3 31 | }, 32 | "boundElements": [ 33 | { 34 | "id": "5c5aXxRFQmUBGU7K6FDtE", 35 | "type": "arrow" 36 | }, 37 | { 38 | "id": "fL5XpIxZk5gu22oI7Nu6t", 39 | "type": "arrow" 40 | }, 41 | { 42 | "id": "Cb_rUlWl5VITiirvJ_U_W", 43 | "type": "arrow" 44 | } 45 | ], 46 | "updated": 1705953142573, 47 | "link": null, 48 | "locked": false 49 | }, 50 | { 51 | "type": "text", 52 | "version": 266, 53 | "versionNonce": 1348091152, 54 | "isDeleted": false, 55 | "id": "7vY0ZYpm071wBnS5puyUZ", 56 | "fillStyle": "solid", 57 | "strokeWidth": 2, 58 | "strokeStyle": "solid", 59 | "roughness": 1, 60 | "opacity": 100, 61 | "angle": 0, 62 | "x": 1461.2690815676451, 63 | "y": -555.3379349712066, 64 | "strokeColor": "#1e1e1e", 65 | "backgroundColor": "transparent", 66 | "width": 299.37591552734375, 67 | "height": 45, 68 | "seed": 1274699543, 69 | "groupIds": [ 70 | "EwIH6d5Rn8VaPDNlnMZhT" 71 | ], 72 | "frameId": null, 73 | "roundness": null, 74 | "boundElements": [], 75 | "updated": 1706203062490, 76 | "link": null, 77 | "locked": false, 78 | "fontSize": 36, 79 | "fontFamily": 1, 80 | "text": "Django Template", 81 | "textAlign": "left", 82 | "verticalAlign": "top", 83 | "containerId": null, 84 | "originalText": "Django Template", 85 | "lineHeight": 1.25, 86 | "baseline": 32 87 | }, 88 | { 89 | "type": "text", 90 | "version": 301, 91 | "versionNonce": 450986256, 92 | "isDeleted": false, 93 | "id": "nqV5ARNn-XuEToyDvmNFr", 94 | "fillStyle": "solid", 95 | "strokeWidth": 2, 96 | "strokeStyle": "solid", 97 | "roughness": 1, 98 | "opacity": 100, 99 | "angle": 0, 100 | "x": 1368.5923261361602, 101 | "y": -420.0976527944158, 102 | "strokeColor": "#1e1e1e", 103 | "backgroundColor": "transparent", 104 | "width": 278.23306128708333, 105 | "height": 25.900968614361208, 106 | "seed": 998156345, 107 | "groupIds": [ 108 | "EwIH6d5Rn8VaPDNlnMZhT" 109 | ], 110 | "frameId": null, 111 | "roundness": null, 112 | "boundElements": [], 113 | "updated": 1706203135561, 114 | "link": null, 115 | "locked": false, 116 | "fontSize": 21.584140511967668, 117 | "fontFamily": 3, 118 | "text": "{% load ReactLoader %}", 119 | "textAlign": "left", 120 | "verticalAlign": "top", 121 | "containerId": null, 122 | "originalText": "{% load ReactLoader %}", 123 | "lineHeight": 1.2, 124 | "baseline": 20.000000000000007 125 | }, 126 | { 127 | "type": "text", 128 | "version": 328, 129 | "versionNonce": 1378884368, 130 | "isDeleted": false, 131 | "id": "k3N-GivOOaaFFzrNXztFl", 132 | "fillStyle": "solid", 133 | "strokeWidth": 2, 134 | "strokeStyle": "solid", 135 | "roughness": 1, 136 | "opacity": 100, 137 | "angle": 0, 138 | "x": 1359.550691459918, 139 | "y": -353.2031981010545, 140 | "strokeColor": "#1e1e1e", 141 | "backgroundColor": "transparent", 142 | "width": 400.31573486328125, 143 | "height": 210, 144 | "seed": 875297721, 145 | "groupIds": [ 146 | "EwIH6d5Rn8VaPDNlnMZhT" 147 | ], 148 | "frameId": null, 149 | "roundness": null, 150 | "boundElements": [], 151 | "updated": 1706203062493, 152 | "link": null, 153 | "locked": false, 154 | "fontSize": 28, 155 | "fontFamily": 1, 156 | "text": "~~~~~~~~~~~~~~~~~\n~~~~~~~~\n~~~~~~~~~\n~~~~~~~~~~~~~~\n~~~~~~~~~~~~~\n~~~~", 157 | "textAlign": "left", 158 | "verticalAlign": "top", 159 | "containerId": null, 160 | "originalText": "~~~~~~~~~~~~~~~~~\n~~~~~~~~\n~~~~~~~~~\n~~~~~~~~~~~~~~\n~~~~~~~~~~~~~\n~~~~", 161 | "lineHeight": 1.25, 162 | "baseline": 200 163 | }, 164 | { 165 | "type": "text", 166 | "version": 381, 167 | "versionNonce": 1059522832, 168 | "isDeleted": false, 169 | "id": "b5kzSPbVM57QvPy9BtwDT", 170 | "fillStyle": "solid", 171 | "strokeWidth": 2, 172 | "strokeStyle": "solid", 173 | "roughness": 1, 174 | "opacity": 100, 175 | "angle": 0, 176 | "x": 1359.5506914599177, 177 | "y": -109.57919116697751, 178 | "strokeColor": "#1e1e1e", 179 | "backgroundColor": "transparent", 180 | "width": 552.298828125, 181 | "height": 27.598085979166054, 182 | "seed": 848222039, 183 | "groupIds": [ 184 | "EwIH6d5Rn8VaPDNlnMZhT" 185 | ], 186 | "frameId": null, 187 | "roundness": null, 188 | "boundElements": [], 189 | "updated": 1706203110952, 190 | "link": null, 191 | "locked": false, 192 | "fontSize": 22.99840498263838, 193 | "fontFamily": 3, 194 | "text": "{% jsxComponent \"components/Button.js\" %}", 195 | "textAlign": "left", 196 | "verticalAlign": "top", 197 | "containerId": null, 198 | "originalText": "{% jsxComponent \"components/Button.js\" %}", 199 | "lineHeight": 1.2, 200 | "baseline": 22 201 | }, 202 | { 203 | "type": "text", 204 | "version": 441, 205 | "versionNonce": 429537552, 206 | "isDeleted": false, 207 | "id": "9x9TZfZLsr3k7uuJ09iJ2", 208 | "fillStyle": "solid", 209 | "strokeWidth": 2, 210 | "strokeStyle": "solid", 211 | "roughness": 1, 212 | "opacity": 100, 213 | "angle": 0, 214 | "x": 1362.8296042437323, 215 | "y": -40.027594324841345, 216 | "strokeColor": "#1e1e1e", 217 | "backgroundColor": "transparent", 218 | "width": 400.31573486328125, 219 | "height": 210, 220 | "seed": 663645433, 221 | "groupIds": [ 222 | "EwIH6d5Rn8VaPDNlnMZhT" 223 | ], 224 | "frameId": null, 225 | "roundness": null, 226 | "boundElements": [], 227 | "updated": 1706203062496, 228 | "link": null, 229 | "locked": false, 230 | "fontSize": 28, 231 | "fontFamily": 1, 232 | "text": "~~~~~~~~~~~~~~~~~\n~~~~~~~~\n~~~~~~~~~\n~~~~~~~~~~~~~~\n~~~~~~~~~~~~~\n~~~~", 233 | "textAlign": "left", 234 | "verticalAlign": "top", 235 | "containerId": null, 236 | "originalText": "~~~~~~~~~~~~~~~~~\n~~~~~~~~\n~~~~~~~~~\n~~~~~~~~~~~~~~\n~~~~~~~~~~~~~\n~~~~", 237 | "lineHeight": 1.25, 238 | "baseline": 200 239 | }, 240 | { 241 | "type": "rectangle", 242 | "version": 254, 243 | "versionNonce": 1479919053, 244 | "isDeleted": false, 245 | "id": "4EKeFcGFkThotE4vNopwJ", 246 | "fillStyle": "solid", 247 | "strokeWidth": 2, 248 | "strokeStyle": "solid", 249 | "roughness": 1, 250 | "opacity": 100, 251 | "angle": 0, 252 | "x": 2256.633285127929, 253 | "y": -94.69718508713731, 254 | "strokeColor": "#1e1e1e", 255 | "backgroundColor": "transparent", 256 | "width": 322.1874658505312, 257 | "height": 84.33645009926227, 258 | "seed": 480910957, 259 | "groupIds": [], 260 | "frameId": null, 261 | "roundness": { 262 | "type": 3 263 | }, 264 | "boundElements": [ 265 | { 266 | "id": "fL5XpIxZk5gu22oI7Nu6t", 267 | "type": "arrow" 268 | }, 269 | { 270 | "type": "text", 271 | "id": "kdeJMRyfagbzHoSlDU0rU" 272 | }, 273 | { 274 | "id": "qa-ro7yMv_wVQD8Lig5-D", 275 | "type": "arrow" 276 | }, 277 | { 278 | "id": "HT80xschFgla7fqyUWoKp", 279 | "type": "arrow" 280 | } 281 | ], 282 | "updated": 1705953142573, 283 | "link": null, 284 | "locked": false 285 | }, 286 | { 287 | "type": "text", 288 | "version": 141, 289 | "versionNonce": 1129056643, 290 | "isDeleted": false, 291 | "id": "kdeJMRyfagbzHoSlDU0rU", 292 | "fillStyle": "solid", 293 | "strokeWidth": 2, 294 | "strokeStyle": "solid", 295 | "roughness": 1, 296 | "opacity": 100, 297 | "angle": 0, 298 | "x": 2326.447049791476, 299 | "y": -87.52896003750618, 300 | "strokeColor": "#1e1e1e", 301 | "backgroundColor": "transparent", 302 | "width": 182.5599365234375, 303 | "height": 70, 304 | "seed": 1007312419, 305 | "groupIds": [], 306 | "frameId": null, 307 | "roundness": null, 308 | "boundElements": [], 309 | "updated": 1705953142573, 310 | "link": null, 311 | "locked": false, 312 | "fontSize": 28, 313 | "fontFamily": 1, 314 | "text": "Read JSX & \nComponent", 315 | "textAlign": "center", 316 | "verticalAlign": "middle", 317 | "containerId": "4EKeFcGFkThotE4vNopwJ", 318 | "originalText": "Read JSX & Component", 319 | "lineHeight": 1.25, 320 | "baseline": 64 321 | }, 322 | { 323 | "type": "arrow", 324 | "version": 1236, 325 | "versionNonce": 1086362384, 326 | "isDeleted": false, 327 | "id": "fL5XpIxZk5gu22oI7Nu6t", 328 | "fillStyle": "solid", 329 | "strokeWidth": 2, 330 | "strokeStyle": "solid", 331 | "roughness": 1, 332 | "opacity": 100, 333 | "angle": 0, 334 | "x": 1941.768777515311, 335 | "y": -56.673041895551094, 336 | "strokeColor": "#1e1e1e", 337 | "backgroundColor": "transparent", 338 | "width": 301.3383609865375, 339 | "height": 2.2685898149188617, 340 | "seed": 687230029, 341 | "groupIds": [], 342 | "frameId": null, 343 | "roundness": { 344 | "type": 2 345 | }, 346 | "boundElements": [ 347 | { 348 | "type": "text", 349 | "id": "FybSGq4z7Q3JgyOrXewLu" 350 | } 351 | ], 352 | "updated": 1706203128702, 353 | "link": null, 354 | "locked": false, 355 | "startBinding": { 356 | "elementId": "48ddpPigwfhz59uGm416y", 357 | "gap": 14.855510121179236, 358 | "focus": 0.1855909989123895 359 | }, 360 | "endBinding": { 361 | "elementId": "4EKeFcGFkThotE4vNopwJ", 362 | "gap": 13.52614662608039, 363 | "focus": 0.012929327645089953 364 | }, 365 | "lastCommittedPoint": null, 366 | "startArrowhead": null, 367 | "endArrowhead": "arrow", 368 | "points": [ 369 | [ 370 | 0, 371 | 0 372 | ], 373 | [ 374 | 301.3383609865375, 375 | 2.2685898149188617 376 | ] 377 | ] 378 | }, 379 | { 380 | "type": "text", 381 | "version": 26, 382 | "versionNonce": 2056121840, 383 | "isDeleted": false, 384 | "id": "FybSGq4z7Q3JgyOrXewLu", 385 | "fillStyle": "solid", 386 | "strokeWidth": 2, 387 | "strokeStyle": "solid", 388 | "roughness": 1, 389 | "opacity": 100, 390 | "angle": 0, 391 | "x": 1963.2146444128434, 392 | "y": -98.04172273070219, 393 | "strokeColor": "#1e1e1e", 394 | "backgroundColor": "transparent", 395 | "width": 196.7559051513672, 396 | "height": 35, 397 | "seed": 929153891, 398 | "groupIds": [], 399 | "frameId": null, 400 | "roundness": null, 401 | "boundElements": [], 402 | "updated": 1706203071925, 403 | "link": null, 404 | "locked": false, 405 | "fontSize": 28, 406 | "fontFamily": 1, 407 | "text": "Component File", 408 | "textAlign": "center", 409 | "verticalAlign": "middle", 410 | "containerId": "fL5XpIxZk5gu22oI7Nu6t", 411 | "originalText": "Component File", 412 | "lineHeight": 1.25, 413 | "baseline": 25 414 | }, 415 | { 416 | "type": "rectangle", 417 | "version": 425, 418 | "versionNonce": 1670657123, 419 | "isDeleted": false, 420 | "id": "hF6QDLelGfOx_MAN9BX_n", 421 | "fillStyle": "solid", 422 | "strokeWidth": 2, 423 | "strokeStyle": "solid", 424 | "roughness": 1, 425 | "opacity": 100, 426 | "angle": 0, 427 | "x": 2791.8076312034514, 428 | "y": -89.18855123708812, 429 | "strokeColor": "#1e1e1e", 430 | "backgroundColor": "transparent", 431 | "width": 357.6204457386994, 432 | "height": 65.42504095815389, 433 | "seed": 1952283299, 434 | "groupIds": [], 435 | "frameId": null, 436 | "roundness": { 437 | "type": 3 438 | }, 439 | "boundElements": [ 440 | { 441 | "type": "text", 442 | "id": "6CSdbdQGEFK-d3Dgg7VaT" 443 | }, 444 | { 445 | "id": "qa-ro7yMv_wVQD8Lig5-D", 446 | "type": "arrow" 447 | }, 448 | { 449 | "id": "rehjvCjxbJShTg8-wNYqv", 450 | "type": "arrow" 451 | }, 452 | { 453 | "id": "wwcuKOvQLU5eVidKUJEzC", 454 | "type": "arrow" 455 | } 456 | ], 457 | "updated": 1705953142573, 458 | "link": null, 459 | "locked": false 460 | }, 461 | { 462 | "type": "text", 463 | "version": 300, 464 | "versionNonce": 1984034637, 465 | "isDeleted": false, 466 | "id": "6CSdbdQGEFK-d3Dgg7VaT", 467 | "fillStyle": "solid", 468 | "strokeWidth": 2, 469 | "strokeStyle": "solid", 470 | "roughness": 1, 471 | "opacity": 100, 472 | "angle": 0, 473 | "x": 2856.573908760301, 474 | "y": -73.97603075801118, 475 | "strokeColor": "#1e1e1e", 476 | "backgroundColor": "transparent", 477 | "width": 228.087890625, 478 | "height": 35, 479 | "seed": 750530115, 480 | "groupIds": [], 481 | "frameId": null, 482 | "roundness": null, 483 | "boundElements": [], 484 | "updated": 1705953142573, 485 | "link": null, 486 | "locked": false, 487 | "fontSize": 28, 488 | "fontFamily": 1, 489 | "text": "Bundle to js file", 490 | "textAlign": "center", 491 | "verticalAlign": "middle", 492 | "containerId": "hF6QDLelGfOx_MAN9BX_n", 493 | "originalText": "Bundle to js file", 494 | "lineHeight": 1.25, 495 | "baseline": 29 496 | }, 497 | { 498 | "type": "rectangle", 499 | "version": 610, 500 | "versionNonce": 788497837, 501 | "isDeleted": false, 502 | "id": "qLw9EoJkzM5QXkKV3s362", 503 | "fillStyle": "solid", 504 | "strokeWidth": 2, 505 | "strokeStyle": "solid", 506 | "roughness": 1, 507 | "opacity": 100, 508 | "angle": 0, 509 | "x": 2757.0833109130463, 510 | "y": 121.49592406955452, 511 | "strokeColor": "#1e1e1e", 512 | "backgroundColor": "transparent", 513 | "width": 397.3053832134475, 514 | "height": 63.149700916305164, 515 | "seed": 664920259, 516 | "groupIds": [], 517 | "frameId": null, 518 | "roundness": { 519 | "type": 3 520 | }, 521 | "boundElements": [ 522 | { 523 | "type": "text", 524 | "id": "36mtKnHXld57R1oAIkUrz" 525 | }, 526 | { 527 | "id": "rehjvCjxbJShTg8-wNYqv", 528 | "type": "arrow" 529 | }, 530 | { 531 | "id": "5wTIRTg3yguIEfqz2CGja", 532 | "type": "arrow" 533 | } 534 | ], 535 | "updated": 1705953142573, 536 | "link": null, 537 | "locked": false 538 | }, 539 | { 540 | "type": "text", 541 | "version": 546, 542 | "versionNonce": 1726396323, 543 | "isDeleted": false, 544 | "id": "36mtKnHXld57R1oAIkUrz", 545 | "fillStyle": "solid", 546 | "strokeWidth": 2, 547 | "strokeStyle": "solid", 548 | "roughness": 1, 549 | "opacity": 100, 550 | "angle": 0, 551 | "x": 2811.816050127192, 552 | "y": 135.5707745277071, 553 | "strokeColor": "#1e1e1e", 554 | "backgroundColor": "transparent", 555 | "width": 287.83990478515625, 556 | "height": 35, 557 | "seed": 1657720931, 558 | "groupIds": [], 559 | "frameId": null, 560 | "roundness": null, 561 | "boundElements": [], 562 | "updated": 1705953142573, 563 | "link": null, 564 | "locked": false, 565 | "fontSize": 28, 566 | "fontFamily": 1, 567 | "text": "place file at /static", 568 | "textAlign": "center", 569 | "verticalAlign": "middle", 570 | "containerId": "qLw9EoJkzM5QXkKV3s362", 571 | "originalText": "place file at /static", 572 | "lineHeight": 1.25, 573 | "baseline": 29 574 | }, 575 | { 576 | "type": "arrow", 577 | "version": 457, 578 | "versionNonce": 1427491651, 579 | "isDeleted": false, 580 | "id": "qa-ro7yMv_wVQD8Lig5-D", 581 | "fillStyle": "solid", 582 | "strokeWidth": 2, 583 | "strokeStyle": "solid", 584 | "roughness": 1, 585 | "opacity": 100, 586 | "angle": 0, 587 | "x": 2591.4849692694015, 588 | "y": -47.41453100392816, 589 | "strokeColor": "#1e1e1e", 590 | "backgroundColor": "transparent", 591 | "width": 189.92077220058127, 592 | "height": 5.420189941265207, 593 | "seed": 1864061347, 594 | "groupIds": [], 595 | "frameId": null, 596 | "roundness": { 597 | "type": 2 598 | }, 599 | "boundElements": [ 600 | { 601 | "type": "text", 602 | "id": "3-g5I8tSn2ccxOC5WIA8W" 603 | } 604 | ], 605 | "updated": 1705953142573, 606 | "link": null, 607 | "locked": false, 608 | "startBinding": { 609 | "elementId": "4EKeFcGFkThotE4vNopwJ", 610 | "gap": 12.664218290941335, 611 | "focus": 0.2164672478811351 612 | }, 613 | "endBinding": { 614 | "elementId": "hF6QDLelGfOx_MAN9BX_n", 615 | "gap": 10.401889733468579, 616 | "focus": 0.046506056244010274 617 | }, 618 | "lastCommittedPoint": null, 619 | "startArrowhead": null, 620 | "endArrowhead": "arrow", 621 | "points": [ 622 | [ 623 | 0, 624 | 0 625 | ], 626 | [ 627 | 189.92077220058127, 628 | -5.420189941265207 629 | ] 630 | ] 631 | }, 632 | { 633 | "type": "text", 634 | "version": 102, 635 | "versionNonce": 141672045, 636 | "isDeleted": false, 637 | "id": "3-g5I8tSn2ccxOC5WIA8W", 638 | "fillStyle": "solid", 639 | "strokeWidth": 2, 640 | "strokeStyle": "solid", 641 | "roughness": 1, 642 | "opacity": 100, 643 | "angle": 0, 644 | "x": 2632.3389845817164, 645 | "y": -78.93664794235482, 646 | "strokeColor": "#1e1e1e", 647 | "backgroundColor": "transparent", 648 | "width": 51.51997375488281, 649 | "height": 35, 650 | "seed": 1027896557, 651 | "groupIds": [], 652 | "frameId": null, 653 | "roundness": null, 654 | "boundElements": [], 655 | "updated": 1705953142573, 656 | "link": null, 657 | "locked": false, 658 | "fontSize": 28, 659 | "fontFamily": 1, 660 | "text": "JSX", 661 | "textAlign": "center", 662 | "verticalAlign": "middle", 663 | "containerId": "qa-ro7yMv_wVQD8Lig5-D", 664 | "originalText": "JSX", 665 | "lineHeight": 1.25, 666 | "baseline": 29 667 | }, 668 | { 669 | "type": "arrow", 670 | "version": 720, 671 | "versionNonce": 1063348963, 672 | "isDeleted": false, 673 | "id": "rehjvCjxbJShTg8-wNYqv", 674 | "fillStyle": "solid", 675 | "strokeWidth": 2, 676 | "strokeStyle": "solid", 677 | "roughness": 1, 678 | "opacity": 100, 679 | "angle": 0, 680 | "x": 2963.6895653947713, 681 | "y": -13.956454892209422, 682 | "strokeColor": "#1e1e1e", 683 | "backgroundColor": "transparent", 684 | "width": 2.223844157943404, 685 | "height": 128.47996196609256, 686 | "seed": 1318285901, 687 | "groupIds": [], 688 | "frameId": null, 689 | "roundness": { 690 | "type": 2 691 | }, 692 | "boundElements": [ 693 | { 694 | "type": "text", 695 | "id": "bxl7RZx1dInrUN2wUJD5F" 696 | } 697 | ], 698 | "updated": 1705953142573, 699 | "link": null, 700 | "locked": false, 701 | "startBinding": { 702 | "elementId": "hF6QDLelGfOx_MAN9BX_n", 703 | "focus": 0.034521381249021266, 704 | "gap": 9.807055386724812 705 | }, 706 | "endBinding": { 707 | "elementId": "qLw9EoJkzM5QXkKV3s362", 708 | "focus": 0.025414300294078127, 709 | "gap": 6.972416995671381 710 | }, 711 | "lastCommittedPoint": null, 712 | "startArrowhead": null, 713 | "endArrowhead": "arrow", 714 | "points": [ 715 | [ 716 | 0, 717 | 0 718 | ], 719 | [ 720 | -2.223844157943404, 721 | 128.47996196609256 722 | ] 723 | ] 724 | }, 725 | { 726 | "type": "text", 727 | "version": 105, 728 | "versionNonce": 1911122125, 729 | "isDeleted": false, 730 | "id": "bxl7RZx1dInrUN2wUJD5F", 731 | "fillStyle": "solid", 732 | "strokeWidth": 2, 733 | "strokeStyle": "solid", 734 | "roughness": 1, 735 | "opacity": 100, 736 | "angle": 0, 737 | "x": 2840.8153847050744, 738 | "y": 6.1025037892488285, 739 | "strokeColor": "#1e1e1e", 740 | "backgroundColor": "transparent", 741 | "width": 90.94395446777344, 742 | "height": 35, 743 | "seed": 1096302595, 744 | "groupIds": [], 745 | "frameId": null, 746 | "roundness": null, 747 | "boundElements": [], 748 | "updated": 1705953142573, 749 | "link": null, 750 | "locked": false, 751 | "fontSize": 28, 752 | "fontFamily": 1, 753 | "text": ".js File", 754 | "textAlign": "center", 755 | "verticalAlign": "middle", 756 | "containerId": "rehjvCjxbJShTg8-wNYqv", 757 | "originalText": ".js File", 758 | "lineHeight": 1.25, 759 | "baseline": 29 760 | }, 761 | { 762 | "type": "rectangle", 763 | "version": 532, 764 | "versionNonce": 79187491, 765 | "isDeleted": false, 766 | "id": "6u7ehXkt72AHfe-YYl_kw", 767 | "fillStyle": "solid", 768 | "strokeWidth": 2, 769 | "strokeStyle": "solid", 770 | "roughness": 1, 771 | "opacity": 100, 772 | "angle": 0, 773 | "x": 2264.564890467509, 774 | "y": 110.15737050534062, 775 | "strokeColor": "#1e1e1e", 776 | "backgroundColor": "transparent", 777 | "width": 322.1874658505312, 778 | "height": 80, 779 | "seed": 27911939, 780 | "groupIds": [], 781 | "frameId": null, 782 | "roundness": { 783 | "type": 3 784 | }, 785 | "boundElements": [ 786 | { 787 | "type": "text", 788 | "id": "R-wirXAyBr1IRTgL5hOlV" 789 | }, 790 | { 791 | "id": "Cb_rUlWl5VITiirvJ_U_W", 792 | "type": "arrow" 793 | }, 794 | { 795 | "id": "5wTIRTg3yguIEfqz2CGja", 796 | "type": "arrow" 797 | } 798 | ], 799 | "updated": 1705953142573, 800 | "link": null, 801 | "locked": false 802 | }, 803 | { 804 | "type": "text", 805 | "version": 429, 806 | "versionNonce": 2085968269, 807 | "isDeleted": false, 808 | "id": "R-wirXAyBr1IRTgL5hOlV", 809 | "fillStyle": "solid", 810 | "strokeWidth": 2, 811 | "strokeStyle": "solid", 812 | "roughness": 1, 813 | "opacity": 100, 814 | "angle": 0, 815 | "x": 2303.956665690138, 816 | "y": 115.15737050534062, 817 | "strokeColor": "#1e1e1e", 818 | "backgroundColor": "transparent", 819 | "width": 243.40391540527344, 820 | "height": 70, 821 | "seed": 1115426467, 822 | "groupIds": [], 823 | "frameId": null, 824 | "roundness": null, 825 | "boundElements": [], 826 | "updated": 1705953142573, 827 | "link": null, 828 | "locked": false, 829 | "fontSize": 28, 830 | "fontFamily": 1, 831 | "text": "Add to template \nImports", 832 | "textAlign": "center", 833 | "verticalAlign": "middle", 834 | "containerId": "6u7ehXkt72AHfe-YYl_kw", 835 | "originalText": "Add to template Imports", 836 | "lineHeight": 1.25, 837 | "baseline": 64 838 | }, 839 | { 840 | "type": "rectangle", 841 | "version": 744, 842 | "versionNonce": 561025475, 843 | "isDeleted": false, 844 | "id": "_nb_r7uRYplTPJOuWatzB", 845 | "fillStyle": "solid", 846 | "strokeWidth": 2, 847 | "strokeStyle": "solid", 848 | "roughness": 1, 849 | "opacity": 100, 850 | "angle": 0, 851 | "x": 2796.2285738153946, 852 | "y": -300.8599224261769, 853 | "strokeColor": "#1e1e1e", 854 | "backgroundColor": "transparent", 855 | "width": 353.3684881521192, 856 | "height": 80, 857 | "seed": 1786049411, 858 | "groupIds": [], 859 | "frameId": null, 860 | "roundness": { 861 | "type": 3 862 | }, 863 | "boundElements": [ 864 | { 865 | "type": "text", 866 | "id": "sJoHxZ73AmeRzPsWac2XP" 867 | }, 868 | { 869 | "id": "wwcuKOvQLU5eVidKUJEzC", 870 | "type": "arrow" 871 | }, 872 | { 873 | "id": "7GArb-2_pFFxm1BxlQtYc", 874 | "type": "arrow" 875 | } 876 | ], 877 | "updated": 1705953142573, 878 | "link": null, 879 | "locked": false 880 | }, 881 | { 882 | "type": "text", 883 | "version": 659, 884 | "versionNonce": 1491902445, 885 | "isDeleted": false, 886 | "id": "sJoHxZ73AmeRzPsWac2XP", 887 | "fillStyle": "solid", 888 | "strokeWidth": 2, 889 | "strokeStyle": "solid", 890 | "roughness": 1, 891 | "opacity": 100, 892 | "angle": 0, 893 | "x": 2817.554877217626, 894 | "y": -295.8599224261769, 895 | "strokeColor": "#1e1e1e", 896 | "backgroundColor": "transparent", 897 | "width": 310.71588134765625, 898 | "height": 70, 899 | "seed": 1731300131, 900 | "groupIds": [], 901 | "frameId": null, 902 | "roundness": null, 903 | "boundElements": [], 904 | "updated": 1705953142573, 905 | "link": null, 906 | "locked": false, 907 | "fontSize": 28, 908 | "fontFamily": 1, 909 | "text": "Create React Render \nScript", 910 | "textAlign": "center", 911 | "verticalAlign": "middle", 912 | "containerId": "_nb_r7uRYplTPJOuWatzB", 913 | "originalText": "Create React Render Script", 914 | "lineHeight": 1.25, 915 | "baseline": 64 916 | }, 917 | { 918 | "type": "rectangle", 919 | "version": 620, 920 | "versionNonce": 1906270563, 921 | "isDeleted": false, 922 | "id": "9HZnp_BH1RhRnqMf1bFom", 923 | "fillStyle": "solid", 924 | "strokeWidth": 2, 925 | "strokeStyle": "solid", 926 | "roughness": 1, 927 | "opacity": 100, 928 | "angle": 0, 929 | "x": 2252.4118748361866, 930 | "y": -474.2741778060989, 931 | "strokeColor": "#1e1e1e", 932 | "backgroundColor": "transparent", 933 | "width": 322.1874658505312, 934 | "height": 80, 935 | "seed": 1261434157, 936 | "groupIds": [], 937 | "frameId": null, 938 | "roundness": { 939 | "type": 3 940 | }, 941 | "boundElements": [ 942 | { 943 | "type": "text", 944 | "id": "rlP7F3IwzOxBrlfBR5kXn" 945 | }, 946 | { 947 | "id": "5c5aXxRFQmUBGU7K6FDtE", 948 | "type": "arrow" 949 | }, 950 | { 951 | "id": "7GArb-2_pFFxm1BxlQtYc", 952 | "type": "arrow" 953 | }, 954 | { 955 | "id": "HT80xschFgla7fqyUWoKp", 956 | "type": "arrow" 957 | }, 958 | { 959 | "id": "t2I8QqNMfMAmyW1LgOgyu", 960 | "type": "arrow" 961 | } 962 | ], 963 | "updated": 1705953142573, 964 | "link": null, 965 | "locked": false 966 | }, 967 | { 968 | "type": "text", 969 | "version": 590, 970 | "versionNonce": 59513421, 971 | "isDeleted": false, 972 | "id": "rlP7F3IwzOxBrlfBR5kXn", 973 | "fillStyle": "solid", 974 | "strokeWidth": 2, 975 | "strokeStyle": "solid", 976 | "roughness": 1, 977 | "opacity": 100, 978 | "angle": 0, 979 | "x": 2269.4036637917256, 980 | "y": -469.2741778060989, 981 | "strokeColor": "#1e1e1e", 982 | "backgroundColor": "transparent", 983 | "width": 288.2038879394531, 984 | "height": 70, 985 | "seed": 355649421, 986 | "groupIds": [], 987 | "frameId": null, 988 | "roundness": null, 989 | "boundElements": [], 990 | "updated": 1705953142573, 991 | "link": null, 992 | "locked": false, 993 | "fontSize": 28, 994 | "fontFamily": 1, 995 | "text": "Create Placeholder \nHtml element with ID", 996 | "textAlign": "center", 997 | "verticalAlign": "middle", 998 | "containerId": "9HZnp_BH1RhRnqMf1bFom", 999 | "originalText": "Create Placeholder Html element with ID", 1000 | "lineHeight": 1.25, 1001 | "baseline": 64 1002 | }, 1003 | { 1004 | "type": "arrow", 1005 | "version": 1255, 1006 | "versionNonce": 1735333648, 1007 | "isDeleted": false, 1008 | "id": "5c5aXxRFQmUBGU7K6FDtE", 1009 | "fillStyle": "solid", 1010 | "strokeWidth": 2, 1011 | "strokeStyle": "solid", 1012 | "roughness": 1, 1013 | "opacity": 100, 1014 | "angle": 0, 1015 | "x": 2240.592665907191, 1016 | "y": -431.3590662118504, 1017 | "strokeColor": "#1e1e1e", 1018 | "backgroundColor": "transparent", 1019 | "width": 295.78169777719313, 1020 | "height": 3.588338878432012, 1021 | "seed": 1966668419, 1022 | "groupIds": [], 1023 | "frameId": null, 1024 | "roundness": { 1025 | "type": 2 1026 | }, 1027 | "boundElements": [ 1028 | { 1029 | "type": "text", 1030 | "id": "zn8i5Hsl42h6zoio6rNXg" 1031 | } 1032 | ], 1033 | "updated": 1706203128702, 1034 | "link": null, 1035 | "locked": false, 1036 | "startBinding": { 1037 | "elementId": "9HZnp_BH1RhRnqMf1bFom", 1038 | "focus": -0.019482638204442196, 1039 | "gap": 11.819208928995522 1040 | }, 1041 | "endBinding": { 1042 | "elementId": "48ddpPigwfhz59uGm416y", 1043 | "focus": -0.8393321837897294, 1044 | "gap": 17.897700735866124 1045 | }, 1046 | "lastCommittedPoint": null, 1047 | "startArrowhead": null, 1048 | "endArrowhead": "arrow", 1049 | "points": [ 1050 | [ 1051 | 0, 1052 | 0 1053 | ], 1054 | [ 1055 | -295.78169777719313, 1056 | 3.588338878432012 1057 | ] 1058 | ] 1059 | }, 1060 | { 1061 | "type": "text", 1062 | "version": 117, 1063 | "versionNonce": 1778788336, 1064 | "isDeleted": false, 1065 | "id": "zn8i5Hsl42h6zoio6rNXg", 1066 | "fillStyle": "solid", 1067 | "strokeWidth": 2, 1068 | "strokeStyle": "solid", 1069 | "roughness": 1, 1070 | "opacity": 100, 1071 | "angle": 0, 1072 | "x": 2033.271831972208, 1073 | "y": -447.06489677263437, 1074 | "strokeColor": "#1e1e1e", 1075 | "backgroundColor": "transparent", 1076 | "width": 118.85997009277344, 1077 | "height": 35, 1078 | "seed": 721042221, 1079 | "groupIds": [], 1080 | "frameId": null, 1081 | "roundness": null, 1082 | "boundElements": [], 1083 | "updated": 1706203071924, 1084 | "link": null, 1085 | "locked": false, 1086 | "fontSize": 28, 1087 | "fontFamily": 1, 1088 | "text": "Html tag", 1089 | "textAlign": "center", 1090 | "verticalAlign": "middle", 1091 | "containerId": "5c5aXxRFQmUBGU7K6FDtE", 1092 | "originalText": "Html tag", 1093 | "lineHeight": 1.25, 1094 | "baseline": 25 1095 | }, 1096 | { 1097 | "type": "arrow", 1098 | "version": 753, 1099 | "versionNonce": 1491275536, 1100 | "isDeleted": false, 1101 | "id": "Cb_rUlWl5VITiirvJ_U_W", 1102 | "fillStyle": "solid", 1103 | "strokeWidth": 2, 1104 | "strokeStyle": "solid", 1105 | "roughness": 1, 1106 | "opacity": 100, 1107 | "angle": 0, 1108 | "x": 2254.1630007340405, 1109 | "y": 147.2383350178846, 1110 | "strokeColor": "#1e1e1e", 1111 | "backgroundColor": "transparent", 1112 | "width": 306.6996121132677, 1113 | "height": 2.357844398015459, 1114 | "seed": 1922818221, 1115 | "groupIds": [], 1116 | "frameId": null, 1117 | "roundness": { 1118 | "type": 2 1119 | }, 1120 | "boundElements": [ 1121 | { 1122 | "type": "text", 1123 | "id": "jBaiHlPSt9yb2v-9YWPxi" 1124 | } 1125 | ], 1126 | "updated": 1706203128703, 1127 | "link": null, 1128 | "locked": false, 1129 | "startBinding": { 1130 | "elementId": "6u7ehXkt72AHfe-YYl_kw", 1131 | "focus": 0.038813575708421796, 1132 | "gap": 10.401889733468579 1133 | }, 1134 | "endBinding": { 1135 | "elementId": "48ddpPigwfhz59uGm416y", 1136 | "focus": 0.7531758460337967, 1137 | "gap": 20.550121226640954 1138 | }, 1139 | "lastCommittedPoint": null, 1140 | "startArrowhead": null, 1141 | "endArrowhead": "arrow", 1142 | "points": [ 1143 | [ 1144 | 0, 1145 | 0 1146 | ], 1147 | [ 1148 | -306.6996121132677, 1149 | -2.357844398015459 1150 | ] 1151 | ] 1152 | }, 1153 | { 1154 | "type": "text", 1155 | "version": 82, 1156 | "versionNonce": 1856205808, 1157 | "isDeleted": false, 1158 | "id": "jBaiHlPSt9yb2v-9YWPxi", 1159 | "fillStyle": "solid", 1160 | "strokeWidth": 2, 1161 | "strokeStyle": "solid", 1162 | "roughness": 1, 1163 | "opacity": 100, 1164 | "angle": 0, 1165 | "x": 1995.8655382135153, 1166 | "y": 48.84248477229522, 1167 | "strokeColor": "#1e1e1e", 1168 | "backgroundColor": "transparent", 1169 | "width": 148.09193420410156, 1170 | "height": 35, 1171 | "seed": 1807928899, 1172 | "groupIds": [], 1173 | "frameId": null, 1174 | "roundness": null, 1175 | "boundElements": [], 1176 | "updated": 1706203071926, 1177 | "link": null, 1178 | "locked": false, 1179 | "fontSize": 28, 1180 | "fontFamily": 1, 1181 | "text": "Script Tag", 1182 | "textAlign": "center", 1183 | "verticalAlign": "middle", 1184 | "containerId": "Cb_rUlWl5VITiirvJ_U_W", 1185 | "originalText": "Script Tag", 1186 | "lineHeight": 1.25, 1187 | "baseline": 25 1188 | }, 1189 | { 1190 | "type": "arrow", 1191 | "version": 458, 1192 | "versionNonce": 150214723, 1193 | "isDeleted": false, 1194 | "id": "5wTIRTg3yguIEfqz2CGja", 1195 | "fillStyle": "solid", 1196 | "strokeWidth": 2, 1197 | "strokeStyle": "solid", 1198 | "roughness": 1, 1199 | "opacity": 100, 1200 | "angle": 0, 1201 | "x": 2748.807399972868, 1202 | "y": 149.83135761219165, 1203 | "strokeColor": "#1e1e1e", 1204 | "backgroundColor": "transparent", 1205 | "width": 155.90511150793964, 1206 | "height": 2.7840476567237857, 1207 | "seed": 1382424931, 1208 | "groupIds": [], 1209 | "frameId": null, 1210 | "roundness": { 1211 | "type": 2 1212 | }, 1213 | "boundElements": [ 1214 | { 1215 | "type": "text", 1216 | "id": "zRv3vdhic6Y7pc3xU4xDZ" 1217 | } 1218 | ], 1219 | "updated": 1705953142573, 1220 | "link": null, 1221 | "locked": false, 1222 | "startBinding": { 1223 | "elementId": "qLw9EoJkzM5QXkKV3s362", 1224 | "focus": 0.19744197420815277, 1225 | "gap": 8.275910940178164 1226 | }, 1227 | "endBinding": { 1228 | "elementId": "6u7ehXkt72AHfe-YYl_kw", 1229 | "focus": 0.12698174746373617, 1230 | "gap": 6.149932146888204 1231 | }, 1232 | "lastCommittedPoint": null, 1233 | "startArrowhead": null, 1234 | "endArrowhead": "arrow", 1235 | "points": [ 1236 | [ 1237 | 0, 1238 | 0 1239 | ], 1240 | [ 1241 | -155.90511150793964, 1242 | 2.7840476567237857 1243 | ] 1244 | ] 1245 | }, 1246 | { 1247 | "type": "text", 1248 | "version": 124, 1249 | "versionNonce": 1110098285, 1250 | "isDeleted": false, 1251 | "id": "zRv3vdhic6Y7pc3xU4xDZ", 1252 | "fillStyle": "solid", 1253 | "strokeWidth": 2, 1254 | "strokeStyle": "solid", 1255 | "roughness": 1, 1256 | "opacity": 100, 1257 | "angle": 0, 1258 | "x": 2613.342433886155, 1259 | "y": 132.45648696205143, 1260 | "strokeColor": "#1e1e1e", 1261 | "backgroundColor": "transparent", 1262 | "width": 66.83596801757812, 1263 | "height": 35, 1264 | "seed": 1275992749, 1265 | "groupIds": [], 1266 | "frameId": null, 1267 | "roundness": null, 1268 | "boundElements": [], 1269 | "updated": 1705953142573, 1270 | "link": null, 1271 | "locked": false, 1272 | "fontSize": 28, 1273 | "fontFamily": 1, 1274 | "text": "Path", 1275 | "textAlign": "center", 1276 | "verticalAlign": "middle", 1277 | "containerId": "5wTIRTg3yguIEfqz2CGja", 1278 | "originalText": "Path", 1279 | "lineHeight": 1.25, 1280 | "baseline": 29 1281 | }, 1282 | { 1283 | "type": "arrow", 1284 | "version": 1326, 1285 | "versionNonce": 1542330339, 1286 | "isDeleted": false, 1287 | "id": "wwcuKOvQLU5eVidKUJEzC", 1288 | "fillStyle": "solid", 1289 | "strokeWidth": 2, 1290 | "strokeStyle": "solid", 1291 | "roughness": 1, 1292 | "opacity": 100, 1293 | "angle": 0, 1294 | "x": 2967.925428056798, 1295 | "y": -211.8753518882353, 1296 | "strokeColor": "#1e1e1e", 1297 | "backgroundColor": "transparent", 1298 | "width": 0.40046478090198434, 1299 | "height": 108.62778767784232, 1300 | "seed": 2146761155, 1301 | "groupIds": [], 1302 | "frameId": null, 1303 | "roundness": { 1304 | "type": 2 1305 | }, 1306 | "boundElements": [ 1307 | { 1308 | "type": "text", 1309 | "id": "hJUfyH1IeNV2LMJOF3DOF" 1310 | } 1311 | ], 1312 | "updated": 1705953142574, 1313 | "link": null, 1314 | "locked": false, 1315 | "startBinding": { 1316 | "elementId": "_nb_r7uRYplTPJOuWatzB", 1317 | "gap": 8.984570537941636, 1318 | "focus": 0.027190199767608975 1319 | }, 1320 | "endBinding": { 1321 | "elementId": "hF6QDLelGfOx_MAN9BX_n", 1322 | "gap": 14.059012973304846, 1323 | "focus": -0.01824905115566554 1324 | }, 1325 | "lastCommittedPoint": null, 1326 | "startArrowhead": null, 1327 | "endArrowhead": "arrow", 1328 | "points": [ 1329 | [ 1330 | 0, 1331 | 0 1332 | ], 1333 | [ 1334 | -0.40046478090198434, 1335 | 108.62778767784232 1336 | ] 1337 | ] 1338 | }, 1339 | { 1340 | "type": "text", 1341 | "version": 17, 1342 | "versionNonce": 864685005, 1343 | "isDeleted": false, 1344 | "id": "hJUfyH1IeNV2LMJOF3DOF", 1345 | "fillStyle": "solid", 1346 | "strokeWidth": 2, 1347 | "strokeStyle": "solid", 1348 | "roughness": 1, 1349 | "opacity": 100, 1350 | "angle": 0, 1351 | "x": 2689.6357906032836, 1352 | "y": -266.89703404264526, 1353 | "strokeColor": "#1e1e1e", 1354 | "backgroundColor": "transparent", 1355 | "width": 183.53990173339844, 1356 | "height": 35, 1357 | "seed": 1881810765, 1358 | "groupIds": [], 1359 | "frameId": null, 1360 | "roundness": null, 1361 | "boundElements": [], 1362 | "updated": 1705953142574, 1363 | "link": null, 1364 | "locked": false, 1365 | "fontSize": 28, 1366 | "fontFamily": 1, 1367 | "text": "Render Script", 1368 | "textAlign": "center", 1369 | "verticalAlign": "middle", 1370 | "containerId": "wwcuKOvQLU5eVidKUJEzC", 1371 | "originalText": "Render Script", 1372 | "lineHeight": 1.25, 1373 | "baseline": 29 1374 | }, 1375 | { 1376 | "type": "arrow", 1377 | "version": 1039, 1378 | "versionNonce": 592513581, 1379 | "isDeleted": false, 1380 | "id": "7GArb-2_pFFxm1BxlQtYc", 1381 | "fillStyle": "solid", 1382 | "strokeWidth": 2, 1383 | "strokeStyle": "solid", 1384 | "roughness": 1, 1385 | "opacity": 100, 1386 | "angle": 0, 1387 | "x": 2596.98697501123, 1388 | "y": -255.33753810714762, 1389 | "strokeColor": "#1e1e1e", 1390 | "backgroundColor": "transparent", 1391 | "width": 184.63648098363183, 1392 | "height": 2.3342313692265293, 1393 | "seed": 771298669, 1394 | "groupIds": [], 1395 | "frameId": null, 1396 | "roundness": { 1397 | "type": 2 1398 | }, 1399 | "boundElements": [ 1400 | { 1401 | "type": "text", 1402 | "id": "8PYGtyK3sU7YcbxLHCjFa" 1403 | } 1404 | ], 1405 | "updated": 1705953142574, 1406 | "link": null, 1407 | "locked": false, 1408 | "startBinding": { 1409 | "elementId": "ubSZ2N6MP6huCApVsnVnc", 1410 | "focus": -0.147653438269418, 1411 | "gap": 10.470404579861679 1412 | }, 1413 | "endBinding": { 1414 | "elementId": "_nb_r7uRYplTPJOuWatzB", 1415 | "focus": -0.24328807622845455, 1416 | "gap": 14.605117820533223 1417 | }, 1418 | "lastCommittedPoint": null, 1419 | "startArrowhead": null, 1420 | "endArrowhead": "arrow", 1421 | "points": [ 1422 | [ 1423 | 0, 1424 | 0 1425 | ], 1426 | [ 1427 | 184.63648098363183, 1428 | 2.3342313692265293 1429 | ] 1430 | ] 1431 | }, 1432 | { 1433 | "type": "text", 1434 | "version": 98, 1435 | "versionNonce": 1035844387, 1436 | "isDeleted": false, 1437 | "id": "8PYGtyK3sU7YcbxLHCjFa", 1438 | "fillStyle": "solid", 1439 | "strokeWidth": 2, 1440 | "strokeStyle": "solid", 1441 | "roughness": 1, 1442 | "opacity": 100, 1443 | "angle": 0, 1444 | "x": 2660.9067935907806, 1445 | "y": -457.5147684083752, 1446 | "strokeColor": "#1e1e1e", 1447 | "backgroundColor": "transparent", 1448 | "width": 37.09999084472656, 1449 | "height": 35, 1450 | "seed": 94505891, 1451 | "groupIds": [], 1452 | "frameId": null, 1453 | "roundness": null, 1454 | "boundElements": [], 1455 | "updated": 1705953142574, 1456 | "link": null, 1457 | "locked": false, 1458 | "fontSize": 28, 1459 | "fontFamily": 1, 1460 | "text": "ID", 1461 | "textAlign": "center", 1462 | "verticalAlign": "middle", 1463 | "containerId": "7GArb-2_pFFxm1BxlQtYc", 1464 | "originalText": "ID", 1465 | "lineHeight": 1.25, 1466 | "baseline": 29 1467 | }, 1468 | { 1469 | "type": "arrow", 1470 | "version": 496, 1471 | "versionNonce": 1382628493, 1472 | "isDeleted": false, 1473 | "id": "HT80xschFgla7fqyUWoKp", 1474 | "fillStyle": "solid", 1475 | "strokeWidth": 2, 1476 | "strokeStyle": "solid", 1477 | "roughness": 1, 1478 | "opacity": 100, 1479 | "angle": 0, 1480 | "x": 2416.029268809185, 1481 | "y": -108.15886557435715, 1482 | "strokeColor": "#1e1e1e", 1483 | "backgroundColor": "transparent", 1484 | "width": 3.8822606864068803, 1485 | "height": 107.45483366420731, 1486 | "seed": 1399926179, 1487 | "groupIds": [], 1488 | "frameId": null, 1489 | "roundness": { 1490 | "type": 2 1491 | }, 1492 | "boundElements": [ 1493 | { 1494 | "type": "text", 1495 | "id": "wd4uz3Mn4IcZN0LNJKH_6" 1496 | } 1497 | ], 1498 | "updated": 1705953142574, 1499 | "link": null, 1500 | "locked": false, 1501 | "startBinding": { 1502 | "elementId": "4EKeFcGFkThotE4vNopwJ", 1503 | "focus": -0.019414536078267822, 1504 | "gap": 13.461680487219809 1505 | }, 1506 | "endBinding": { 1507 | "elementId": "ubSZ2N6MP6huCApVsnVnc", 1508 | "focus": 0.0257599247494445, 1509 | "gap": 4.809984359043085 1510 | }, 1511 | "lastCommittedPoint": null, 1512 | "startArrowhead": null, 1513 | "endArrowhead": "arrow", 1514 | "points": [ 1515 | [ 1516 | 0, 1517 | 0 1518 | ], 1519 | [ 1520 | 3.8822606864068803, 1521 | -107.45483366420731 1522 | ] 1523 | ] 1524 | }, 1525 | { 1526 | "type": "text", 1527 | "version": 16, 1528 | "versionNonce": 1363173059, 1529 | "isDeleted": false, 1530 | "id": "wd4uz3Mn4IcZN0LNJKH_6", 1531 | "fillStyle": "solid", 1532 | "strokeWidth": 2, 1533 | "strokeStyle": "solid", 1534 | "roughness": 1, 1535 | "opacity": 100, 1536 | "angle": 0, 1537 | "x": 2349.034432660689, 1538 | "y": -179.3862824064608, 1539 | "strokeColor": "#1e1e1e", 1540 | "backgroundColor": "transparent", 1541 | "width": 137.87193298339844, 1542 | "height": 35, 1543 | "seed": 205138669, 1544 | "groupIds": [], 1545 | "frameId": null, 1546 | "roundness": null, 1547 | "boundElements": [], 1548 | "updated": 1705953142574, 1549 | "link": null, 1550 | "locked": false, 1551 | "fontSize": 28, 1552 | "fontFamily": 1, 1553 | "text": "Component", 1554 | "textAlign": "center", 1555 | "verticalAlign": "middle", 1556 | "containerId": "HT80xschFgla7fqyUWoKp", 1557 | "originalText": "Component", 1558 | "lineHeight": 1.25, 1559 | "baseline": 29 1560 | }, 1561 | { 1562 | "type": "rectangle", 1563 | "version": 531, 1564 | "versionNonce": 1514356045, 1565 | "isDeleted": false, 1566 | "id": "ubSZ2N6MP6huCApVsnVnc", 1567 | "fillStyle": "solid", 1568 | "strokeWidth": 2, 1569 | "strokeStyle": "solid", 1570 | "roughness": 1, 1571 | "opacity": 100, 1572 | "angle": 0, 1573 | "x": 2264.329104580837, 1574 | "y": -284.52336656697935, 1575 | "strokeColor": "#1e1e1e", 1576 | "backgroundColor": "transparent", 1577 | "width": 322.1874658505312, 1578 | "height": 64.09968296937183, 1579 | "seed": 1448699171, 1580 | "groupIds": [], 1581 | "frameId": null, 1582 | "roundness": { 1583 | "type": 3 1584 | }, 1585 | "boundElements": [ 1586 | { 1587 | "type": "text", 1588 | "id": "McsmWH5f54okTS_fbboEk" 1589 | }, 1590 | { 1591 | "id": "HT80xschFgla7fqyUWoKp", 1592 | "type": "arrow" 1593 | }, 1594 | { 1595 | "id": "7GArb-2_pFFxm1BxlQtYc", 1596 | "type": "arrow" 1597 | }, 1598 | { 1599 | "id": "t2I8QqNMfMAmyW1LgOgyu", 1600 | "type": "arrow" 1601 | } 1602 | ], 1603 | "updated": 1705953142574, 1604 | "link": null, 1605 | "locked": false 1606 | }, 1607 | { 1608 | "type": "text", 1609 | "version": 459, 1610 | "versionNonce": 1284753923, 1611 | "isDeleted": false, 1612 | "id": "McsmWH5f54okTS_fbboEk", 1613 | "fillStyle": "solid", 1614 | "strokeWidth": 2, 1615 | "strokeStyle": "solid", 1616 | "roughness": 1, 1617 | "opacity": 100, 1618 | "angle": 0, 1619 | "x": 2336.046868572997, 1620 | "y": -269.97352508229346, 1621 | "strokeColor": "#1e1e1e", 1622 | "backgroundColor": "transparent", 1623 | "width": 178.75193786621094, 1624 | "height": 35, 1625 | "seed": 1826015427, 1626 | "groupIds": [], 1627 | "frameId": null, 1628 | "roundness": null, 1629 | "boundElements": [], 1630 | "updated": 1705953142574, 1631 | "link": null, 1632 | "locked": false, 1633 | "fontSize": 28, 1634 | "fontFamily": 1, 1635 | "text": "Generate ID", 1636 | "textAlign": "center", 1637 | "verticalAlign": "middle", 1638 | "containerId": "ubSZ2N6MP6huCApVsnVnc", 1639 | "originalText": "Generate ID", 1640 | "lineHeight": 1.25, 1641 | "baseline": 29 1642 | }, 1643 | { 1644 | "type": "arrow", 1645 | "version": 1152, 1646 | "versionNonce": 1323830189, 1647 | "isDeleted": false, 1648 | "id": "t2I8QqNMfMAmyW1LgOgyu", 1649 | "fillStyle": "solid", 1650 | "strokeWidth": 2, 1651 | "strokeStyle": "solid", 1652 | "roughness": 1, 1653 | "opacity": 100, 1654 | "angle": 0, 1655 | "x": 2417.03003464953, 1656 | "y": -292.4293564372527, 1657 | "strokeColor": "#1e1e1e", 1658 | "backgroundColor": "transparent", 1659 | "width": 2.733661577116891, 1660 | "height": 87.2857373488593, 1661 | "seed": 1020460237, 1662 | "groupIds": [], 1663 | "frameId": null, 1664 | "roundness": { 1665 | "type": 2 1666 | }, 1667 | "boundElements": [ 1668 | { 1669 | "type": "text", 1670 | "id": "C0mD4SEyKGW9a_PhId7pC" 1671 | } 1672 | ], 1673 | "updated": 1705953142574, 1674 | "link": null, 1675 | "locked": false, 1676 | "startBinding": { 1677 | "elementId": "ubSZ2N6MP6huCApVsnVnc", 1678 | "focus": -0.04405651897308518, 1679 | "gap": 7.905989870273288 1680 | }, 1681 | "endBinding": { 1682 | "elementId": "9HZnp_BH1RhRnqMf1bFom", 1683 | "focus": 0.005654236551808222, 1684 | "gap": 14.559084019986926 1685 | }, 1686 | "lastCommittedPoint": null, 1687 | "startArrowhead": null, 1688 | "endArrowhead": "arrow", 1689 | "points": [ 1690 | [ 1691 | 0, 1692 | 0 1693 | ], 1694 | [ 1695 | -2.733661577116891, 1696 | -87.2857373488593 1697 | ] 1698 | ] 1699 | }, 1700 | { 1701 | "type": "text", 1702 | "version": 101, 1703 | "versionNonce": 2083623331, 1704 | "isDeleted": false, 1705 | "id": "C0mD4SEyKGW9a_PhId7pC", 1706 | "fillStyle": "solid", 1707 | "strokeWidth": 2, 1708 | "strokeStyle": "solid", 1709 | "roughness": 1, 1710 | "opacity": 100, 1711 | "angle": 0, 1712 | "x": 2373.9487101175973, 1713 | "y": -268.28870649285864, 1714 | "strokeColor": "#1e1e1e", 1715 | "backgroundColor": "transparent", 1716 | "width": 37.09999084472656, 1717 | "height": 35, 1718 | "seed": 1481890605, 1719 | "groupIds": [], 1720 | "frameId": null, 1721 | "roundness": null, 1722 | "boundElements": [], 1723 | "updated": 1705953142574, 1724 | "link": null, 1725 | "locked": false, 1726 | "fontSize": 28, 1727 | "fontFamily": 1, 1728 | "text": "ID", 1729 | "textAlign": "center", 1730 | "verticalAlign": "middle", 1731 | "containerId": "t2I8QqNMfMAmyW1LgOgyu", 1732 | "originalText": "ID", 1733 | "lineHeight": 1.25, 1734 | "baseline": 29 1735 | }, 1736 | { 1737 | "type": "rectangle", 1738 | "version": 111, 1739 | "versionNonce": 1185156621, 1740 | "isDeleted": false, 1741 | "id": "_3NVXjblp0lZZ6oWA-Wc5", 1742 | "fillStyle": "hachure", 1743 | "strokeWidth": 1, 1744 | "strokeStyle": "solid", 1745 | "roughness": 1, 1746 | "opacity": 100, 1747 | "angle": 0, 1748 | "x": 1204.1080336544192, 1749 | "y": -657.7546644776069, 1750 | "strokeColor": "#1e1e1e", 1751 | "backgroundColor": "transparent", 1752 | "width": 2027.309179626322, 1753 | "height": 1005.2494937118909, 1754 | "seed": 1481578627, 1755 | "groupIds": [ 1756 | "PjZHqap7DynD__V9uq9g0" 1757 | ], 1758 | "frameId": null, 1759 | "roundness": null, 1760 | "boundElements": [], 1761 | "updated": 1705953142574, 1762 | "link": null, 1763 | "locked": false 1764 | }, 1765 | { 1766 | "type": "text", 1767 | "version": 274, 1768 | "versionNonce": 252551152, 1769 | "isDeleted": false, 1770 | "id": "9QJa7GB0hp_M6hV6Q1vok", 1771 | "fillStyle": "hachure", 1772 | "strokeWidth": 1, 1773 | "strokeStyle": "solid", 1774 | "roughness": 1, 1775 | "opacity": 100, 1776 | "angle": 0, 1777 | "x": 1774.3341719112059, 1778 | "y": -746.5845642928854, 1779 | "strokeColor": "#1e1e1e", 1780 | "backgroundColor": "transparent", 1781 | "width": 928.8231201171875, 1782 | "height": 54.348367952806726, 1783 | "seed": 744728451, 1784 | "groupIds": [ 1785 | "PjZHqap7DynD__V9uq9g0" 1786 | ], 1787 | "frameId": null, 1788 | "roundness": null, 1789 | "boundElements": [], 1790 | "updated": 1706203062497, 1791 | "link": null, 1792 | "locked": false, 1793 | "fontSize": 43.47869436224538, 1794 | "fontFamily": 1, 1795 | "text": "Django Template React Component Rendrer", 1796 | "textAlign": "left", 1797 | "verticalAlign": "top", 1798 | "containerId": null, 1799 | "originalText": "Django Template React Component Rendrer", 1800 | "lineHeight": 1.25, 1801 | "baseline": 38 1802 | }, 1803 | { 1804 | "type": "rectangle", 1805 | "version": 276, 1806 | "versionNonce": 1081132141, 1807 | "isDeleted": false, 1808 | "id": "Gf7lJOJJVdDqQqG8M_Y0Z", 1809 | "fillStyle": "hachure", 1810 | "strokeWidth": 1, 1811 | "strokeStyle": "solid", 1812 | "roughness": 1, 1813 | "opacity": 100, 1814 | "angle": 0, 1815 | "x": 1203.6524540246241, 1816 | "y": -773.9353664329899, 1817 | "strokeColor": "#1e1e1e", 1818 | "backgroundColor": "transparent", 1819 | "width": 2027.309179626322, 1820 | "height": 1122.0724207949334, 1821 | "seed": 1489614275, 1822 | "groupIds": [ 1823 | "PjZHqap7DynD__V9uq9g0" 1824 | ], 1825 | "frameId": null, 1826 | "roundness": null, 1827 | "boundElements": [], 1828 | "updated": 1705953142574, 1829 | "link": null, 1830 | "locked": false 1831 | }, 1832 | { 1833 | "type": "rectangle", 1834 | "version": 324, 1835 | "versionNonce": 2098953891, 1836 | "isDeleted": false, 1837 | "id": "xql8itph-bpVfG69zpkqX", 1838 | "fillStyle": "hachure", 1839 | "strokeWidth": 1, 1840 | "strokeStyle": "solid", 1841 | "roughness": 1, 1842 | "opacity": 100, 1843 | "angle": 0, 1844 | "x": 1204.6524540246241, 1845 | "y": -874.3457398838482, 1846 | "strokeColor": "#1e1e1e", 1847 | "backgroundColor": "transparent", 1848 | "width": 2027.309179626322, 1849 | "height": 1222.4827942457916, 1850 | "seed": 322433059, 1851 | "groupIds": [ 1852 | "PjZHqap7DynD__V9uq9g0" 1853 | ], 1854 | "frameId": null, 1855 | "roundness": null, 1856 | "boundElements": [], 1857 | "updated": 1705953148873, 1858 | "link": null, 1859 | "locked": false 1860 | }, 1861 | { 1862 | "type": "text", 1863 | "version": 403, 1864 | "versionNonce": 1779472144, 1865 | "isDeleted": false, 1866 | "id": "YjuZ6AYLjzyEXcL4wDJpu", 1867 | "fillStyle": "hachure", 1868 | "strokeWidth": 1, 1869 | "strokeStyle": "solid", 1870 | "roughness": 1, 1871 | "opacity": 100, 1872 | "angle": 0, 1873 | "x": 1974.4020545566848, 1874 | "y": -851.5520467837131, 1875 | "strokeColor": "#1e1e1e", 1876 | "backgroundColor": "transparent", 1877 | "width": 416.26861572265625, 1878 | "height": 54.348367952806726, 1879 | "seed": 1243763651, 1880 | "groupIds": [ 1881 | "SzSYyelrDJDtMBFtnGSLO" 1882 | ], 1883 | "frameId": null, 1884 | "roundness": null, 1885 | "boundElements": [], 1886 | "updated": 1706203062498, 1887 | "link": null, 1888 | "locked": false, 1889 | "fontSize": 43.47869436224538, 1890 | "fontFamily": 1, 1891 | "text": "Data Flow Diagram", 1892 | "textAlign": "left", 1893 | "verticalAlign": "top", 1894 | "containerId": null, 1895 | "originalText": "Data Flow Diagram", 1896 | "lineHeight": 1.25, 1897 | "baseline": 38 1898 | } 1899 | ], 1900 | "appState": { 1901 | "gridSize": null, 1902 | "viewBackgroundColor": "#ffffff" 1903 | }, 1904 | "files": {} 1905 | } --------------------------------------------------------------------------------