Full Code of lidojs/canva-clone for AI

master 195c80465814 cached
55 files
294.7 KB
114.2k tokens
18 symbols
1 requests
Download .txt
Showing preview only (313K chars total). Download the full file or copy to clipboard to get everything.
Repository: lidojs/canva-clone
Branch: master
Commit: 195c80465814
Files: 55
Total size: 294.7 KB

Directory structure:
gitextract_2xb0w4ev/

├── README.md
├── index.html
├── package.json
├── src/
│   ├── assets/
│   │   └── .gitkeep
│   ├── constant/
│   │   ├── data.ts
│   │   └── text-effects.ts
│   ├── features/
│   │   └── design/
│   │       ├── components/
│   │       │   ├── LidoJSEditor.tsx
│   │       │   ├── editor-content/
│   │       │   │   ├── EditorContent.tsx
│   │       │   │   └── index.ts
│   │       │   ├── editor-header/
│   │       │   │   ├── EditorHeader.tsx
│   │       │   │   └── index.ts
│   │       │   ├── index.ts
│   │       │   ├── layer-settings/
│   │       │   │   ├── LayerSettings.tsx
│   │       │   │   └── index.ts
│   │       │   ├── preview/
│   │       │   │   ├── PreviewModal.tsx
│   │       │   │   └── index.ts
│   │       │   ├── sidebar/
│   │       │   │   ├── DrawContent.tsx
│   │       │   │   ├── FrameContent.tsx
│   │       │   │   ├── GraphicContent.tsx
│   │       │   │   ├── IframeContent.tsx
│   │       │   │   ├── ImageContent.tsx
│   │       │   │   ├── Photo.tsx
│   │       │   │   ├── QrCodeContent.tsx
│   │       │   │   ├── ShapeContent.tsx
│   │       │   │   ├── Sidebar.tsx
│   │       │   │   ├── TableContent.tsx
│   │       │   │   ├── TemplateContent.tsx
│   │       │   │   ├── TextContent.tsx
│   │       │   │   ├── UploadContent.tsx
│   │       │   │   ├── VideoContent.tsx
│   │       │   │   └── index.ts
│   │       │   └── tabs/
│   │       │       ├── TabList.tsx
│   │       │       └── index.ts
│   │       ├── config/
│   │       │   ├── iframe.tsx
│   │       │   ├── line.tsx
│   │       │   ├── qrCode.tsx
│   │       │   └── shape.tsx
│   │       └── pages/
│   │           ├── DesignPage.tsx
│   │           └── index.ts
│   ├── main.tsx
│   ├── pages/
│   │   └── Main.tsx
│   ├── shared/
│   │   ├── components/
│   │   │   ├── index.ts
│   │   │   └── masonry/
│   │   │       ├── Masonry.tsx
│   │   │       └── index.ts
│   │   ├── icons/
│   │   │   └── pencil/
│   │   │       ├── Highlighter.tsx
│   │   │       ├── Marker.tsx
│   │   │       └── Pencil.tsx
│   │   └── theme/
│   │       ├── index.ts
│   │       ├── palette.ts
│   │       └── theme.ts
│   ├── styles.css
│   └── utils/
│       ├── download.ts
│       └── thumbnail.ts
├── tsconfig.json
└── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: README.md
================================================
<img width="960" alt="Screenshot 2023-04-22 101234" src="https://github.com/lidojs/canva-clone/assets/19285404/06249d78-3e6c-45a0-b14a-bd73c186fd84" />

# LidoJS - Powerful Design Tool for Creatives

LidoJS is a simple and user-friendly graphic design tool, that helps you create great designs quickly. Whether you need presentations, marketing materials, or social media graphics, LidoJS has what you need to bring your ideas to life.
The tool is developed from scratch with ReactJS, so it's easy to upgrade by yourself.
This code will show you how to integrate LidoJS into an application to make an online design editor application.

Explore the platform at [https://lidojs.com](https://lidojs.com) or meet us at [https://discord.gg/mBj7fqKpEM](https://discord.gg/mBj7fqKpEM).

## **Overview**

LidoJS makes designing simple and fun, offering a variety of tools to help designers, marketers, and content creators reach their goals. From creating slides to making graphics, LidoJS makes the whole process easier.

## **Demo**

[https://demo.lidojs.com](https://demo.lidojs.com).

After creating designs, you can download them in various formats, including PDF, PNG, and JPG. Download feature does not include in the packages, but you can see the output file below

[Open PDF file](https://s3.us-east-2.amazonaws.com/lidojs.com/output-from-templates.pdf)

## **Key Features**
- **Drag-and-Drop Interface:** Simplify the design process with an intuitive editor.
- **Customizable Templates:** Access a vast collection of pre-designed templates for presentations, posters, and social media.
- **Advanced Editing Tools:** Fine-tune your designs with precise editing options, including layering, masking, painting, shapes, frames, and color adjustments.
- **Export Options:** Download designs in various formats suitable for both print (PDF, PNG, JPG) and digital platforms (SVG, WebP).
Technical Highlights

## **Technical Highlights**
- **Proven Technology:** LidoJS uses the same advanced technical solutions as Canva, ensuring a reliable and high-performance design experience.
- **Built from Scratch:** Core features are developed entirely with React.js, without relying on third-party frameworks.
- **Unlimited Customization:** Enjoy total creative freedom with the ability to customize any element to fit your specific needs.

## **Usage Purpose**
LidoJS works for many design tasks, such as:

- **Presentations:** Craft professional slide decks with ease using customizable templates.
- **Marketing Campaigns:** Create eye-catching promotional materials for ads and social media.
- **Branding:** Develop cohesive brand assets such as logos and banners.
- **Content Creation:** Design visually compelling graphics for blogs, websites, and newsletters.

## **Trusted by Leading Brands**
LidoJS is a preferred design tool for top brands and organizations, including:

- **Sendsteps:** Engaging presentation solutions.
- **Menuzen:** Dynamic digital menu creation.
- **Designstripe:** Simplified design experiences.
- **Momentumstack:** Innovative technology and business services.

Start designing with LidoJS and experience the future of creative possibilities. Visit [https://lidojs.com](https://lidojs.com) to learn more and get started today.


## Disclaimer

This source code is developed entirely by our team from scratch. We have not used any third-party resources that require licensing. 
Our product is designed to assist users in developing design applications efficiently. However, we do not guarantee any specific outcomes or take responsibility for how this software is used.  
Use this code at your own risk. We are not liable for any damages or issues arising from its use.




================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>LidoJS Design Editor</title>
  <meta
    content="Canva clone, design editor tool, graphic editor tool, image editor tool."
    name="description"
  />
  <meta
    content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content"
    name="viewport">
  <meta content="website" property="og:type" />
  <meta content="LidoJS Design Editor" property="og:title" />
  <meta
    content="Canva clone, design editor tool, graphic editor tool, image editor tool."
    property="og:description"
  />
  <link href="https://fonts.googleapis.com" rel="preconnect">
  <link crossorigin href="https://fonts.gstatic.com" rel="preconnect">
  <link
    crossorigin
    href="https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,300;0,400;0,600;0,700;1,400;1,600;1,700&display=swap"
    rel="stylesheet">
</head>
<body>
<style>
  html, body {
    margin: 0;
    font-family: 'Nunito', sans-serif;
    color: #5E6278;
    font-size: 14px;
  }

  html,
  body {
    overflow-wrap: break-word;
    -webkit-hyphens: none;
    hyphens: none;
    word-break: break-word;
    margin: 0;
    display: flex;
    flex-direction: column;
    height: unset;
    overscroll-behavior: none;
  }

  blockquote,
  dl,
  dd,
  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  hr,
  figure,
  p,
  pre {
    margin: 0;
  }

  button {
    background-color: transparent;
    background-image: none;
  }

  fieldset {
    margin: 0;
    padding: 0;
  }

  ol,
  ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  body {
    font-family: inherit;
    line-height: inherit;
  }

  *,
  ::before,
  ::after {
    box-sizing: border-box; /* 1 */
    border-width: 0; /* 2 */
    border-style: solid; /* 2 */
    border-color: currentColor; /* 2 */
    outline: none;
  }

  hr {
    border-top-width: 1px;
  }

  img {
    border-style: solid;
  }

  textarea {
    resize: vertical;
  }

  input::placeholder,
  textarea::placeholder {
    opacity: 1;
    color: #a1a1aa;
  }

  button,
  [role='button'] {
    cursor: pointer;
  }

  table {
    border-collapse: collapse;
  }

  a {
    color: inherit;
    text-decoration: inherit;
  }

  button,
  input,
  optgroup,
  select,
  textarea {
    padding: 0;
    line-height: inherit;
    color: inherit;
  }

  pre,
  code,
  kbd,
  samp {
    font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
  }

  img,
  svg,
  video,
  canvas,
  audio,
  iframe,
  embed,
  object {
    display: block;
    vertical-align: middle;
  }

  img,
  video {
    max-width: 100%;
    height: auto;
  }
</style>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./index.tsx" type="module"></script>
</body>
<% if(process.env.NODE_ENV !== 'development'){ %>
<script>
  window["_fs_host"] = "fullstory.com";
  window["_fs_script"] = "edge.fullstory.com/s/fs.js";
  window["_fs_org"] = "o-1PBR16-na1";
  window["_fs_namespace"] = "FS";
  (function(m, n, e, t, l, o, g, y) {
    if (e in m) {
      if (m.console && m.console.log) {
        m.console.log("FullStory namespace conflict. Please set window[\"_fs_namespace\"].");
      }
      return;
    }
    g = m[e] = function(a, b, s) {
      g.q ? g.q.push([a, b, s]) : g._api(a, b, s);
    };
    g.q = [];
    o = n.createElement(t);
    o.async = 1;
    o.crossOrigin = "anonymous";
    o.src = "https://" + _fs_script;
    y = n.getElementsByTagName(t)[0];
    y.parentNode.insertBefore(o, y);
    g.identify = function(i, v, s) {
      g(l, { uid: i }, s);
      if (v) g(l, v, s);
    };
    g.setUserVars = function(v, s) {
      g(l, v, s);
    };
    g.event = function(i, v, s) {
      g("event", { n: i, p: v }, s);
    };
    g.anonymize = function() {
      g.identify(!!0);
    };
    g.shutdown = function() {
      g("rec", !1);
    };
    g.restart = function() {
      g("rec", !0);
    };
    g.log = function(a, b) {
      g("log", [a, b]);
    };
    g.consent = function(a) {
      g("consent", !arguments.length || a);
    };
    g.identifyAccount = function(i, v) {
      o = "account";
      v = v || {};
      v.acctId = i;
      g(o, v);
    };
    g.clearUserCookie = function() {
    };
    g.setVars = function(n, p) {
      g("setVars", [n, p]);
    };
    g._w = {};
    y = "XMLHttpRequest";
    g._w[y] = m[y];
    y = "fetch";
    g._w[y] = m[y];
    if (m[y]) m[y] = function() {
      return g._w[y].apply(this, arguments);
    };
    g._v = "1.3.0";
  })(window, document, window["_fs_namespace"], "script", "user");
</script>
<% } %>
</html>


================================================
FILE: package.json
================================================
{
  "name": "@lidojs/react",
  "version": "2.0.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "@duyank/icons": "^0.1.2",
    "@emotion/react": "11.14.0",
    "@emotion/styled": "11.14.0",
    "@lidojs/color-picker": "^2.0.0",
    "@lidojs/design-core": "^2.0.0",
    "@lidojs/design-editor": "^2.0.0",
    "@lidojs/design-layers": "^2.0.0",
    "@lidojs/design-screen": "^2.0.0",
    "@lidojs/design-utils": "^2.0.0",
    "@lidojs/draw": "^2.0.0",
    "@lidojs/text-editor": "^2.0.0",
    "@mui/material": "^6.4.5",
    "axios": "^1.7.9",
    "lodash": "^4.17.21",
    "react": "^18.3.1",
    "react-device-detect": "^2.2.3",
    "react-dom": "catalog:",
    "react-responsive-masonry": "^2.7.1",
    "react-use": "^17.6.0",
    "uuid": "^11.0.5"
  },
  "devDependencies": {
    "@types/react": "^18.3.1",
    "@types/react-dom": "^18.3.5",
    "prop-types": "^15.8.1",
    "vite-bundle-analyzer": "^0.17.0",
    "vite-plugin-environment": "^1.1.3"
  }
}


================================================
FILE: src/assets/.gitkeep
================================================


================================================
FILE: src/constant/data.ts
================================================
export const data = [
  {
    layers: {
      ROOT: {
        type: { resolvedName: 'RootLayer' },
        props: {
          boxSize: { width: 1640, height: 924 },
          position: { x: 0, y: 0 },
          rotate: 0,
          color: 'rgb(255, 255, 255)',
          image: null,
        },
        locked: false,
        child: [
          'd80fa039-b645-4678-89f2-fc0336958989',
          '851673f7-8917-4b3e-95d0-3a81b6f638bb',
          'af7b69ea-3060-46ff-914e-9833d66ddc1d',
          '09a24f96-90bd-4e0d-b610-47d23de11c2b',
          '2096d6ab-d78e-4acf-b509-170a3e6b97f5',
        ],
        parent: null,
      },
      'd80fa039-b645-4678-89f2-fc0336958989': {
        type: { resolvedName: 'ShapeLayer' },
        props: {
          shape: 'rectangle',
          position: { x: -41, y: 652 },
          boxSize: { width: 1766, height: 296, x: -41, y: 652 },
          rotate: 0,
          color: 'rgb(253, 235, 207)',
        },
        locked: false,
        child: [],
        parent: 'ROOT',
      },
      '851673f7-8917-4b3e-95d0-3a81b6f638bb': {
        type: { resolvedName: 'TextLayer' },
        props: {
          doc: {
            type: 'doc',
            content: [
              {
                type: 'paragraph',
                attrs: {
                  textAlign: 'center',
                  color: 'rgb(0, 0, 0)',
                  fontFamily: 'Agdasima',
                  fontSize: '42px',
                  lineHeight: 1.03,
                  letterSpacing: 0,
                  textTransform: '',
                  marginLeft: null,
                  indent: 0,
                  listType: '',
                },
                content: [
                  {
                    type: 'text',
                    marks: [
                      { type: 'color', attrs: { color: 'rgb(0, 0, 0)' } },
                    ],
                    text: 'LIDOJS',
                  },
                ],
              },
              {
                type: 'paragraph',
                attrs: {
                  textAlign: 'center',
                  color: 'rgb(0, 0, 0)',
                  fontFamily: 'Agdasima',
                  fontSize: '42px',
                  lineHeight: 1.03,
                  letterSpacing: 0,
                  textTransform: '',
                  marginLeft: null,
                  indent: 0,
                  listType: '',
                },
                content: [
                  {
                    type: 'text',
                    marks: [
                      { type: 'color', attrs: { color: 'rgb(0, 0, 0)' } },
                    ],
                    text: 'DESIGN EDITOR',
                  },
                ],
              },
            ],
          },
          position: { x: 528.5031977930256, y: 276.6364429758855 },
          boxSize: {
            width: 536.3009995574356,
            height: 144.98254063816975,
            x: 523.1390728476821,
            y: 259.9867549668875,
          },
          scale: 1.6664659843467788,
          rotate: 0,
          fonts: [
            {
              name: 'Agdasima',
              fonts: [
                {
                  urls: [
                    'https://fonts.gstatic.com/s/agdasima/v4/PN_zRfyxp2f1fUCgAMg6rzjb_-Da.ttf',
                  ],
                },
              ],
            },
          ],
          colors: ['rgb(0, 0, 0)'],
          fontSizes: [42],
        },
        locked: false,
        child: [],
        parent: 'ROOT',
      },
      'af7b69ea-3060-46ff-914e-9833d66ddc1d': {
        type: { resolvedName: 'TextLayer' },
        props: {
          doc: {
            type: 'doc',
            content: [
              {
                type: 'paragraph',
                attrs: {
                  textAlign: 'center',
                  color: 'rgb(0, 0, 0)',
                  fontFamily: 'Oswald',
                  fontSize: '18px',
                  lineHeight: '1.4',
                  letterSpacing: 0,
                  textTransform: 'uppercase',
                  marginLeft: null,
                  indent: 0,
                  listType: '',
                },
                content: [
                  {
                    type: 'text',
                    marks: [
                      { type: 'bold' },
                      { type: 'color', attrs: { color: 'rgb(0, 0, 0)' } },
                    ],
                    text: 'DEVELOPED WITH REACTJS',
                  },
                ],
              },
            ],
          },
          position: { x: 665.6953642384103, y: 440.2251655629139 },
          boxSize: {
            width: 261.91666666666606,
            height: 25,
            x: 660.2715231788078,
            y: 440.2251655629139,
          },
          scale: 1,
          rotate: 0,
          fonts: [
            {
              name: 'Oswald',
              fonts: [
                {
                  style: 'Bold',
                  urls: [
                    'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Oswald/Oswald-Bold.woff2',
                  ],
                },
                {
                  urls: [
                    'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Oswald/Oswald-Regular.woff2',
                  ],
                },
              ],
            },
          ],
          colors: ['rgb(0, 0, 0)'],
          fontSizes: [18],
          effect: null,
        },
        locked: false,
        child: [],
        parent: 'ROOT',
      },
      '09a24f96-90bd-4e0d-b610-47d23de11c2b': {
        type: { resolvedName: 'TextLayer' },
        props: {
          doc: {
            type: 'doc',
            content: [
              {
                type: 'paragraph',
                attrs: {
                  textAlign: 'center',
                  color: 'rgb(0, 0, 0)',
                  fontFamily: 'Oswald',
                  fontSize: '18px',
                  lineHeight: '1.4',
                  letterSpacing: 0,
                  textTransform: 'uppercase',
                  marginLeft: null,
                  indent: 0,
                  listType: '',
                },
                content: [
                  {
                    type: 'text',
                    marks: [
                      { type: 'bold' },
                      { type: 'color', attrs: { color: 'rgb(0, 0, 0)' } },
                    ],
                    text: 'DISCORD: https://discord.gg/mBj7fqKpEM',
                  },
                ],
              },
            ],
          },
          position: { x: 587.4569536423838, y: 839.8609271523178 },
          boxSize: {
            width: 379.3868653421627,
            height: 25,
            x: 587.4569536423838,
            y: 839.8609271523178,
          },
          scale: 1,
          rotate: 0,
          fonts: [
            {
              name: 'Oswald',
              fonts: [
                {
                  style: 'Bold',
                  urls: [
                    'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Oswald/Oswald-Bold.woff2',
                  ],
                },
                {
                  urls: [
                    'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Oswald/Oswald-Regular.woff2',
                  ],
                },
              ],
            },
          ],
          colors: ['rgb(0, 0, 0)'],
          fontSizes: [18],
          effect: null,
        },
        locked: false,
        child: [],
        parent: 'ROOT',
      },
      '2096d6ab-d78e-4acf-b509-170a3e6b97f5': {
        type: { resolvedName: 'TextLayer' },
        props: {
          doc: {
            type: 'doc',
            content: [
              {
                type: 'paragraph',
                attrs: {
                  textAlign: 'center',
                  color: 'rgb(0, 0, 0)',
                  fontFamily: 'Oswald',
                  fontSize: '18px',
                  lineHeight: '1.4',
                  letterSpacing: 0,
                  textTransform: 'uppercase',
                  marginLeft: null,
                  indent: 0,
                  listType: '',
                },
                content: [
                  {
                    type: 'text',
                    marks: [
                      { type: 'bold' },
                      { type: 'color', attrs: { color: 'rgb(0, 0, 0)' } },
                    ],
                    text: 'CONTACT: DUYANH980@GMAIL.COM',
                  },
                ],
              },
            ],
          },
          position: { x: 634.1125827814567, y: 810.4900662251654 },
          boxSize: {
            width: 293.4994481236197,
            height: 25,
            x: 634.1125827814567,
            y: 810.4900662251654,
          },
          scale: 1,
          rotate: 0,
          fonts: [
            {
              name: 'Oswald',
              fonts: [
                {
                  style: 'Bold',
                  urls: [
                    'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Oswald/Oswald-Bold.woff2',
                  ],
                },
                {
                  urls: [
                    'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Oswald/Oswald-Regular.woff2',
                  ],
                },
              ],
            },
          ],
          colors: ['rgb(0, 0, 0)'],
          fontSizes: [18],
          effect: null,
        },
        locked: false,
        child: [],
        parent: 'ROOT',
      },
    },
  },
];


================================================
FILE: src/constant/text-effects.ts
================================================
export const addAHeading = {
  rootId: 'f2d33316-8857-4496-a0c7-3dcc9c4ff981',
  layers: {
    'f2d33316-8857-4496-a0c7-3dcc9c4ff981': {
      type: { resolvedName: 'TextLayer' },
      props: {
        doc: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Roboto',
                fontSize: '68px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: '',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'bold',
                    },
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(0, 0, 0)',
                      },
                    },
                  ],
                  text: 'Add a heading',
                },
              ],
            },
          ],
        },
        position: { x: 525.12102340009, y: 261.582179409994 },
        boxSize: {
          width: 536.3009995574356,
          height: 95,
          x: 523.1390728476821,
          y: 259.9867549668875,
        },
        scale: 1,
        rotate: 0,
        fonts: [
          {
            name: 'Roboto',
            fonts: [
              {
                style: 'Bold',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                style: 'Bold_Italic',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                style: 'Bold',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
              {
                style: 'Italic',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
              {
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
            ],
          },
        ],
        colors: ['rgb(0, 0, 0)'],
        fontSizes: [68],
      },
      locked: false,
      child: [],
      parent: 'ROOT',
    },
  },
};

export const addASubheading = {
  rootId: '9cc89a8c-49d5-4f90-9964-f65bbe90db92',
  layers: {
    '9cc89a8c-49d5-4f90-9964-f65bbe90db92': {
      type: { resolvedName: 'TextLayer' },
      props: {
        doc: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Roboto',
                fontSize: '38px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: '',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'bold',
                    },
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(0, 0, 0)',
                      },
                    },
                  ],
                  text: 'Add a subheading',
                },
              ],
            },
          ],
        },
        position: { x: 519.12102340009, y: 365.582179409994 },
        boxSize: {
          width: 536.3009995574356,
          height: 53,
          x: 523.1390728476821,
          y: 259.9867549668875,
        },
        scale: 1,
        rotate: 0,
        fonts: [
          {
            name: 'Roboto',
            fonts: [
              {
                style: 'Bold',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                style: 'Bold_Italic',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                style: 'Bold',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
              {
                style: 'Italic',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
              {
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
            ],
          },
        ],
        colors: ['rgb(0, 0, 0)'],
        fontSizes: [38],
      },
      locked: false,
      child: [],
      parent: 'ROOT',
    },
  },
};

export const addABodyText = {
  rootId: '3cace409-216f-4e0e-9449-9248901c8c94',
  layers: {
    '3cace409-216f-4e0e-9449-9248901c8c94': {
      type: { resolvedName: 'TextLayer' },
      props: {
        doc: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Roboto',
                fontSize: '26px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: '',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(0, 0, 0)',
                      },
                    },
                  ],
                  text: 'Add a little bit of body text',
                },
              ],
            },
          ],
        },
        position: { x: 508.99988653474736, y: 434.903672486454 },
        boxSize: {
          width: 536.3009995574356,
          height: 36,
          x: 523.1390728476821,
          y: 259.9867549668875,
        },
        scale: 1,
        rotate: 0,
        fonts: [
          {
            name: 'Roboto',
            fonts: [
              {
                style: 'Bold',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                style: 'Bold_Italic',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                style: 'Bold',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Bold.woff2',
                ],
              },
              {
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
              {
                style: 'Italic',
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
              {
                urls: [
                  'https://lidojs-fonts.s3.us-east-2.amazonaws.com/Roboto/Roboto-Regular.woff2',
                ],
              },
            ],
          },
        ],
        colors: ['rgb(0, 0, 0)'],
        fontSizes: [26],
      },
      locked: false,
      child: [],
      parent: 'ROOT',
    },
  },
};


================================================
FILE: src/features/design/components/LidoJSEditor.tsx
================================================
'use client';

import type { FontData } from '@lidojs/design-core';
import { Editor, type GetFontQuery, PageControl } from '@lidojs/design-editor';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { EditorContent } from './editor-content';
import { EditorHeader } from './editor-header';
import { LayerSettings } from './layer-settings';
import { PreviewModal } from './preview';
import { Sidebar } from './sidebar';

export const LidoJSEditor = ({
  googleFontList,
}: { googleFontList: FontData[] }) => {
  const leftSidebarRef = useRef<HTMLDivElement>(null);
  const [openPreview, setOpenPreview] = useState(false);

  const getFonts = useCallback(
    async (query: GetFontQuery) => {
      return googleFontList
        .filter((i) => !query.q || i.name.toLowerCase().includes(query.q))
        .slice(
          Number.parseInt(query?.offset ?? '', 10),
          Number.parseInt(query?.offset ?? '', 10) +
            Number.parseInt(query?.limit || '', 10),
        );
    },
    [googleFontList],
  );
  const [viewPortHeight, setViewPortHeight] = useState<number>();
  useEffect(() => {
    if (!window) return;
    const windowHeight = () => {
      setViewPortHeight(window.innerHeight);
    };
    window.addEventListener('resize', windowHeight);
    windowHeight();
    return () => {
      window.removeEventListener('resize', windowHeight);
    };
  }, []);
  const config = useMemo(
    () => ({
      assetPath: './assets',
      frame: {
        defaultImage: {
          url: './assets/images/frame-placeholder.png',
          width: 1200,
          height: 800,
        },
      },
    }),
    [],
  );

  const uploadImage = async (file: File) => {
    // TODO: to integrate with image manipulation then need update this
    return new Promise<{ url: string; thumb: string }>((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        resolve({
          url: reader.result as string,
          thumb: reader.result as string,
        });
      };
      reader.onerror = reject;
    });
  };

  return (
    <Editor config={config} getFonts={getFonts} uploadImage={uploadImage}>
      <div
        css={{
          display: 'flex',
          flexDirection: 'column',
          width: '100vw',
          height: '100vh',
          maxHeight: viewPortHeight ? `${viewPortHeight}px` : 'auto',
        }}
      >
        <EditorHeader openPreview={() => setOpenPreview(true)} />
        {openPreview && <PreviewModal onClose={() => setOpenPreview(false)} />}
        <div
          css={{
            display: 'flex',
            flexDirection: 'row',
            flex: 'auto',
            overflow: 'auto',
            background: '#EBECF0',
            '@media (max-width: 900px)': {
              flexDirection: 'column-reverse',
            },
          }}
        >
          <div
            ref={leftSidebarRef}
            css={{
              display: 'flex',
              background: 'white',
            }}
          >
            <Sidebar />
          </div>
          <div
            css={{
              flexGrow: 1,
              position: 'relative',
              display: 'flex',
              flexDirection: 'column',
              overflow: 'auto',
            }}
          >
            <LayerSettings />
            <div
              css={{
                flexGrow: 1,
                overflow: 'auto',
                display: 'flex',
                flexDirection: 'column',
              }}
            >
              <EditorContent />
            </div>
            <div
              css={{
                height: 40,
                background: '#fff',
                borderTop: '1px solid rgba(57,76,96,.15)',
                display: 'grid',
                alignItems: 'center',
                flexShrink: 0,
                '@media (max-width: 900px)': {
                  display: 'none',
                },
              }}
            >
              <PageControl />
            </div>
          </div>
        </div>
      </div>
    </Editor>
  );
};


================================================
FILE: src/features/design/components/editor-content/EditorContent.tsx
================================================
import { DesignFrame } from '@lidojs/design-editor';
import { data } from '../../../../constant/data';

export const EditorContent = () => {
  return <DesignFrame data={data} />;
};


================================================
FILE: src/features/design/components/editor-content/index.ts
================================================
export * from './EditorContent';


================================================
FILE: src/features/design/components/editor-header/EditorHeader.tsx
================================================
import ArrowClockwiseIcon from '@duyank/icons/regular/ArrowClockwise';
import ArrowCounterClockwiseIcon from '@duyank/icons/regular/ArrowCounterClockwise';
import GithubLogoIcon from '@duyank/icons/regular/GithubLogo';
import PlayCircleIcon from '@duyank/icons/regular/PlayCircle';
import { useEditor } from '@lidojs/design-editor';
import {
  type ChangeEvent,
  type ForwardRefRenderFunction,
  forwardRef,
  useRef,
} from 'react';
import { downloadObjectAsJson } from '../../../../utils/download';

interface HeaderLayoutProps {
  openPreview: () => void;
}

const EditorHeaderForwardRef: ForwardRefRenderFunction<
  HTMLDivElement,
  HeaderLayoutProps
> = ({ openPreview }, ref) => {
  const uploadRef = useRef<HTMLInputElement>(null);
  const { actions, query } = useEditor();
  const handleExport = () => {
    downloadObjectAsJson('file', query.serialize());
  };

  const handleImport = (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onload = () => {
        const fileContent = JSON.parse(reader.result as string);
        actions.setData(fileContent);
      };
      reader.readAsText(file);
      e.target.value = '';
    }
  };
  return (
    <div
      ref={ref}
      css={{
        background: '#1E1E2D',
        padding: '12px 32px',
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center',
        '@media (max-width: 900px)': {
          padding: 12,
        },
      }}
    >
      <div
        css={{
          color: '#3d8eff',
          fontSize: 36,
        }}
      >
        <div
          css={{ color: 'white', height: 42, paddingTop: 6, paddingBottom: 6 }}
        >
          <a href="https://lidojs.com" rel="noreferrer" target="_blank">
            <img
              alt="LidoJs"
              css={{ maxHeight: '100%' }}
              src="./assets/logo.png"
            />
          </a>
        </div>
      </div>
      <div css={{ display: 'flex', alignItems: 'center', gap: 32 }}>
        <div css={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          <div
            css={{
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              color: '#fff',
              background: '#3a3a4c',
              width: 36,
              height: 36,
              borderRadius: '50%',
              cursor: query.history.canUndo() ? 'pointer' : undefined,
              opacity: query.history.canUndo() ? 1 : 0.5,
              ':hover': {
                background: query.history.canUndo()
                  ? 'rgba(58,58,76,0.5)'
                  : undefined,
              },
            }}
            onClick={actions.history.undo}
          >
            <ArrowCounterClockwiseIcon />
          </div>
          <div
            css={{
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              color: '#fff',
              background: '#3a3a4c',
              width: 36,
              height: 36,
              borderRadius: '50%',
              cursor: query.history.canRedo() ? 'pointer' : undefined,
              opacity: query.history.canRedo() ? 1 : 0.5,
              ':hover': {
                background: query.history.canRedo()
                  ? 'rgba(58,58,76,0.5)'
                  : undefined,
              },
            }}
            onClick={actions.history.redo}
          >
            <ArrowClockwiseIcon />
          </div>
        </div>
        <a
          href="https://github.com/lidojs/canva-clone"
          rel="noreferrer"
          target="_blank"
        >
          <span
            css={{
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
              color: '#fff',
              background: '#3a3a4c',
              width: 36,
              height: 36,
              borderRadius: '50%',
              cursor: 'pointer',
              ':hover': {
                background: 'rgba(58,58,76,0.5)',
              },
            }}
          >
            <GithubLogoIcon />
          </span>
        </a>
        <div
          css={{
            cursor: 'pointer',
            color: '#fff',
            fontWeight: 700,
            ':hover': {
              textDecoration: 'underline',
            },
            '@media (max-width: 900px)': {
              display: 'none',
            },
          }}
          onClick={() => uploadRef.current?.click()}
        >
          <input
            ref={uploadRef}
            accept="application/json"
            css={{ display: 'none' }}
            type="file"
            onChange={handleImport}
          />
          Import
        </div>
        <div
          css={{
            cursor: 'pointer',
            color: '#fff',
            fontWeight: 700,
            ':hover': {
              textDecoration: 'underline',
            },
            '@media (max-width: 900px)': {
              display: 'none',
            },
          }}
          onClick={() => handleExport()}
        >
          Export
        </div>
        <div
          css={{
            display: 'flex',
            alignItems: 'center',
            color: '#fff',
            lineHeight: 1,
            background: '#3a3a4c',
            padding: '8px 14px',
            borderRadius: 8,
            cursor: 'pointer',
            ':hover': {
              background: 'rgba(58,58,76,0.5)',
            },
            '@media (max-width: 900px)': {
              display: 'none',
            },
          }}
          onClick={openPreview}
        >
          <div css={{ marginRight: 4, fontSize: 20 }}>
            <PlayCircleIcon />
          </div>
          Preview
        </div>
      </div>
    </div>
  );
};

export const EditorHeader = forwardRef(EditorHeaderForwardRef);


================================================
FILE: src/features/design/components/editor-header/index.ts
================================================
export * from './EditorHeader';


================================================
FILE: src/features/design/components/index.ts
================================================
export * from './LidoJSEditor';


================================================
FILE: src/features/design/components/layer-settings/LayerSettings.tsx
================================================
import {
  LayerSettings as EditorLayerSettings,
  useSelectedLayers,
} from '@lidojs/design-editor';

export const LayerSettings = () => {
  const { selectedLayerIds } = useSelectedLayers();
  return (
    <div
      css={{
        background: 'white',
        borderBottom: '1px solid rgba(57,76,96,.15)',
        height: 50,
        overflowX: 'auto',
        flexShrink: 0,
        '@media (max-width: 900px)': {
          position: 'fixed',
          bottom: 0,
          left: 0,
          right: 0,
          background: '#fff',
          display: selectedLayerIds.length > 0 ? 'flex' : 'none',
          justifyContent: 'center',
          zIndex: 20,
          height: 72,
        },
      }}
    >
      <EditorLayerSettings />
    </div>
  );
};


================================================
FILE: src/features/design/components/layer-settings/index.ts
================================================
export * from './LayerSettings';


================================================
FILE: src/features/design/components/preview/PreviewModal.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import { Preview } from '@lidojs/design-editor';
import type { FC } from 'react';

interface PreviewModalProps {
  onClose: () => void;
}

export const PreviewModal: FC<PreviewModalProps> = ({ onClose }) => {
  return (
    <div
      css={{
        position: 'fixed',
        inset: 0,
        zIndex: 1040,
        background: 'rgba(13,18,22,.95)',
      }}
    >
      <Preview />
      <div
        css={{
          background: 'rgba(255,255,255,0.3)',
          width: 60,
          height: 60,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          position: 'fixed',
          right: 24,
          top: 24,
          borderRadius: '50%',
          fontSize: 36,
          color: '#fff',
          cursor: 'pointer',
        }}
        onClick={onClose}
      >
        <XIcon />
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/preview/index.ts
================================================
export * from './PreviewModal';


================================================
FILE: src/features/design/components/sidebar/DrawContent.tsx
================================================
import XBoldIcon from '@duyank/icons/bold/XBold';
import { useEditor } from '@lidojs/design-editor';
import { useDraw } from '@lidojs/draw';
import { type FC, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Highlighter } from '../../../../shared/icons/pencil/Highlighter';
import { Marker } from '../../../../shared/icons/pencil/Marker';
import { Pencil } from '../../../../shared/icons/pencil/Pencil';

export const DrawContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const svgRef = useRef<SVGSVGElement>(null);
  const boxRef = useRef<HTMLDivElement>(null);
  const penColorRef = useRef('#0571d3');
  const penWidthRef = useRef(5);
  const scaleRef = useRef(1);
  const transparencyRef = useRef(1);
  const [size, setSize] = useState({ width: 0, height: 0 });
  const { actions, scale, activePage, sidebar } = useEditor((state) => ({
    scale: state.scale,
    activePage: state.activePage,
    sidebar: state.sidebar,
  }));
  useEffect(() => {
    scaleRef.current = scale;
  }, [scale]);
  useDraw({
    options: {
      width: penWidthRef.current * scaleRef.current,
      minWidth: 33 * scaleRef.current,
      minHeight: 33 * scaleRef.current,
    },
    canStartDraw: (e) => {
      const page = document.getElementById(`lidojs-page-${activePage}`);
      if (!page) {
        throw new Error("Can't find page");
      }
      const rect = page.getBoundingClientRect();
      if (
        e.x >= rect.x &&
        e.x <= rect.x + rect.width &&
        e.y >= rect.y &&
        e.y <= rect.y + rect.height &&
        !sidebar &&
        boxRef.current
      ) {
        boxRef.current.style.pointerEvents = 'auto';
        boxRef.current.style.opacity = transparencyRef.current.toString();
        return true;
      }
      return false;
    },
    onChange: (path) => {
      if (!svgRef.current) return;
      const svgPath = document.createElementNS(
        'http://www.w3.org/2000/svg',
        'path',
      );
      svgPath.setAttributeNS(null, 'd', path);
      svgPath.setAttributeNS(null, 'fill', 'none');
      svgPath.setAttributeNS(null, 'stroke', penColorRef.current);
      svgPath.setAttributeNS(null, 'stroke-linecap', 'round');
      svgPath.setAttributeNS(
        null,
        'stroke-width',
        (penWidthRef.current * scaleRef.current).toString(),
      );
      svgRef.current.appendChild(svgPath);
    },
    onEnd: (path, boxSize, position) => {
      const page = document.getElementById(`lidojs-page-${activePage}`);
      if (page) {
        const rect = page.getBoundingClientRect();
        const p = {
          x: (position.x - rect.x) / scaleRef.current,
          y: (position.y - rect.y) / scaleRef.current,
        };
        actions.addDrawLayer(
          { path, color: penColorRef.current, width: penWidthRef.current },
          {
            width: boxSize.width / scaleRef.current,
            height: boxSize.height / scaleRef.current,
          },
          p,
          1 / scaleRef.current,
          transparencyRef.current,
        );
      }

      if (boxRef.current) {
        boxRef.current.style.pointerEvents = 'none';
      }
      if (svgRef.current) {
        svgRef.current.innerHTML = '';
      }
    },
  });

  useEffect(() => {
    if (!window) return;
    const { innerWidth: width, innerHeight: height } = window;
    setSize({ width, height });
  }, []);

  return (
    <>
      <div
        css={{
          position: 'absolute',
          left: 72,
          top: 500,
          zIndex: 10,
          overflow: 'hidden',
          width: 120,
          height: 250,
          paddingTop: 60,
          paddingBottom: 60,
        }}
      >
        <div
          css={{
            background: '#FFFFFF',
            boxShadow:
              '0px 0px 0px 1px rgba(64,87,109,.04),0px 6px 20px -4px rgba(64,87,109,.3)',
            width: 30,
            height: 30,
            borderRadius: 9999,
            zIndex: 10,
            top: 0,
            left: 30,
            position: 'absolute',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            cursor: 'pointer',
          }}
          onClick={() => onClose()}
        >
          <XBoldIcon />
        </div>
        <div
          css={{
            background: '#ffffff',
            borderRadius: 20,
            inset: 0,
            top: 40,
            right: 40,
            bottom: 10,
            position: 'absolute',
            boxShadow:
              '0px 0px 0px 1px rgba(64,87,109,.04),0px 6px 20px -4px rgba(64,87,109,.3)',
          }}
        />
        <div
          css={{
            display: 'flex',
            flexDirection: 'column',
            gap: 8,
            position: 'relative',
          }}
        >
          <div
            css={{
              color: 'rgb(5, 113, 211)',
              marginLeft: '-30px',
              cursor: 'pointer',
              ':hover': {
                marginLeft: 0,
              },
            }}
            onClick={() => {
              penColorRef.current = '#0571d3';
              penWidthRef.current = 5;
              transparencyRef.current = 1;
            }}
          >
            <Pencil width="100px" />
          </div>
          <div
            css={{
              color: 'rgb(231, 25, 31)',
              marginLeft: '-30px',
              cursor: 'pointer',
              ':hover': {
                marginLeft: 0,
              },
            }}
            onClick={() => {
              penColorRef.current = '#e7171f';
              penWidthRef.current = 10;
              transparencyRef.current = 1;
            }}
          >
            <Marker width="100px" />
          </div>
          <div
            css={{
              color: 'rgb(255, 242, 52)',
              marginLeft: '-30px',
              cursor: 'pointer',
              ':hover': {
                marginLeft: 0,
              },
            }}
            onClick={() => {
              penColorRef.current = '#fff234';
              penWidthRef.current = 20;
              transparencyRef.current = 0.8;
            }}
          >
            <Highlighter width="100px" />
          </div>
        </div>
      </div>
      {createPortal(
        <div
          ref={boxRef}
          css={{ position: 'absolute', inset: 0 }}
          style={{ pointerEvents: 'none' }}
        >
          <svg
            ref={svgRef}
            height={size.height}
            viewBox={`0 0 ${size.width} ${size.height}`}
            width={size.width}
            xmlns="http://www.w3.org/2000/svg"
          />
        </div>,
        document.body,
      )}
    </>
  );
};


================================================
FILE: src/features/design/components/sidebar/FrameContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import axios from 'axios';
import { type FC, useState } from 'react';
import { isMobile } from 'react-device-detect';
import { useAsync } from 'react-use';

interface Frame {
  id: string;
  img: string;
  clipPath: string;
  width: number;
  height: number;
}

export const FrameContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const [frames, setFrames] = useState<Frame[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const { actions, query } = useEditor();
  useAsync(async () => {
    const response = await axios.get<Frame[]>('/frames');
    setFrames(response.data);
    setIsLoading(false);
  }, []);
  const addFrame = async (data: Frame) => {
    actions.addFrameLayer(data, data.clipPath);
    if (isMobile) {
      onClose();
    }
  };

  const handleDrag = (event: React.DragEvent, frame: Frame) => {
    const { clientX, clientY } = event;
    const pageSize = query.getPageSize(query.activePage());
    const ratio = pageSize.width / pageSize.height;
    const frameRatio = frame.width / frame.height;
    const scale =
      ratio > frameRatio
        ? (pageSize.height * 0.5) / frame.height
        : (pageSize.width * 0.5) / frame.width;
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Frame',
      data: {
        rootId: frame.id,
        layers: {
          [frame.id]: {
            type: {
              resolvedName: 'FrameLayer',
            },
            props: {
              clipPath: frame.clipPath,
              position: {
                x: 0,
                y: 0,
              },
              boxSize: {
                width: frame.width * scale,
                height: frame.height * scale,
              },
              rotate: 0,
              scale,
            },
            locked: false,
            parent: 'ROOT',
            child: [],
          },
        },
      },
    };
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Frames
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{ flexDirection: 'column', overflowY: 'auto', display: 'flex' }}
      >
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(3,minmax(0,1fr))',
            gridGap: 8,
            padding: '16px',
          }}
        >
          {isLoading && <div>Loading...</div>}
          {frames.map((item, index) => (
            <div
              key={index}
              css={{
                cursor: 'pointer',
                position: 'relative',
                '-webkit-user-drag': 'element',
              }}
              onClick={() => addFrame(item)}
              onDragStart={(e) => handleDrag(e, item)}
            >
              <div css={{ paddingBottom: '100%' }} />
              <div
                css={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  height: '100%',
                  width: '100%',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                }}
              >
                <img
                  alt={item.img}
                  css={{
                    maxHeight: '100%',
                    maxWidth: '100%',
                  }}
                  src={item.img}
                />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/GraphicContent.tsx
================================================
import MagnifyingGlassIcon from '@duyank/icons/regular/MagnifyingGlass';
import XIcon from '@duyank/icons/regular/X';
import { useEventCallback } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import axios from 'axios';
import { type FC, type FormEvent, useEffect, useRef, useState } from 'react';
import { isMobile } from 'react-device-detect';
import { useAsync } from 'react-use';
import Masonry from '../../../../shared/components/masonry/Masonry';

export const GraphicContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const qRef = useRef<HTMLInputElement>(null);
  const scrollRef = useRef<HTMLDivElement>(null);
  const dataRef = useRef(false);
  const [images, setImages] = useState<
    {
      id: string;
      thumb: string;
      downloadUrl: string;
    }[]
  >([]);
  const [isLoading, setIsLoading] = useState(true);
  const [keyword, setKeyword] = useState('');
  const { actions } = useEditor();

  const loadGraphicList = useEventCallback(async (offset: number) => {
    dataRef.current = true;
    setIsLoading(true);
    const params = {
      limit: '100',
      offset: `${offset}`,
      q: keyword,
    };
    const response = await axios.get<
      {
        id: string;
        thumb: string;
        downloadUrl: string;
      }[]
    >(`/graphics?${new URLSearchParams(params).toString()}`);
    if (offset) {
      setImages((prevState) => {
        prevState.push(...response.data);
        return prevState;
      });
    } else {
      setImages(response.data);
    }
    setIsLoading(false);
    if (response.data.length > 0) {
      dataRef.current = false;
    }
  });
  useAsync(async () => {
    await loadGraphicList(0);
  }, [loadGraphicList]);

  const handleLoadMore = useEventCallback(async (e: Event) => {
    const node = e.target as HTMLDivElement;
    if (
      node.scrollHeight - node.scrollTop - 80 <= node.clientHeight &&
      !dataRef.current
    ) {
      await loadGraphicList(images.length);
    }
  });

  useEffect(() => {
    const ele = scrollRef.current;
    ele?.addEventListener('scroll', handleLoadMore);
    return () => {
      ele?.removeEventListener('scroll', handleLoadMore);
    };
  }, [handleLoadMore]);
  const handleSearch = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (scrollRef.current) {
      scrollRef.current.scrollTop = 0;
    }
    setKeyword(qRef.current?.value || '');
    setTimeout(async () => {
      await loadGraphicList(0);
    });
  };
  const addGraphic = async (item: {
    id: string;
    thumb: string;
    downloadUrl: string;
  }) => {
    const res = await axios.get(
      `/graphics/download?url=${window.encodeURIComponent(item.downloadUrl)}`,
    );
    const file = res.data.file;
    const parser = new DOMParser();
    const ele = parser.parseFromString(file, 'text/xml')
      .documentElement as unknown as SVGElement;
    const viewBox = ele.getAttribute('viewBox')?.split(' ') || [];
    const width =
      viewBox.length === 4 ? +viewBox[2] : +(ele.getAttribute('width') || 100);
    const height =
      viewBox.length === 4 ? +viewBox[3] : +(ele.getAttribute('height') || 100);

    const svgBlob = new Blob([ele.outerHTML], {
      type: 'image/svg+xml;charset=utf-8',
    });
    const svgUrl = URL.createObjectURL(svgBlob);
    actions.addSvgLayer(svgUrl, { width, height }, ele);
    if (isMobile) {
      onClose();
    }
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Graphic
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{
          flexDirection: 'column',
          overflowY: 'auto',
          display: 'flex',
          flexGrow: 1,
        }}
      >
        <div
          css={{
            borderRadius: 4,
            boxShadow: '0 0 0 1px rgba(43,59,74,.3)',
            margin: 16,
          }}
        >
          <div
            css={{
              height: 40,
              borderRadius: 4,
              padding: '0 12px',
              display: 'flex',
              alignItems: 'center',
            }}
          >
            <div css={{ fontSize: 24, marginRight: 8, flexShrink: 0 }}>
              <MagnifyingGlassIcon />
            </div>
            <form onSubmit={handleSearch}>
              <input
                ref={qRef}
                css={{ width: '100%', height: '100%' }}
                type="text"
              />
            </form>
          </div>
        </div>
        <div
          ref={scrollRef}
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            padding: '16px',
            gridGap: 8,
          }}
        >
          <Masonry columnsCount={4} gutter="20px">
            {images.map((item) => (
              <div
                key={item.id}
                css={{ cursor: 'pointer' }}
                onClick={() => addGraphic(item)}
              >
                <img
                  key={item.id}
                  alt={item.thumb}
                  loading="lazy"
                  src={item.thumb}
                />
              </div>
            ))}
          </Masonry>
          {isLoading && <div>Loading...</div>}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/IframeContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type {
  LayerId,
  LayerType,
  SerializedLayerTree,
  SerializedLayers,
} from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import type { FC } from 'react';
import { isMobile } from 'react-device-detect';
import { iframeList } from '../../config/iframe';

export const IframeContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const { actions } = useEditor();
  const addIframe = async (elements: SerializedLayerTree) => {
    actions.addLayerTree(elements);
    if (isMobile) {
      onClose();
    }
  };
  const handleDrag = (
    event: React.DragEvent,
    elements: SerializedLayerTree,
  ) => {
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Image',
      data: elements,
    };
    const { clientX, clientY } = event;
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Widgets
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{ flexDirection: 'column', overflowY: 'auto', display: 'flex' }}
      >
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(3,minmax(0,1fr))',
            gridGap: 8,
            padding: '16px',
          }}
        >
          {iframeList.map((item, index) => (
            <div
              key={index}
              css={{
                cursor: 'pointer',
                position: 'relative',
                '-webkit-user-drag': 'element',
              }}
              onClick={() => addIframe(item.elements[0])}
              onDragStart={(e) => handleDrag(e, item.elements[0])}
            >
              <div css={{ paddingBottom: '100%' }} />
              <div
                css={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  height: '100%',
                  width: '100%',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                }}
              >
                <img
                  alt={item.img}
                  css={{
                    maxHeight: '100%',
                    maxWidth: '100%',
                  }}
                  src={item.img}
                />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/ImageContent.tsx
================================================
import MagnifyingGlassIcon from '@duyank/icons/regular/MagnifyingGlass';
import XIcon from '@duyank/icons/regular/X';
import {
  type LayerId,
  type LayerType,
  type SerializedLayers,
  useEventCallback,
} from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import axios from 'axios';
import type React from 'react';
import { type FC, type FormEvent, useEffect, useRef, useState } from 'react';
import { isMobile } from 'react-device-detect';
import Masonry from 'react-responsive-masonry';
import { useAsync } from 'react-use';
import { Photo } from './Photo';

export const ImageContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const qRef = useRef<HTMLInputElement>(null);
  const scrollRef = useRef<HTMLDivElement>(null);
  const dataRef = useRef(false);
  const [images, setImages] = useState<
    {
      id: string;
      image: string;
      thumb: string;
      width: number;
      height: number;
      username: string;
      name: string;
    }[]
  >([]);
  const [isLoading, setIsLoading] = useState(true);
  const [keyword, setKeyword] = useState('');
  const { actions, query } = useEditor((state) => ({
    dragNDrop: state.dragNDrop,
  }));

  const loadImageList = useEventCallback(async (offset = 0) => {
    dataRef.current = true;
    setIsLoading(true);
    const params = {
      limit: '40',
      page: `${(images.length % 40) + 1}`,
      q: keyword,
    };
    const response = await axios.get<
      {
        id: string;
        image: string;
        thumb: string;
        width: number;
        height: number;
        username: string;
        name: string;
      }[]
    >(`/images?${new URLSearchParams(params).toString()}`);
    if (offset) {
      setImages((prevState) => {
        prevState.push(...response.data);
        return prevState;
      });
    } else {
      setImages(response.data);
    }
    setIsLoading(false);
    if (response.data.length > 0) {
      dataRef.current = false;
    }
  });
  useAsync(async () => {
    await loadImageList(0);
  }, [loadImageList]);

  useEffect(() => {
    const handleLoadMore = async (e: Event) => {
      const node = e.target as HTMLDivElement;
      if (
        node.scrollHeight - node.scrollTop - 80 <= node.clientHeight &&
        !dataRef.current
      ) {
        await loadImageList(images.length);
      }
    };
    const ele = scrollRef.current;
    ele?.addEventListener('scroll', handleLoadMore);
    return () => {
      ele?.removeEventListener('scroll', handleLoadMore);
    };
  }, [loadImageList, images]);
  const handleSearch = async (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (scrollRef.current) {
      scrollRef.current.scrollTop = 0;
    }
    setKeyword(qRef.current?.value || '');
    setTimeout(async () => {
      await loadImageList(0);
    });
  };
  const addImage = async (item: {
    id: string;
    image: string;
    thumb: string;
    width: number;
    height: number;
    username: string;
    name: string;
  }) => {
    actions.addImageLayer(
      { thumb: item.thumb, url: item.image },
      { width: item.width, height: item.height },
    );
    axios.put(`/images?id=${item.id}`);
    if (isMobile) {
      onClose();
    }
  };

  const handleDrag = (
    event: React.DragEvent,
    item: {
      id: string;
      image: string;
      thumb: string;
      width: number;
      height: number;
      username: string;
      name: string;
    },
  ) => {
    const { clientX, clientY } = event;
    const pageSize = query.getPageSize(query.activePage());
    const ratio = pageSize.width / pageSize.height;
    const imgRatio = item.width / item.height;
    const w =
      ratio < imgRatio
        ? pageSize.width * 0.8
        : pageSize.height * imgRatio * 0.8;
    const h = w / imgRatio;
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Image',
      data: {
        rootId: item.id,
        layers: {
          [item.id]: {
            type: {
              resolvedName: 'ImageLayer',
            },
            props: {
              image: {
                url: item.image,
                thumb: item.thumb,
                boxSize: {
                  width: w,
                  height: h,
                },
                position: {
                  x: 0,
                  y: 0,
                },
                rotate: 0,
              },
              position: {
                x: 0,
                y: 0,
              },
              boxSize: {
                width: w,
                height: h,
              },
              rotate: 0,
            },
            locked: false,
            parent: 'ROOT',
            child: [],
          },
        },
      },
    };
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Images
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{
          flexDirection: 'column',
          overflowY: 'auto',
          display: 'flex',
          flexGrow: 1,
        }}
      >
        <div
          css={{
            borderRadius: 4,
            boxShadow: '0 0 0 1px rgba(43,59,74,.3)',
            margin: 16,
          }}
        >
          <div
            css={{
              height: 40,
              borderRadius: 4,
              padding: '0 12px',
              display: 'flex',
              alignItems: 'center',
            }}
          >
            <div css={{ fontSize: 24, marginRight: 8, flexShrink: 0 }}>
              <MagnifyingGlassIcon />
            </div>
            <form onSubmit={handleSearch}>
              <input
                ref={qRef}
                css={{ width: '100%', height: '100%' }}
                type="text"
              />
            </form>
          </div>
        </div>
        <div
          ref={scrollRef}
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            padding: '16px',
            gridGap: 8,
          }}
        >
          <Masonry columnsCount={2} gutter="8px">
            {images.map((item) => (
              <Photo
                key={item.id}
                image={item.thumb}
                name={item.name}
                username={item.username}
                onClick={() => {
                  addImage(item);
                }}
                onDragStart={(e) => handleDrag(e, item)}
              />
            ))}
          </Masonry>
          {isLoading && <div>Loading...</div>}
        </div>
      </div>

      <div css={{ flexShrink: 0, paddingLeft: 16, textAlign: 'center' }}>
        Photos by
        <a href="https://unsplash.com/" rel="noreferrer" target="_blank">
          Unsplash
        </a>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/Photo.tsx
================================================
import { type FC, type HTMLProps, memo, useState } from 'react';

type Props = {
  image: string;
  name: string;
  username: string;
} & HTMLProps<HTMLDivElement>;

export const Photo: FC<Props> = memo(({ image, name, username, ...props }) => {
  const [isShow, setIsShow] = useState(false);
  return (
    <div
      css={{
        cursor: 'pointer',
        position: 'relative',
        '-webkit-user-drag': 'element',
      }}
      {...props}
      onMouseOut={() => setIsShow(false)}
      onMouseOver={() => setIsShow(true)}
    >
      <img alt={image} loading="lazy" src={image} />
      <p
        css={{
          display: isShow ? 'block' : 'none',
          position: 'absolute',
          bottom: 0,
          fontSize: 10,
          left: 0,
          right: 0,
          color: '#fff',
          textAlign: 'center',
          paddingTop: 12,
          paddingBottom: 2,
          paddingLeft: 4,
          paddingRight: 4,
          background:
            'linear-gradient(0deg, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.2) 80%, rgba(0, 0, 0, 0) 100%)',
        }}
      >
        Photo by
        <a
          css={{ color: '#3d8eff' }}
          href={`https://unsplash.com/@${username}?utm_source=lidojs&utm_medium=referral`}
          rel="noreferrer"
          target="_blank"
        >
          {name}
        </a>
        on
        <a
          href={`https://unsplash.com/@${username}?utm_source=lidojs&utm_medium=referral`}
          rel="noreferrer"
          target="_blank"
        >
          unsplash
        </a>
      </p>
    </div>
  );
});


================================================
FILE: src/features/design/components/sidebar/QrCodeContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type {
  LayerId,
  LayerType,
  SerializedLayerTree,
  SerializedLayers,
} from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import type { FC } from 'react';
import { isMobile } from 'react-device-detect';
import { qrCodeList } from '../../config/qrCode';

export const QrCodeContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const { actions } = useEditor();
  const addQrCode = async (elements: SerializedLayerTree) => {
    actions.addLayerTree(elements);
    if (isMobile) {
      onClose();
    }
  };
  const handleDrag = (
    event: React.DragEvent,
    elements: SerializedLayerTree,
  ) => {
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Image',
      data: elements,
    };
    const { clientX, clientY } = event;
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          QR Code
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{ flexDirection: 'column', overflowY: 'auto', display: 'flex' }}
      >
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(3,minmax(0,1fr))',
            gridGap: 8,
            padding: '16px',
          }}
        >
          {qrCodeList.map((item, index) => (
            <div
              key={index}
              css={{
                cursor: 'pointer',
                position: 'relative',
                '-webkit-user-drag': 'element',
              }}
              onClick={() => addQrCode(item.elements[0])}
              onDragStart={(e) => handleDrag(e, item.elements[0])}
            >
              <div css={{ paddingBottom: '100%' }} />
              <div
                css={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  height: '100%',
                  width: '100%',
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                }}
              >
                <img
                  alt={item.img}
                  css={{
                    maxHeight: '100%',
                    maxWidth: '100%',
                  }}
                  src={item.img}
                />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/ShapeContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import type { FC } from 'react';
import { isMobile } from 'react-device-detect';
import { v4 } from 'uuid';
import { type Line, lines } from '../../config/line';
import { type Shape, shapes } from '../../config/shape';

export const ShapeContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const { actions, query } = useEditor();
  const addLine = (props: Line['props']) => {
    actions.addLineLayer({ props });
  };
  const addShape = (shape: Shape) => {
    actions.addShapeLayer({
      type: {
        resolvedName: 'ShapeLayer',
      },
      props: {
        shape: shape.type,
        position: {
          x: 0,
          y: 400,
        },
        boxSize: {
          width: shape.width,
          height: shape.height,
        },
        rotate: 0,
        color: '#5E6278',
      },
    });
    if (isMobile) {
      onClose();
    }
  };

  const handleDrag = (event: React.DragEvent, shape: Shape) => {
    const { clientX, clientY } = event;
    const id = v4();
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Shape',
      data: {
        rootId: id,
        layers: {
          [id]: {
            type: {
              resolvedName: 'ShapeLayer',
            },
            locked: false,
            parent: 'ROOT',
            child: [],
            props: {
              shape: shape.type,
              position: {
                x: 0,
                y: 400,
              },
              boxSize: {
                width: shape.width,
                height: shape.height,
              },
              rotate: 0,
              color: '#5E6278',
            },
          },
        },
      },
    };
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };
  const handleDragLine = (event: React.DragEvent, line: Line) => {
    const { clientX, clientY } = event;
    const width = query.getPageSize(query.activePage()).width / 2;
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Line',
      data: {
        rootId: line.id,
        layers: {
          [line.id]: {
            props: {
              ...line.props,
              boxSize: {
                width,
                height: 4,
              },
              position: {
                x: 0,
                y: 0,
              },
              style: 'solid',
              color: 'rgb(0, 0, 0)',
              scale: 1,
              rotate: 0,
            },
            type: {
              resolvedName: 'LineLayer',
            },
            locked: false,
            parent: 'ROOT',
            child: [],
          },
        },
      },
    };
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Shapes
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div css={{ padding: '16px' }}>
        <div css={{ padding: '8px 0', fontWeight: 700 }}>
          Arrow
          <div
            css={{
              display: 'inline-block',
              marginLeft: 6,
              background: '#fdebcf',
              borderRadius: 9999,
              fontSize: 9,
              padding: '2px 4px',
            }}
          >
            Business
          </div>
        </div>
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(4,minmax(0,1fr))',
            gridGap: 8,
          }}
        >
          {lines.map((l, idx) => (
            <div
              key={idx}
              draggable
              css={{
                width: '100%',
                paddingBottom: '100%',
                position: 'relative',
                cursor: 'pointer',
                '-webkit-user-drag': 'element',
              }}
              onClick={() => addLine(l.props)}
              onDragStart={(e) => handleDragLine(e, l)}
            >
              <div
                css={{
                  position: 'absolute',
                  inset: 0,
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                }}
              >
                {l.icon}
              </div>
            </div>
          ))}
        </div>
        <div css={{ padding: '8px 0', fontWeight: 700 }}>Shape</div>
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(4,minmax(0,1fr))',
            gridGap: 8,
          }}
        >
          {shapes.map((shape) => (
            <div
              key={shape.type}
              css={{
                width: '100%',
                paddingBottom: '100%',
                position: 'relative',
                cursor: 'pointer',
                '-webkit-user-drag': 'element',
              }}
              draggable={true}
              onClick={() => addShape(shape)}
              onDragStart={(e) => handleDrag(e, shape)}
            >
              {shape.icon}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/Sidebar.tsx
================================================
import BrowserIcon from '@duyank/icons/regular/Browser';
import FrameCornersIcon from '@duyank/icons/regular/FrameCorners';
import ImageIcon from '@duyank/icons/regular/Image';
import LayoutIcon from '@duyank/icons/regular/Layout';
import PencilIcon from '@duyank/icons/regular/Pencil';
import PiggyBankIcon from '@duyank/icons/regular/PiggyBank';
import QrCodeIcon from '@duyank/icons/regular/QrCode';
import SquareIcon from '@duyank/icons/regular/Square';
import TableIcon from '@duyank/icons/regular/Table';
import TextTIcon from '@duyank/icons/regular/TextT';
import UploadIcon from '@duyank/icons/regular/Upload';
import VideoIcon from '@duyank/icons/regular/Video';
import { useEditor } from '@lidojs/design-editor';
import { useCallback, useState } from 'react';
import { SidebarTab } from '../tabs';
import { DrawContent } from './DrawContent';
import { FrameContent } from './FrameContent';
import { GraphicContent } from './GraphicContent';
import { IframeContent } from './IframeContent';
import { ImageContent } from './ImageContent';
import { QrCodeContent } from './QrCodeContent';
import { ShapeContent } from './ShapeContent';
import { TableContent } from './TableContent';
import { TemplateContent } from './TemplateContent';
import { TextContent } from './TextContent';
import { UploadContent } from './UploadContent';
import { VideoContent } from './VideoContent';

const tabs = [
  {
    name: 'Template',
    icon: <LayoutIcon />,
  },
  {
    name: 'Text',
    icon: <TextTIcon />,
  },
  {
    name: 'Shape',
    icon: <SquareIcon />,
  },
  {
    name: 'Table',
    icon: <TableIcon />,
    isBusiness: true,
  },
  {
    name: 'Frame',
    icon: <FrameCornersIcon />,
  },
  {
    name: 'Image',
    icon: <ImageIcon />,
  },
  {
    name: 'Graphic',
    icon: <PiggyBankIcon />,
  },
  {
    name: 'QrCode',
    icon: <QrCodeIcon />,
    isBusiness: true,
  },
  {
    name: 'Widgets',
    icon: <BrowserIcon />,
  },
  {
    name: 'Draw',
    icon: <PencilIcon />,
    isBusiness: true,
  },
  {
    name: 'Video',
    icon: <VideoIcon />,
  },
  {
    name: 'Upload',
    icon: <UploadIcon />,
  },
];
export const Sidebar = () => {
  const { actions } = useEditor();
  const [tab, setTab] = useState<string | null>('Template');

  const handleCloseTab = useCallback(() => {
    setTab(null);
    actions.setSidebar();
  }, [actions]);
  return (
    <div
      css={{
        display: 'flex',
        zIndex: 2,
        position: 'relative',
        backgroundColor: '#ffffff',
        borderRight: '1px solid rgba(217, 219, 228, 0.6)',
      }}
    >
      <div
        css={{
          display: 'flex',
        }}
      >
        <SidebarTab
          active={tab}
          tabs={tabs}
          onChange={(_, tab) => {
            actions.setSidebar();
            setTab(tab);
          }}
        />
        {tab && (
          <div
            css={{
              width: tab === 'Draw' ? 0 : 360,
              '@media (max-width: 900px)': {
                width: '100%',
                position: 'fixed',
                bottom: 0,
                left: 0,
                top: 0,
                background: '#fff',
              },
            }}
          >
            {tab === 'Template' && (
              <TemplateContent
                onClose={() => {
                  setTab(null);
                  actions.setSidebar();
                }}
              />
            )}
            {tab === 'Text' && <TextContent onClose={handleCloseTab} />}
            {tab === 'Frame' && <FrameContent onClose={handleCloseTab} />}
            {tab === 'Image' && <ImageContent onClose={handleCloseTab} />}
            {tab === 'Graphic' && <GraphicContent onClose={handleCloseTab} />}
            {tab === 'QrCode' && <QrCodeContent onClose={handleCloseTab} />}
            {tab === 'Widgets' && <IframeContent onClose={handleCloseTab} />}
            {tab === 'Shape' && <ShapeContent onClose={handleCloseTab} />}
            {tab === 'Table' && <TableContent onClose={handleCloseTab} />}
            {tab === 'Video' && <VideoContent onClose={handleCloseTab} />}
            {tab === 'Draw' && <DrawContent onClose={handleCloseTab} />}
            <UploadContent
              visibility={tab === 'Upload'}
              onClose={handleCloseTab}
            />
          </div>
        )}
      </div>
      <div
        css={{
          width: 360,
          position: 'absolute',
          overflow: 'hidden',
          top: 0,
          left: 73,
          height: '100%',
          pointerEvents: 'none',
        }}
        id="settings"
      />
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/TableContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import type { FC } from 'react';
import { isMobile } from 'react-device-detect';
import { v4 } from 'uuid';

const tableLayer = {
  type: {
    resolvedName: 'TableLayer',
  },
  props: {
    position: {
      x: 458.2781364561637,
      y: 229.60681114551085,
    },
    boxSize: {
      width: 838.5314463859182,
      height: 282,
      x: 344.41332869685516,
      y: 226.56694426649582,
    },
    rotate: 0,
    scale: 1,
    format: {
      cellSpacing: 0,
      cellPadding: 10,
      rowHeight: [100, 120, 180, 202.38026124818578],
      colWidth: [400, 400, 300, 252.3802612481859],
      rows: [
        {
          index: 1,
          height: 70,
        },
        {
          index: 2,
          height: 70,
        },
        {
          index: 3,
          height: 70,
        },
        {
          index: 4,
          height: 70,
        },
      ],
      columns: [
        {
          index: 1,
          width: 226.64958360025628,
        },
        {
          index: 2,
          width: 209.83984625240237,
        },
        {
          index: 3,
          width: 200.19218449711718,
        },
        {
          index: 4,
          width: 199.8498320361424,
        },
      ],
    },
    cells: [
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(84, 84, 84)',
                fontFamily: 'Acme',
                fontSize: '18px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'bold',
                    },
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(84, 84, 84)',
                      },
                    },
                  ],
                  text: 'Header 1',
                },
              ],
            },
          ],
        },
        row: 1,
        col: 1,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(84, 84, 84)',
                fontFamily: 'Acme',
                fontSize: '18px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'bold',
                    },
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(84, 84, 84)',
                      },
                    },
                  ],
                  text: 'Header 1',
                },
              ],
            },
          ],
        },
        row: 1,
        col: 2,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(84, 84, 84)',
                fontFamily: 'Acme',
                fontSize: '18px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'bold',
                    },
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(84, 84, 84)',
                      },
                    },
                  ],
                  text: 'Header 3',
                },
              ],
            },
          ],
        },
        row: 1,
        col: 3,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(84, 84, 84)',
                fontFamily: 'Acme',
                fontSize: '18px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'bold',
                    },
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(84, 84, 84)',
                      },
                    },
                  ],
                  text: 'Header 4',
                },
              ],
            },
          ],
        },
        row: 1,
        col: 4,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(0, 0, 0)',
                      },
                    },
                  ],
                  text: 'Content For Column 1',
                },
              ],
            },
          ],
        },
        row: 2,
        col: 1,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(228, 5, 5)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 2',
                },
              ],
            },
          ],
        },
        row: 2,
        col: 2,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(96, 25, 211)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  marks: [
                    {
                      type: 'color',
                      attrs: {
                        color: 'rgb(0, 0, 0)',
                      },
                    },
                  ],
                  text: 'Content For Column 3',
                },
              ],
            },
          ],
        },
        row: 2,
        col: 3,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        row: 2,
        col: 4,
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 4',
                },
              ],
            },
          ],
        },
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        row: 3,
        col: 3,
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 3',
                },
              ],
            },
          ],
        },
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        row: 3,
        col: 4,
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 4',
                },
              ],
            },
          ],
        },
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 1',
                },
              ],
            },
          ],
        },
        row: 3,
        col: 1,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 2',
                },
              ],
            },
          ],
        },
        row: 3,
        col: 2,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 1',
                },
              ],
            },
          ],
        },
        col: 1,
        row: 4,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 2,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        row: 4,
        col: 2,
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 2',
                },
              ],
            },
          ],
        },
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 2,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        row: 4,
        col: 3,
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 3',
                },
              ],
            },
          ],
        },
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 2,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
      {
        value: {
          type: 'doc',
          content: [
            {
              type: 'paragraph',
              attrs: {
                textAlign: 'center',
                color: 'rgb(0, 0, 0)',
                fontFamily: 'Akatab',
                fontSize: '10px',
                lineHeight: '1.4',
                letterSpacing: 0,
                textTransform: 'uppercase',
                marginLeft: null,
                indent: 0,
                listType: '',
              },
              content: [
                {
                  type: 'text',
                  text: 'Content For Column 4',
                },
              ],
            },
          ],
        },
        col: 4,
        row: 4,
        background: 'rgb(255,255,255)',
        border: {
          top: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          left: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          right: {
            width: 1,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
          bottom: {
            width: 2,
            color: 'rgb(0, 0, 0)',
            style: 'solid',
          },
        },
      },
    ],
    fonts: [
      {
        name: 'Acme',
        fonts: [
          {
            urls: [
              'https://fonts.gstatic.com/s/acme/v25/RrQfboBx-C5_bx3Lb23lzLk.ttf',
            ],
          },
        ],
      },
      {
        name: 'Akatab',
        fonts: [
          {
            style: 'Bold',
            urls: [
              'https://fonts.gstatic.com/s/akatab/v7/VuJzdNrK3Z7gqJE3gKLdPKNiaRpFvg.ttf',
            ],
          },
          {
            urls: [
              'https://fonts.gstatic.com/s/akatab/v7/VuJwdNrK3Z7gqJEPWIz5NIh-YA.ttf',
            ],
          },
        ],
      },
    ],
  },
};

export const TableContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const { actions } = useEditor();

  const addTable = () => {
    actions.addLayer(tableLayer);
    if (isMobile) {
      onClose();
    }
  };
  const handleDrag = (event: React.DragEvent) => {
    const { clientX, clientY } = event;
    const id = v4();
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Table',
      data: {
        rootId: id,
        layers: {
          [id]: {
            ...tableLayer,
            locked: false,
            parent: 'ROOT',
            child: [],
          },
        },
      },
    };
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Table
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div css={{ padding: '16px' }}>
        <div
          css={{ cursor: 'pointer', '-webkit-user-drag': 'element' }}
          onClick={addTable}
          onDragStart={(e) => handleDrag(e)}
        >
          <div>
            <img src="/assets/images/table/table-1.png" alt="Table" />
          </div>
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/TemplateContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type { SerializedPage } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import axios from 'axios';
import { type FC, useState } from 'react';
import { isMobile } from 'react-device-detect';
import { useAsync } from 'react-use';

interface Template {
  img: string;
  elements: SerializedPage;
}

export const TemplateContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const [templates, setTemplates] = useState<Template[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const { actions, activePage } = useEditor((state) => ({
    activePage: state.activePage,
  }));
  useAsync(async () => {
    const response = await axios.get<Template[]>('/templates');
    setTemplates(response.data);
    setIsLoading(false);
  }, []);
  const addPage = async (data: SerializedPage) => {
    actions.setPage(activePage, data);
    if (isMobile) {
      onClose();
    }
  };
  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Templates
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{ flexDirection: 'column', overflowY: 'auto', display: 'flex' }}
      >
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(2,minmax(0,1fr))',
            gridGap: 8,
            padding: '16px',
          }}
        >
          {isLoading && <div>Loading...</div>}
          {templates.map((item, index) => (
            <div
              key={index}
              css={{ cursor: 'pointer' }}
              onClick={() => addPage(item.elements)}
            >
              <img alt={item.img} loading="lazy" src={item.img} />
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/TextContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type { LayerId, SerializedLayers } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import axios from 'axios';
import { type FC, useState } from 'react';
import { isMobile } from 'react-device-detect';
import { useAsync } from 'react-use';
import {
  addABodyText,
  addAHeading,
  addASubheading,
} from '../../../../constant/text-effects';
import { getThumbnail } from '../../../../utils/thumbnail';

interface Text {
  img: string;
  elements: {
    rootId: LayerId;
    layers: SerializedLayers;
  };
}

export const TextContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const { actions } = useEditor();
  const [texts, setTexts] = useState<Text[]>([]);
  const [isLoading, setIsLoading] = useState(true);

  useAsync(async () => {
    const response = await axios.get<Text[]>('/texts');
    setTexts(response.data);
    setIsLoading(false);
  }, []);

  const handleAddText = (data: {
    rootId: LayerId;
    layers: SerializedLayers;
  }) => {
    actions.addTextLayer(data);
    if (isMobile) {
      onClose();
    }
  };

  const handleDrag = (
    event: React.DragEvent,
    data: { rootId: LayerId; layers: SerializedLayers },
  ) => {
    const { clientX, clientY } = event;
    actions.startDragNDrop({ layer: 'Text', data }, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData(
      'text/plain',
      JSON.stringify({ layer: 'Text', data }),
    );
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Text
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{ flexDirection: 'column', overflowY: 'auto', display: 'flex' }}
      >
        <div
          css={{
            padding: 16,
            display: 'flex',
            gap: 8,
            flexDirection: 'column',
          }}
        >
          <div
            css={{
              fontSize: 28,
              lineHeight: 1,
              padding: '16px 16px',
              fontWeight: 700,
              background: '#EBECF0',
              borderRadius: 4,
              cursor: 'pointer',
              '-webkit-user-drag': 'element',
            }}
            draggable={true}
            onClick={() => handleAddText(addAHeading)}
            onDragStart={(e) => handleDrag(e, addAHeading)}
          >
            Add a heading
          </div>
          <div
            css={{
              fontSize: 18,
              lineHeight: 1,
              padding: '16px',
              fontWeight: 700,
              background: '#EBECF0',
              borderRadius: 4,
              cursor: 'pointer',
              '-webkit-user-drag': 'element',
            }}
            onClick={() => handleAddText(addASubheading)}
            onDragStart={(e) => handleDrag(e, addASubheading)}
          >
            Add a subheading
          </div>
          <div
            css={{
              fontSize: 12,
              lineHeight: 1,
              padding: '16px',
              fontWeight: 700,
              background: '#EBECF0',
              borderRadius: 4,
              cursor: 'pointer',
              '-webkit-user-drag': 'element',
            }}
            onClick={() => handleAddText(addABodyText)}
            onDragStart={(e) => handleDrag(e, addABodyText)}
          >
            Add a little bit of body text
          </div>
        </div>
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(3,minmax(0,1fr))',
            gridGap: 8,
            padding: '16px',
          }}
        >
          {isLoading && <div>Loading...</div>}
          {texts.map(({ img, elements }, idx) => (
            <div
              key={idx}
              css={{
                cursor: 'pointer',
                position: 'relative',
                paddingBottom: '100%',
                width: '100%',
                '-webkit-user-drag': 'element',
              }}
              onClick={() => handleAddText(elements)}
              onDragStart={(e) => handleDrag(e, elements)}
            >
              <img
                alt={getThumbnail(img)}
                css={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  height: '100%',
                  width: '100%',
                  objectFit: 'cover',
                }}
                src={getThumbnail(img)}
              />
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/UploadContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import { useEditor } from '@lidojs/design-editor';
import { fetchSvgContent } from '@lidojs/design-utils';
import { type ChangeEvent, type FC, useRef, useState } from 'react';
import { isMobile } from 'react-device-detect';

interface UploadContentProps {
  visibility: boolean;
  onClose: () => void;
}

export const UploadContent: FC<UploadContentProps> = ({
  visibility,
  onClose,
}) => {
  const inputFileRef = useRef<HTMLInputElement>(null);
  const { actions } = useEditor();

  const [images, setImages] = useState<
    { url: string; type: 'svg' | 'image' }[]
  >([]);
  const addImage = async (url: string) => {
    if (!window) return;
    const img = new Image();
    img.onerror = (err) => window.alert(err);
    img.src = url;
    img.crossOrigin = 'anonymous';
    img.onload = () => {
      actions.addImageLayer(
        { url, thumb: url },
        { width: img.naturalWidth, height: img.naturalHeight },
      );
      if (isMobile) {
        onClose();
      }
    };
  };
  const addSvg = async (url: string) => {
    const ele = await fetchSvgContent(url);
    const viewBox = ele.getAttribute('viewBox')?.split(' ') || [];
    const width =
      viewBox.length === 4 ? +viewBox[2] : +(ele.getAttribute('width') || 100);
    const height =
      viewBox.length === 4 ? +viewBox[3] : +(ele.getAttribute('height') || 100);
    actions.addSvgLayer(url, { width, height }, ele);
    if (isMobile) {
      onClose();
    }
  };

  const handleUpload = (e: ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setImages((prevState) => {
          return prevState.concat([
            {
              url: reader.result as string,
              type: file.type === 'image/svg+xml' ? 'svg' : 'image',
            },
          ]);
        });
      };
      reader.readAsDataURL(file);
    }
  };
  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: visibility ? 'flex' : 'none',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Upload Images
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{
          margin: 16,
          background: '#3a3a4c',
          borderRadius: 8,
          color: '#fff',
          padding: '8px 16px',
          cursor: 'pointer',
          textAlign: 'center',
        }}
        onClick={() => inputFileRef.current?.click()}
      >
        Upload
      </div>
      <input
        ref={inputFileRef}
        accept="image/*"
        css={{ display: 'none' }}
        type="file"
        onChange={handleUpload}
      />
      <div css={{ padding: '16px' }}>
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(2,minmax(0,1fr))',
            gridGap: 8,
          }}
        >
          {images.map((item, idx) => (
            <div
              key={idx}
              css={{ cursor: 'pointer', position: 'relative' }}
              onClick={() =>
                item.type === 'image' ? addImage(item.url) : addSvg(item.url)
              }
            >
              <div css={{ paddingBottom: '100%', height: 0 }} />
              <div
                css={{
                  position: 'absolute',
                  inset: 0,
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                }}
              >
                <img
                  alt={item.url}
                  css={{ maxHeight: '100%' }}
                  loading="lazy"
                  src={item.url}
                />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/VideoContent.tsx
================================================
import XIcon from '@duyank/icons/regular/X';
import type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-core';
import { useEditor } from '@lidojs/design-editor';
import { type FC, useState } from 'react';
import { isMobile } from 'react-device-detect';
import { useAsync } from 'react-use';
import { v4 } from 'uuid';

export const VideoContent: FC<{ onClose: () => void }> = ({ onClose }) => {
  const [videos] = useState<
    { img: string; url: string; width: number; height: number }[]
  >([
    {
      img: 'https://template.canva.com/EAFaarkqz_0/2/0/400w-IVVQCZOr1K4.jpg',
      url: 'https://template.canva.com/EAFaarkqz_0/2/0/400w-xadNArxL6gA.mp4',
      width: 400,
      height: 334,
    },
  ]);
  const [isLoading] = useState(false);
  const { actions, query } = useEditor();
  useAsync(async () => {
    //const response = await axios.get<{ img: string; url: string; width: number; height: number }[]>('/videos');
    //setVideos(response.data);
    //setIsLoading(false);
  }, []);

  const addVideo = ({
    url,
    width,
    height,
  }: {
    url: string;
    width: number;
    height: number;
  }) => {
    actions.addVideoLayer({ url }, { width, height });
    if (isMobile) {
      onClose();
    }
  };

  const handleDrag = (
    event: React.DragEvent,
    { url, width, height }: (typeof videos)[number],
  ) => {
    const { clientX, clientY } = event;
    const pageSize = query.getPageSize(query.activePage());
    const ratio = pageSize.width / pageSize.height;
    const imgRatio = width / height;
    const w =
      ratio < imgRatio
        ? pageSize.width * 0.4
        : pageSize.height * imgRatio * 0.4;
    const h = w / imgRatio;
    const id: LayerId = v4();
    const data: {
      layer: LayerType;
      data: { rootId: LayerId; layers: SerializedLayers };
    } = {
      layer: 'Video',
      data: {
        rootId: id,
        layers: {
          [id]: {
            type: {
              resolvedName: 'VideoLayer',
            },
            props: {
              video: {
                url: url,
                boxSize: {
                  width: w,
                  height: h,
                },
                position: {
                  x: 0,
                  y: 0,
                },
                rotate: 0,
              },
              position: {
                x: 0,
                y: 0,
              },
              boxSize: {
                width: w,
                height: h,
              },
              rotate: 0,
            },
            locked: false,
            parent: 'ROOT',
            child: [],
          },
        },
      },
    };
    actions.startDragNDrop(data, { x: clientX, y: clientY });
    event.dataTransfer.clearData('text/plain');
    event.dataTransfer.setData('text/plain', JSON.stringify(data));
    event.dataTransfer.setDragImage(new Image(), 0, 0);
  };

  return (
    <div
      css={{
        width: '100%',
        height: '100%',
        flexDirection: 'column',
        overflowY: 'auto',
        display: 'flex',
      }}
    >
      <div
        css={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          flexShrink: 0,
          height: 48,
          borderBottom: '1px solid rgba(57,76,96,.15)',
          padding: '0 20px',
        }}
      >
        <p
          css={{
            lineHeight: '48px',
            fontWeight: 600,
            color: '#181C32',
            flexGrow: 1,
          }}
        >
          Images
        </p>
        <div
          css={{
            fontSize: 20,
            flexShrink: 0,
            width: 32,
            height: 32,
            cursor: 'pointer',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
          onClick={onClose}
        >
          <XIcon />
        </div>
      </div>
      <div
        css={{ flexDirection: 'column', overflowY: 'auto', display: 'flex' }}
      >
        <div
          css={{
            flexGrow: 1,
            overflowY: 'auto',
            display: 'grid',
            gridTemplateColumns: 'repeat(3,minmax(0,1fr))',
            padding: '16px',
            gridGap: 8,
          }}
        >
          {isLoading && <div>Loading...</div>}
          {videos.map((item, idx) => (
            <div
              key={idx}
              css={{
                cursor: 'pointer',
                position: 'relative',
                paddingBottom: '100%',
                width: '100%',
                '-webkit-user-drag': 'element',
              }}
              onClick={() => addVideo(item)}
              draggable={true}
              onDragStart={(e) => handleDrag(e, item)}
            >
              <img
                alt={item.img}
                css={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  height: '100%',
                  width: '100%',
                  objectFit: 'cover',
                }}
                loading="lazy"
                src={item.img}
              />
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/sidebar/index.ts
================================================
export * from './Sidebar';


================================================
FILE: src/features/design/components/tabs/TabList.tsx
================================================
import type { FC, ReactNode } from 'react';

interface SidebarTabProps {
  tabs: {
    name: string;
    icon: ReactNode;
    isBusiness?: boolean;
  }[];
  active: string | null;
  onChange: (e: React.MouseEvent, tab: string) => void;
}

export const SidebarTab: FC<SidebarTabProps> = ({ tabs, active, onChange }) => {
  const activeIdx = tabs.findIndex((tab) => tab.name === active);
  return (
    <div
      css={{
        color: '#5E6278',
        borderRight: '1px solid rgba(217, 219, 228, 0.6)',
        overflowY: 'auto',
        '@media (max-width: 900px)': {
          position: 'fixed',
          bottom: 0,
          left: 0,
          right: 0,
          background: '#fff',
          display: 'flex',
          justifyContent: 'center',
        },
      }}
    >
      <div
        css={{
          position: 'relative',
          '@media (max-width: 900px)': {
            display: 'flex',
            overflowX: 'scroll',
          },
        }}
      >
        {activeIdx >= 0 && (
          <div
            css={{
              background: '#fff',
              width: 72,
              height: 72,
              position: 'absolute',
              left: 0,
              top: 0,
              transform: `translateY(${activeIdx * 100}%)`,
              '@media (max-width: 900px)': {
                display: 'none',
              },
            }}
          >
            <div
              css={{
                position: 'absolute',
                height: 8,
                width: 8,
                right: 0,
                top: -8,
                background:
                  'radial-gradient(circle closest-side,transparent 0,transparent 50%,#fff 0) 200% 200% /400% 400%',
              }}
            />
            <div
              css={{
                position: 'absolute',
                height: 8,
                width: 8,
                right: 0,
                bottom: -8,
                transform: 'scaleY(-1)',
                background:
                  'radial-gradient(circle closest-side,transparent 0,transparent 50%,#fff 0) 200% 200% /400% 400%',
              }}
            />
          </div>
        )}
        {tabs.map((tab, idx) => (
          <div
            key={idx}
            css={{
              color: idx === activeIdx ? '#009ef7' : undefined,
              borderBottomRightRadius: idx === activeIdx - 1 ? 8 : 0,
              borderTopRightRadius: idx === activeIdx + 1 ? 8 : 0,
              position: 'relative',
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'center',
              alignItems: 'center',
              padding: '0 2px',
              height: 72,
              width: 72,
              minWidth: 72,
              minHeight: 72,
              cursor: 'pointer',
              ':hover': {
                color: '#009ef7',
              },
            }}
            onClick={(e) => onChange(e, tab.name)}
          >
            <div css={{ fontSize: 24 }}>{tab.icon}</div>
            <span css={{ fontSize: 10, lineHeight: 1.6, fontWeight: 600 }}>
              {tab.name}
            </span>
            {tab.isBusiness && (
              <div
                css={{
                  position: 'absolute',
                  background: '#fdebcf',
                  borderRadius: 9999,
                  fontSize: 9,
                  padding: '2px 4px',
                  top: 0,
                }}
              >
                Business
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
};


================================================
FILE: src/features/design/components/tabs/index.ts
================================================
export * from './TabList';


================================================
FILE: src/features/design/config/iframe.tsx
================================================
import type { LayerId, SerializedLayers } from '@lidojs/design-core';

export type IframeItem = {
  elements: [
    {
      rootId: LayerId;
      layers: SerializedLayers;
    },
  ];
  img: string;
};

export const iframeList: IframeItem[] = [
  {
    elements: [
      {
        rootId: '3f39efa2-4017-4c77-b3a9-c98456c629f0',
        layers: {
          '3f39efa2-4017-4c77-b3a9-c98456c629f0': {
            type: { resolvedName: 'IframeLayer' },
            props: {
              url: 'https://api.wo-cloud.com/content/widget/?geoObjectKey=6112695&language=en&region=US&timeFormat=HH:mm&windUnit=mph&systemOfMeasurement=imperial&temperatureUnit=fahrenheit',
              position: { x: 45.586770981507925, y: 121.96449211646916 },
              boxSize: {
                width: 350.8264580369844,
                height: 350.8264580369844,
              },
              rotate: 0,
              scale: 1,
            },
            locked: false,
            child: [],
            parent: 'ROOT',
          },
        },
      },
    ],
    img: '/assets/images/weather/1.png',
  },
];


================================================
FILE: src/features/design/config/line.tsx
================================================
import type { LayerId } from '@lidojs/design-core';
import type { DeepPartial, LineLayerProps } from '@lidojs/design-editor';
import type { ReactElement } from 'react';
import { v4 } from 'uuid';

export type Line = {
  id: LayerId;
  props: DeepPartial<LineLayerProps>;
  icon: ReactElement;
};

export const lines: Line[] = [
  {
    id: v4(),
    props: {
      style: 'solid',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="0.5"
          x2="32.5"
        />
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'shortDashes',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Short Dashes"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray="3,1"
          strokeLinecap="butt"
          x1="0.5"
          x2="32.5"
        />
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'dots',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Dots"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray="1,1"
          strokeLinecap="butt"
          x1="0.5"
          x2="32.5"
        />
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'bar',
      arrowEnd: 'bar',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Bar Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="1"
          x2="32"
        />
        <rect height="4" rx="0.5" stroke="none" width="1" x="0" y="-2" />
        <g transform="translate(33)">
          <rect height="4" rx="0.5" stroke="none" width="1" x="-1" y="-2" />
        </g>
      </svg>
    ),
  },

  {
    id: v4(),
    props: {
      style: 'dots',
      arrowStart: 'none',
      arrowEnd: 'arrow',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Dots with Arrow End"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray="1,1"
          strokeLinecap="butt"
          x1="0.5"
          x2="32.25"
        />
        <g transform="translate(33)">
          <path
            d="M -2.5,-1.5,-0.5,0,-2.5,1.5 "
            fill="none"
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'arrow',
      arrowEnd: 'arrow',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Arrow Start and End"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="0.75"
          x2="32.25"
        />
        <path
          d="M 2.5,-1.5,0.5,0,2.5,1.5 "
          fill="none"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
        <g transform="translate(33)">
          <path
            d="M -2.5,-1.5,-0.5,0,-2.5,1.5 "
            fill="none"
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },

  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'outlineDiamond',
      arrowEnd: 'outlineDiamond',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Dots"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="3.5"
          x2="29.5"
        />
        <path
          d="M 0.5,0 l 1.5,-1.5 1.5,1.5 -1.5,1.5 Z"
          fill="none"
          strokeLinejoin="round"
        />
        <g transform="translate(33)">
          <path
            d="M -0.5,0 l -1.5,-1.5 -1.5,1.5 1.5,1.5 Z"
            fill="none"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'diamond',
      arrowEnd: 'diamond',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Diamond Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="3.5"
          x2="29.5"
        />
        <path
          d="M 0.5,0 l 1.5,-1.5 1.5,1.5 -1.5,1.5 Z"
          fill="inherit"
          strokeLinejoin="round"
        />
        <g transform="translate(33)">
          <path
            d="M -0.5,0 l -1.5,-1.5 -1.5,1.5 1.5,1.5 Z"
            fill="inherit"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'none',
      arrowEnd: 'arrow',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Arrow End"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="0.5"
          x2="32.25"
        />
        <g transform="translate(33)">
          <path
            d="M -2.5,-1.5,-0.5,0,-2.5,1.5 "
            fill="none"
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'none',
      arrowEnd: 'triangle',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Triangle End"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="0.5"
          x2="30"
        />
        <g transform="translate(33)">
          <path
            d="M -2.5,-1.5,-0.5,0,-2.5,1.5 Z"
            fill="inherit"
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'circle',
      arrowEnd: 'circle',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Circle Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="4"
          x2="29"
        />
        <circle cx="2" fill="inherit" r="1.5" />
        <g transform="translate(33)">
          <circle cx="-2" fill="inherit" r="1.5" />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'square',
      arrowEnd: 'square',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Square Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="4"
          x2="29"
        />
        <rect
          fill="inherit"
          height="3"
          strokeLinejoin="round"
          width="3"
          x="0.5"
          y="-1.5"
        />
        <g transform="translate(33)">
          <rect
            fill="inherit"
            height="3"
            strokeLinejoin="round"
            width="3"
            x="-3.5"
            y="-1.5"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'outlineSquare',
      arrowEnd: 'outlineSquare',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Outline Square Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="4"
          x2="29"
        />
        <rect
          fill="none"
          height="3"
          strokeLinejoin="round"
          width="3"
          x="0.5"
          y="-1.5"
        />
        <g transform="translate(33)">
          <rect
            fill="none"
            height="3"
            strokeLinejoin="round"
            width="3"
            x="-3.5"
            y="-1.5"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'dots',
      arrowStart: 'triangle',
      arrowEnd: 'triangle',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Dots with Triangle Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray="1,1"
          strokeLinecap="butt"
          x1="3"
          x2="30"
        />
        <path
          d="M 2.5,-1.5,0.5,0,2.5,1.5 Z"
          fill="inherit"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
        <g transform="translate(33)">
          <path
            d="M -2.5,-1.5,-0.5,0,-2.5,1.5 Z"
            fill="inherit"
            strokeLinecap="round"
            strokeLinejoin="round"
          />
        </g>
      </svg>
    ),
  },
  {
    id: v4(),
    props: {
      style: 'solid',
      arrowStart: 'outlineCircle',
      arrowEnd: 'outlineCircle',
      boxSize: {
        height: 4,
      },
      color: 'rgb(94, 98, 120)',
    },
    icon: (
      <svg
        role="img"
        aria-label="Solid with Outline Circle Arrows"
        fill="currentColor"
        stroke="currentColor"
        style={{ overflow: 'visible' }}
        viewBox="0 -0.5 33 1"
        xmlns="http://www.w3.org/2000/svg"
      >
        <line
          fill="none"
          strokeDasharray=""
          strokeLinecap="butt"
          x1="4"
          x2="29"
        />
        <circle cx="2" fill="none" r="1.5" />
        <g transform="translate(33)">
          <circle cx="-2" fill="none" r="1.5" />
        </g>
      </svg>
    ),
  },
];


================================================
FILE: src/features/design/config/qrCode.tsx
================================================
import type { LayerId, SerializedLayers } from '@lidojs/design-core';

export type QrCodeItem = {
  elements: [
    {
      rootId: LayerId;
      layers: SerializedLayers;
    },
  ];
  img: string;
};

export const qrCodeList: QrCodeItem[] = [
  {
    elements: [
      {
        rootId: '3f39efa2-4017-4c77-b3a9-c98456c629f0',
        layers: {
          '3f39efa2-4017-4c77-b3a9-c98456c629f0': {
            type: { resolvedName: 'QrCodeLayer' },
            props: {
              text: 'https://lidojs.com',
              position: { x: 45.586770981507925, y: 121.96449211646916 },
              boxSize: {
                width: 350.8264580369844,
                height: 350.8264580369844,
              },
              rotate: 0,
              bgColor: 'rgb(255, 255, 255)',
              textColor: 'rgb(30, 30, 45)',
              logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAAACXBIWXMAAC4jAAAuIwF4pT92AABbQ0lEQVR4nO3defzVc/7//8f73UJ7qBSpLNWEZBlqjKWyFJEYYxnrxzKYGR/MMGMZzAyGMZbMxzbMIDvjVyGERGRG2cmSCElpIS1apN6/P2bM12c+ltQ553nOeV6vl4t/Phc9X3cfb03nds55vWrq6urqAgAAAKhqtakHAAAAAMUnAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIQP3UA4CV8/7778cf//jH1DOK5sgjj4zu3bunngEAAFVDAIAKNWvWrLjssstSzyia3r17CwAAAFBAvgIAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyED91AMAAAAKZenSpTF58uR48803Y+rUqTF9+vSYPn16zJo1K+bOnfvvv5YuXRrLli2L+fPn//vXNm7cOBo2bBgREY0aNYpmzZr9+68111wzWrduHW3atIl27dpFmzZtolOnTtGxY8do0KBBqn9c+FYEAAAAoCJ98MEHMX78+HjhhRfi2WefjQkTJsS7774by5YtW6nz5s6d+61/TW1tbay33nqxwQYbRLdu3WKzzTaLzTbbLDbddNNo1qzZSu2AYhEAqshVV10VS5YsST2j4DbeeOPYddddU88AIuLee++Nt956K/WMotlwww1jzz33TD0DKIHBgwennlA0xx13XKy22mqpZxTFhx9+GCNHjoxHH300xowZE2+++WbqSbF8+fJ499134913341HH3303//3mpqa6NatW3z/+9//918bbbRRwqUQUVNXV1eXegSF0bJly5WqluXusMMOixtuuCH1jLLzwgsvxBZbbJF6RtEMGzYsBg0alHoG/2HQoEFx9913p55RNHvttVcMHz489QygBGpqalJPKJo5c+ZEy5YtU88omClTpsTtt98e9957b/z973+P5cuXp5600tZff/3Yfffdo3///rHTTjtFo0aNUk8iMz4BAAAAlJWFCxfGbbfdFjfddFM8/vjjUS3vWb799ttxxRVXxBVXXBFNmjSJPffcM370ox/FrrvuWrWf2qC8eAoAAABQFiZPnhw///nPY911142jjjoqxowZUzUv/v/TJ598ErfffnsMHDgw2rVrFyeddFK88cYbqWdR5QQAAAAgqYkTJ8bhhx8eXbp0iUsvvTQ+/vjj1JNKas6cOTF48ODo2rVr7LLLLjFy5MjUk6hSAgAAAJDE+++/H4cffnh069YthgwZstJ3768mo0aNit122y169uwZw4cPr9pPQJCGAAAAAJTU4sWL45xzzokuXbrEkCFDvMj9EuPHj4+99947tttuuxg/fnzqOVQJAQAAACiZJ598MjbffPM466yzYuHChannlL2///3v0bNnzzj44INj1qxZqedQ4QQAAACg6JYsWRK/+MUvYvvtt4+JEyemnlNxbrnllujWrVvccsstqadQwQQAAACgqN58883Ydttt45JLLvFx/1Xw4YcfxsEHHxw/+MEPYs6cOannUIEEAAAAoGjuu+++2HLLLeO5555LPaVqDB06NL773e/Gs88+m3oKFUYAAAAAiuLiiy+OgQMHxvz581NPqTqTJ0+O73//+3HrrbemnkIFEQAAAICCqqurixNOOCFOPvnkWL58eeo5VWvJkiVx0EEHxQUXXJB6ChVCAAAAAApm2bJlceSRR8af/vSn1FOycdppp8Xxxx/v/gp8IwEAAAAoiLq6ujj88MPj+uuvTz0lO5dffnmceuqpqWdQ5gQAAACgIH72s5/FzTffnHpGti688MI444wzUs+gjAkAAADAKvvd734XV155ZeoZ2fv9738f11xzTeoZlCkBAAAAWCV33HFHnH322aln8C8//elP45FHHkk9gzIkAAAAACvtmWeeif/6r/9KPYMv+Oyzz+KHP/xhvPfee6mnUGYEAAAAYKXMnTs39t9//1i0aFHqKfyHOXPmxIEHHhifffZZ6imUEQEAAABYKUcddVRMnjw59Qy+wpNPPhm/+93vUs+gjAgAAADAt3bDDTfEXXfdlXoG3+D3v/99PPfcc6lnUCYEAAAA4Fv54IMP4sQTT0w9gxWwbNmyOPLII30VgIgQAAAAgG/puOOOi7lz56aewQp64YUX4pJLLkk9gzIgAAAAACts5MiRMXz48NQz+JbOPffcmDFjRuoZJCYAAAAAK+Szzz6Lk046KfUMVsL8+fPj17/+deoZJFY/9QBg5ay55ppx2GGHpZ5RNB06dEg9AQD4D1deeWW8/vrrqWewkv7617/GSSedFBtvvHHqKSQiAECF6tChQ9xwww2pZwAAmVi0aFGcf/75qWewCurq6uJ3v/td3H777amnkIivAAAAAN/oz3/+c3zwwQepZ7CK7rzzznj11VdTzyARAQAAAPhaixcvjvPOOy/1DAqgrq7Ov8uMCQAAAMDXuvnmm2P27NmpZ1Agd955Z7z//vupZ5CAAAAAAHylurq6GDx4cOoZFNBnn30WV1xxReoZJCAAAAAAX2n06NHxyiuvpJ5Bgf35z3+OxYsXp55BiQkAAADAV/rLX/6SegJF8NFHH8Xw4cNTz6DEBAAAAOBLzZkzJ4YNG5Z6BkVy3XXXpZ5AiQkAAADAl7r11ltjyZIlqWdQJKNGjYopU6aknkEJCQAAAMCXuvPOO1NPoIjq6ur8O86MAAAAAPwfM2fOjLFjx6aeQZHdddddqSdQQvVTDwDIxfLly+Pdd9+Nt99+O955552YMWNGfPTRR/HRRx/FsmXL4uOPP46IiNVWWy0aNWoU9erVi2bNmkWrVq2idevW0aZNm1h33XWjS5cu0bJly6T/LPC5uXPnxrRp0+L999+PGTNmxIcffhhz5879919Lly6Nurq6mDt37r9/TZMmTaJBgwYREdGyZcto0aLFv/9q165dtGvXLtZZZ51o06ZN1NTUpPpHg+wNHTo0li9fnnoGRTZu3LiYMmVKdOjQIfUUSkAAACiSmTNnxuOPPx6PPfZYPPvss/Hyyy/HJ598UpCz11577fjOd74TW265ZWy33Xax3XbbRZs2bQpyNnyZyZMnx/PPPx8vvfRSvPnmmzFx4sSYNGlSzJs3r2jXbNCgQWywwQbRtWvX2GijjWLjjTeOLbbYIjbddNNo2LBh0a4L/NOIESNST6BEhg4dGieeeGLqGZSAAAAVatGiRTFx4sTUM4pm/fXXjxYtWqSe8a299957ceutt8bw4cNj3LhxUVdXV5TrzJgxI2bMmBFjxoyJSy+9NCIiNt5449hnn31in332iS222KIo1yUPixcvjqeeeirGjBkTY8eOjaeffvp/vYNfKkuXLo2JEyf+n9/r6tevH927d49tt902dtxxx9h+++2jbdu2Jd8H1WzJkiXx6KOPpp5BiTzwwAMCQCYEAKhQEydOrOoXecOGDYtBgwalnrFC6urq4oEHHoj/+Z//iQcffLBoL/q/yauvvhqvvvpqnHvuudG5c+f46U9/GkcccUQ0a9YsyR4qy9tvvx333ntvjBgxIh5//PGyvuv3Z599Fs8//3w8//zzccUVV0RExCabbBIDBgyIPfbYI7bddtuoV69e4pVQ2caOHRsLFy5MPYMSGTNmTCxatCgaNWqUegpF5iaAAKtg+PDhsfnmm8eAAQNi5MiRyV78/6dJkybFiSeeGOuuu2788pe//Pf9BeCL3nvvvbjwwgtj8803jw022CBOOOGEePjhh8v6xf9XeeWVV+LCCy+MHXbYIdq0aRPHHntsjB07tmz+m4RKM2rUqNQTKKElS5bEY489lnoGJSAAAKyECRMmxI477hh77713vPTSS6nnfKX58+fHH//4x9hwww3jsssui88++yz1JBL77LPP4q677oq+fftGx44d41e/+lW8+OKLqWcV1EcffRR//vOfY/vtt49OnTrFueeeGx988EHqWVBRnnzyydQTKLFHHnkk9QRKQAAA+BaWLVsW55xzTmy11Vbx+OOPp56zwj766KM48cQTo1evXvHqq6+mnkMCc+bMiXPOOSc6duwYP/zhD+PRRx/N4t3xKVOmxJlnnhkdOnSIAw44IJ555pnUk6DsffrppzF+/PjUMyixSvpzDStPAABYQR988EHsvPPOcdZZZ8Wnn36aes5KefbZZ2PrrbeOm2++OfUUSmTGjBlx6qmnxnrrrRdnnXVWTJs2LfWkJJYuXRp33HFHbL311tG/f/8YM2ZM6klQtp555pmK/CoQq+a5556LBQsWpJ5BkQkAACvg5Zdfjq233roqvh+3cOHCOOSQQ+LMM8/M4h3gXC1YsCDOPvvs2GCDDeIPf/hDwR5BWQ0efPDB6N27d/Tr16+sv8IDqTz33HOpJ5DAsmXL4qmnnko9gyITAAC+wdixY2O77baLqVOnpp5SUOeee24cdthhsWzZstRTKKC6urr461//Gp07d47f/e537uL9NR566KHYfPPN44gjjoiZM2emngNl4/nnn089gUTGjRuXegJF5jGAAF9j3LhxMWDAgJg3b17qKUVx0003xbJly+LGG2/02LQq8Nprr8WPf/zjGDt2bOopFaOuri6uv/76GD58eFx44YVx5JFHRk1NTepZkFQuAaBt27ax4447Rvfu3aNTp07RpEmTiPjnPRDee++9ePHFF2P06NHx/vvvJ15aOs8++2zqCRSZAADwFSZOnBj9+vWr2hf/n7v11lujtrY2brzxRi98KtSyZcvi/PPPj3POOadi70+R2pw5c+Loo4+Om266KYYMGRKdOnVKPQmSWLZsWbzyyiupZxTV7rvvHieffHLsuOOOUVv79R+IrqurizFjxsR5552XxaMR3Si1+vkKAMCXmDNnTgwcODDmzp2bekpJ3HzzzXHGGWeknsFKeOedd6J3795x5plnevFfAI8//nj06NEjbr311tRTIIl33nmnan8vadGiRQwdOjTuu+++6NOnzze++I+IqKmpid69e8fDDz8ct912WzRt2rQES9N57733YtasWalnUEQCAMCXOPzww+ONN95IPaOkzj///LjuuutSz+BbuOeee6JHjx4+8l9g8+bNi4MOOij+67/+KxYvXpx6DpTUxIkTU08oipYtW8YTTzwRe++990qfccABB8SYMWOiefPmBVxWfl5++eXUEygiAQDgP1x99dVxzz33pJ6RxHHHHef7fxWgrq4ufvOb38Ree+1V9V9RSemGG26I7bffPt57773UU6BkqjV+33rrrdG9e/dVPmfLLbeMu+66qwCLyle1fwUkdwIAwBe89957ccopp6Sekcynn34a++23XzZffahECxcujB/84Afx29/+NvWULDzzzDPx3e9+152xycZbb72VekLBHXHEEbHbbrsV7LxddtkljjzyyIKdV25ef/311BMoIgEA4AtOOOGEWLBgQeoZSU2ePDlOOOGE1DP4EnPmzIldd901hg0blnpKVmbOnBl9+vSJ+++/P/UUKLpqe+RtgwYN4uyzzy74uWeeeeYK3UOgEr322mupJ1BE1flTC7ASRo8e7YXVvwwZMiQefPDB1DP4gqlTp8Z2220XTz75ZOopWVq0aFEMHDgwbrnlltRToKimTJmSekJBDRo0KDp06FDwczt27Bh9+/Yt+LnloFrvA8E/CQAA8c/vVP/yl79MPaOsHH300fHJJ5+knkH888V/796949VXX009JWvLli2LQw89VASgqlXbJwD233//op3dr1+/op2d0rRp02LJkiWpZ1AkAgBARNx9991ufvcf3nvvvfjDH/6Qekb2pk2bFr17967K7+VWouXLl4sAVK1ly5bFzJkzU88oqB133LFoZ2+11VZFOzu1d999N/UEikQAALJXV1fnhmpf4aKLLqq6d4Mqydy5c2O33Xbz4r/MfB4BRowYkXoKFNSHH36YekJBrbPOOtGqVauind++ffuinZ3a22+/nXoCRSIAANm7//7744UXXkg9oywtWrQozjzzzNQzsvTpp5/GPvvsEy+99FLqKXyJ5cuXx3777Rf/+Mc/Uk+Bgqm2AFCM7/5/0ZprrlnU81MSAKqXAABkb/DgwaknlLWbbrop3nzzzdQzsnPEEUfE6NGjU8/ga3x+Y8DJkyenngIFMXv27NQTCmqNNdYo6vn16tUr6vkpTZ8+PfUEikQAALI2YcKEGDVqVOoZZW3ZsmVx0UUXpZ6RlYsvvth3zCvE7NmzY9CgQW6YSVX46KOPUk8oqIYNG6aeULE++OCD1BMoEgEAyNq1116bekJFuO6662LatGmpZ2ThkUce8USKCvPyyy/HEUccEXV1damnwCqptpA1b9681BMqlk8AVC8BAMjW4sWL46abbko9oyIsXbo0rrnmmtQzqt706dPjwAMPjOXLl6eewrd05513xhVXXJF6BqyShQsXpp5QUNX2lYZSmjFjRuoJFIkAAGRrxIgRMWfOnNQzKsY111wTS5cuTT2jatXV1cXhhx8es2bNSj2FlXTyySfHhAkTUs+AlbZgwYLUEwrqnXfe8cmclVRtj4Pk/xEAgGzdeuutqSdUlOnTp8djjz2WekbVGjx4cDz00EOpZ7AKlixZEgceeGAsWbIk9RRYKfPnz089oaDmz5/vMaorqdruB8H/IwAAWVqwYEHcd999qWdUnGr7eGi5ePPNN+OMM85IPYMCmDBhQpx77rmpZ8BKWbZsWeoJBff888+nnlCR5s2b5+toVUoAALL0wAMPxKeffpp6BkRdXV0cc8wxsWjRotRTKJALLrjAVwGgTPz9739PPaFi+ZpkdRIAgCzdfffdqSdAREQMGTIkRo8enXoGBfTZZ5/FUUcd5bvHUAb87/3K+/jjj1NPoAgEACA7y5cvjwcffDD1DIgFCxbEaaedlnoGRTBu3Li45ZZbUs+A7L399ts+kbOSqu2eEPyTAABk5/nnn/doIMrC+eefHx988EHqGRTJqaee6r4ZUAb+9re/pZ5Qkfz+VZ0EACA7Dz/8cOoJEFOmTIlLLrkk9QyK6P3334+LLroo9QxYYbW11fnS4Nprr43PPvss9YyK415J1ak6/ysH+Bpjx45NPQHi97//fSxevDj1DIrskksu8T1aKkbz5s1TTyiK6dOnx/Dhw1PPqDhz585NPYEiEACArCxfvtwdgUluypQpcd1116WeQQnMnTs3Bg8enHoGrJBq/QRARMSf/vSn1BMqjk8AVKfq/a8c4EtMmjTJY21I7g9/+EMsXbo09QxKZPDgwbFgwYLUM+AbtWrVKvWEonniiSfiscceSz2jonzyySepJ1AEAgCQlRdeeCH1BDI3Z86cGDJkSOoZlNDcuXPj+uuvTz0DvtFaa62VekJRnXbaaR7PSfYEACArL774YuoJZO4vf/mLd1UydNlll8Xy5ctTz4CvVe0B4KmnnhLjyJ4AAGTFJwBIafny5XH55ZennkECb731Vtx///2pZ8DXatu2beoJRffLX/7S41fJWv3UAwBKSQAgpYceeiimTJmSegaJ/OUvf4k99tgj9Qz4Sm3bto369etX9SPzPvzwwzj66KPj3nvvXeWz6tWrFz169CjAqvLUsmXL1BMoAgEAyMbs2bNj+vTpqWeQsRtuuCH1BBK67777YubMmdGmTZvUU+BL1a9fPzp06BCTJ09OPaWoRowYEZdddlmccMIJq3ROs2bNvLFAxfEVACAbb775ZuoJZGzOnDkxbNiw1DNI6LPPPotbbrkl9Qz4WhtssEHqCSVxyimnxLhx41LPgJLzCQAgG++8807qCck0adIkdtppp9hiiy2ic+fO0bx582jcuHHMmTMnpk2bFk8//XQ89NBDMXPmzNRTq9bw4cOze6Zy+/bto3///rHZZpvFeuutFy1atIgFCxbErFmz4pVXXolHHnkkuxtz3nHHHXHSSSelngFfaaONNopRo0alnlF0S5cujUGDBsXTTz8d7du3Tz0HSkYAALKRYwDo1KlTnH322bH//vtHo0aNvvbvXbp0aQwbNixOPfXUePvtt0u0MB9Dhw5NPaFkttpqqzjnnHOif//+UVNT87V/78svvxznnXde3HHHHSVal9a4ceNiypQp0aFDh9RT4EttuummqSeUzAcffBADBw6MsWPHRuPGjVPPgZLwFQAgG7kFgJNOOikmTpwYhx9++De++I+IaNCgQey3337xyiuvxOGHH178gRmZO3duPPTQQ6lnFF1NTU2ce+65MX78+Nhtt92+8cV/RET37t3j9ttvj/vuuy+bG04NHz489QT4SptvvnnqCSX1/PPPx7777htLly5NPQVKQgAAspFTADjvvPPikksuiYYNG37rX9uoUaO47rrr4tBDDy3Csjw9/PDDWXz8/4Ybbogzzjgjamu//R8vdt9993j88cdjzTXXLMKy8nL33XenngBfqUePHisU76rJAw88EEcffXTU1dWlngJFJwAA2cglAOy7775x+umnr9IZNTU18ec//zm6d+9eoFV5e/DBB1NPKLozzzxzlaNR9+7d429/+9tKBYRK8uSTT8bChQtTz4Av1bRp0+jcuXPqGSU3ZMiQ+MlPfiICUPWq+39hAb7go48+Sj2h6Bo3bhyDBw8uyFmrr756XHbZZQU5K3fV/vH/LbfcMs4+++yCnNW3b99VfjRXuVuyZEk8/vjjqWfAV9p+++1TT0ji6quvjlNPPTX1DCgqAQDIRg4B4Mgjj4x11123YOf16dMn+vTpU7DzcjRp0qSYMmVK6hlFdfHFF0e9evUKdt5ZZ50Va621VsHOK0c53GWdytW7d+/UE5K58MIL45RTTvFJAKqWAABkYd68ebFs2bLUM4ruiCOOKPiZbgi4ap588snUE4qqZ8+eBX+x0LJlyzjmmGMKema5GTt2bOoJ8JVy/QTA5y666CJfB6BqCQBAFmbNmpV6QtF16NChKHdv3nvvvaN+fU+NXVn/+Mc/Uk8oqmOPPbYo5/74xz8uyrnl4rnnnovFixenngFfqmPHjtGpU6fUM5K6+uqr45hjjsnizQPyIgAAWcjh4/99+/YtyrnNmjWLnj17FuXsHFRzAGjQoEHsvffeRTm7Y8eOVf1zt3Tp0njuuedSz4CvNHDgwNQTkrv22mvj0EMPFQGoKgIAkIUcAsBOO+1UtLN33nnnop1dzRYuXBivvPJK6hlFs80220SLFi2Kdn7//v2LdnY5ePrpp1NPgK8kAPzTrbfeGvvuu28Wj3IlDwIAkIVFixalnlB0xQwAxfp0QbV79dVXY/ny5alnFM2OO+5Y0een9tJLL6WeAF9phx12KGrgqyTDhw+Pfffd1+M7qQoCAEAVaNeuXbRr165o5/fq1ct9AFZCtb/A++53v1vU87faaquinp/ayy+/nHoCfKUGDRrEnnvumXpG2bj33ntjjz32EAGoeAIAkIV58+alnlBU3bt3L+r5DRs2jE022aSo16hG1R4Aiv1z17x58+jYsWNRr5HShAkTqvoTIlS+Qw45JPWEsvLoo4/GLrvsEnPnzk09BVaaAABkodr/kL3ZZpsV/Ro9evQo+jWqzZtvvpl6QtHU1NSU5MX5+uuvX/RrpLJo0aJ4//33U8+Ar7TTTjsV9dNllejvf/977LrrrlncW4jqJAAAWaj2AFDsd2IjoiiPGKx2kydPTj2haNq2bRsNGjQo+nU6dOhQ9GukVM0/I1S+evXqxcEHH5x6RtkZP3587Ljjjlk8YpjqIwAAWaj2rwCU4l3Sbt26Ff0a1aSuri7eeuut1DOKZu211y7JdVq3bl2S66QiAFDujjjiiNQTytKECRNihx12iKlTp6aeAt+KAABQBUrxEc1q/i52MUyfPr2qHxu1xhprVNV1UhEAKHff+c53ol+/fqlnlKXXX389evfuHVOmTEk9BVaYAABQBdq3b18V16gm1f7d7ubNm5fkOk2aNCnJdVKZMWNG6gnwjf77v/879YSy9dZbb8W2225b1fd8oboIAAAVbo011ojVV1+96Ndp1qxZyV70VQPfDS2Man/8ZLWHIqpD//79o3PnzqlnlK33338/dtxxR4/2pCIIAAAVrpQvytu2bVuya1W6adOmpZ5QFZo2bZp6QlEJRVSC2tra+NWvfpV6RlmbNm1a9O3bt+of/0rlEwAAKlyLFi1Kdq1GjRqV7FqVzgs7VoSfEyrFIYccEuutt17qGWVt9uzZsf3228e4ceNST4GvJAAAVLiampqSXava340tpGp/8gSFMWfOnNQTYIU0bNgwTj/99NQzyt68efNi1113jcceeyz1FPhSAgBAhWvZsmXJrlXt38cuJAGAFeHnhEpyxBFHeCLMCpg3b17svvvuMXr06NRT4P8QAABYYaWMDZVu/vz5qSdQAerq6mLBggWpZ8AKadiwYVxwwQWpZ1SERYsWRf/+/eOee+5JPQX+FwEAAIrAO7usqMWLF6eeACts//33j169eqWeURGWLl0a++67b9x5552pp8C/CQAAAAn5BACVpKamJi699NLUMyrG0qVL48ADD4xbbrkl9RSICAEAAIpiyZIlqScAFEWvXr3ikEMOST2jYixfvjwOPvjguOqqq1JPAQEAAIph0aJFqScAFM0ll1wSrVq1Sj2jovzkJz+JwYMHp55B5gQAAICEPvnkk9QT4Ftr1apVXHzxxalnVJyTTjrJjRRJSgAAAEho6dKlqSfASjn00ENj5513Tj2j4px22mlx2mmnpZ5BpgQAAABgpVx//fUeEbsSLrjggvjFL34RdXV1qaeQGQEAAABYKe3bt4+rr7469YyKdMkll8RPfvITEYCSEgAAAICVtv/++8fBBx+cekZFuvrqq+PQQw+NZcuWpZ5CJgQAAABglVx55ZXRuXPn1DMq0s033xwHH3xwfPrpp6mnkAEBAAAAWCXNmjWLoUOHRuPGjVNPqUi333577LvvviIARScAAAAAq2zTTTeNa6+9NvWMinXvvffGgAEDYuHChamnUMUEAAAAoCB+9KMfxfHHH596RsUaNWpU7LHHHjFv3rzUU6hSAgAAAFAwl156afTr1y/1jIr16KOPxi677BJz585NPYUqJAAAAAAFU69evbjzzjuje/fuqadUrPHjx8euu+4qAlBwAgAAAFBQzZs3jxEjRkTbtm1TT6lYIgDFIAAAAAAF16FDhxg5cmQ0b9489ZSKNX78+OjTp0/MmjUr9RSqhAAAAAAURY8ePeKBBx6IRo0apZ5SsZ5//vno27evCEBBCAAAAEDRbLvttjFs2LBo0KBB6ikVa8KECTFgwABfB2CVCQAAAEBR9evXL+666y4RYBU8/fTTMXDgwFi4cGHqKVQwAQAAACi6gQMHigCr6PHHH4999tknPv3009RTqFACAAAAUBIiwKp78MEH44ADDohly5alnkIFEgAAAICSGThwYAwdOtSNAVfBsGHD4qijjoq6urrUU6gwAgAAAFBSe+yxR4wYMSKaNWuWekrFuuGGG+Lss89OPYMKIwAAAAAl17dv33j44YejRYsWqadUrHPOOSf+8pe/pJ5BBREAAACAJHr27BlPPPFEtGrVKvWUinXMMcfEAw88kHoGFUIAAAAAkunevXv8/e9/j3XXXTf1lIq0fPny2HfffePZZ59NPYUKIAAAAABJde7cOcaOHRsbbrhh6ikVaeHChTFgwICYOnVq6imUOQEAAABIrlOnTvHYY4/FpptumnpKRZoxY0bstddesXDhwtRTKGMCAAAAUBbat28fo0ePjq222ir1lIr03HPPxZFHHunxgHwlAQAAACgbrVu3jkceeSS23Xbb1FMq0u233x7nn39+6hmUKQEAAAAoKy1atIiHH344dtppp9RTKtKvf/3ruOeee1LPoAwJAAAAQNlp3Lhx3H///bHnnnumnlJx6urq4qCDDopJkyalnkKZEQAAAICy1LBhwxg6dGj86Ec/Sj2l4ixYsCB++MMfxuLFi1NPoYwIAAAAQNmqX79+3HjjjfHjH/849ZSK8+KLL8bxxx+fegZlRAAAAADKWr169eLqq6+On//856mnVJy//OUv7gfAvwkAAABA2aupqYmLL744zjrrrNRTKs7RRx8ds2bNSj2DMiAAAAAAFeO3v/1t/PGPf0w9o6LMnDkzfvKTn6SeQRmon3oAAAAr5+OPP47BgwennlFwbdu2jWOPPTb1DMrYySefHE2aNImf/exnsXz58tRzKsJdd90V9957r6cqZE4AAACoUB9//HH89re/TT2j4Hr06CEA8I2OO+64aN68eRx66KEiwAr66U9/Gn369ImmTZumnkIivgIAAABUpIMOOiiGDRsWDRo0SD2lIrz33ntxzjnnpJ5BQgIAAABQsQYOHBj33XdfNGrUKPWUijB48OCYPHly6hkkIgAAAAAVbZdddokHH3wwmjdvnnpK2fv000/j5JNPTj2DRAQAAACg4m2//fYxatSoaNWqVeopZW/YsGHx1FNPpZ5BAgIAAABQFbbeeusYPXp0tGvXLvWUsnfaaaelnkACAgAAAFA1unfvHo8//nh06NAh9ZSy9thjj8UjjzySegYlJgAAAABVZaONNoonn3wyNtxww9RTyponAuRHAAAAAKpO+/bt47HHHotu3bqlnlK2xowZ414AmREAAACAqtS+ffsYM2ZMbLrppqmnlK3f//73qSdQQgIAAABQtVq3bh2jR4+O7373u6mnlKURI0bEpEmTUs+gRAQAAACgqrVu3TpGjRoV22yzTeopZaeuri4uv/zy1DMoEQEAAACoei1atIiHHnpIBPgS119/fSxYsCD1DEpAAAAAALLweQTwdYD/bf78+XHbbbelnkEJCAAAAEA2WrRoEffff78bA/6Hv/71r6knUAICAAAAkJXPbwwoAvw/48aNi1dffTX1DIpMAAAAALLzeQTYaKONUk8pGzfeeGPqCRSZAAAAAGSpdevW8fDDD8c666yTekpZ+Nvf/pZ6AkUmAAAAANnq1KlTjBw5Mlq0aJF6SnKTJ0+OZ599NvUMikgAAAAAsta9e/e47777olGjRqmnJHfXXXelnkARCQAAAED2vv/978edd94ZtbV5v0QaOXJk6gkUUd4/3QAAAP+yxx57xGWXXZZ6RlIvvPBCzJw5M/UMikQAAAAA+Jef/exncdxxx6WekdTDDz+cegJFIgAAAAB8wZ/+9KfYZZddUs9IZvTo0aknUCQCAAAAwBfUr18/7rzzzujatWvqKUn84x//SD2BIhEAAAAA/kPLli3j//v//r9o3Lhx6ikl9/rrr8fHH3+cegZFUD/1AAAAIL3BgwfH4MGDU88ouIsuuij23Xfflfq1m2yySfz5z3+OQw45pMCryltdXV2MHz8+dt1119RTKDABAAAAiI8//jjefffd1DMKbsGCBav06w8++OAYMWJE3HHHHQVaVBmee+45AaAK+QoAAADA17jqqquiXbt2qWeU1IQJE1JPoAgEAAAAgK+xxhprxOWXX556RkkJANVJAAAAAPgG++yzT+yxxx6pZ5TM66+/Hp999lnqGRSYAAAAALACLr744mjQoEHqGSWxZMmSmDJlSuoZFJgAAAAAsAK6dOkSxx13XOoZJfPOO++knkCBCQAAAAAr6NRTT43VV1899YySePvtt1NPoMAEAAAAgBXUrl27OOaYY1LPKAkBoPoIAAAAAN/CSSedFPXq1Us9o+imTp2aegIFJgAAAAB8Cx07doy999479YyimzFjRuoJFJgAAAAA8C39+Mc/Tj2h6GbPnp16AgUmAAAAAHxLO+20U7Rv3z71jKKaPn166gkUmAAAAADwLdXW1sZ+++2XekZR+QRA9REAAAAAVsKgQYNSTyiqJUuWxNKlS1PPoIAEAAAAgJWw7bbbRosWLVLPKKoFCxaknkABCQAAAAAroV69etGnT5/UM4pq7ty5qSdQQAIAAADAStpxxx1TTyiq+fPnp55AAQkAAAAAK6lnz56pJxTVsmXLUk+ggAQAAACAlbTllltGvXr1Us8omk8//TT1BApIAAAAAFhJq622WnTp0iX1jKJZuHBh6gkUkAAAAACwCjbZZJPUE2CFCAAAAACroGvXrqknwAoRAAAAAFbBhhtumHoCrBABAAAAYBV06NAh9QRYIQIAAADAKmjbtm3qCbBCBAAAAIBV0KpVq9QTiqZhw4apJ1BAAgAAAMAqqOYA0Lhx49QTKCABAAAAYBXUq1cvWrdunXpGUdTWeslYTfzbBAAAWEXV+imA5s2bp55AAQkAQBbUawCgmFZbbbXUE4qiWv+5cuVPxEAW1GsA+Hpu9rZqWrRokXpCUTRq1Cj1BApIAABghS1dujT1BKg63l2jXLjZG1+mWbNmqSdQQAIAACvsk08+ST0Bqo531yi0mpqalfp1TZo0KfCSvFRjQGnatGnUq1cv9QwKSAAAgCLwri6Qysp+FN3vW6umGr9C0bJly9QTKDABAKDCzZ8/v2TXWrx4ccmuVem8qwtUmmq9X87HH3+cekLFEgCqjwAAZKFa/1ATEbFkyZKSXUsAgMKrxncNSWf11Vdf6V8rXPKfBIDqIwAAWWjQoEHqCUWzYMGC1BP4Eqvyh3DyUo3fGyadVfkY/xprrFHAJeXDJwBWXtu2bVNPoMAEACAL1XwH27lz55bsWm4CuOLcTIsVVc2/P1F6K3sDwIjqDQAfffRRSa6zcOHCklynlFq3bp16AgUmAABZqOYbG82dOzeWL19ekmt5DOCK86KOFbHaaqu5wzYFtSof2V5zzTULN6SMzJgxoyTXKWWQLxWfAKg+AgCQhWr+XmNdXV3Mnj27JNeaN29eSa5TDar5vhMUjk+KfLlFixalnlCx1lprrZX+tdX6CYApU6aU5Dql+qRBKbVp0yb1BApMAACyUO1/yC5VAJgzZ05JrlMNmjZtmnoCFaBaX3CtqmL/nlaNL9Q+tyoBoLa2Nlq1alXANeVh4sSJJbnO1KlTS3KdUlpvvfVST6DABAAgC9V8E8CI0ry7UY0fbSwm35tkRVTji61C+Oijj4r6iNNZs2YV7ezUViUARESsvfbaBVpSPubMmRPTp08v6jVmzpxZlU/K6dChQ+oJFJgAAGSh2h9j88477xT9GqX6DmW18LFJVoQA8NXeeOONop399ttvF+3s1FY1AKy77roFWlJexo0bV9TzX3311aKen4oAUH0EACAL1f5x7Lfeeqvo1/jggw+Kfo1q4oVdYSxZsiT1hKJa1VDUokWLAi0pP08//XTRzn7llVeKdnZq7dq1S/rry9Xjjz9e1POff/75op6fQvPmzav695hcCQBAFurXr1/V9wF47bXXin6NadOmFf0a1aQaP0abQrXfDG5VA8CqPPKt3D366KNFO/upp54q2tmpderUaZV+/TrrrFOYIWXm/vvvL+r5Tz75ZFHPT2GDDTZIPYEiEACAbFTz1wBefvnlol+jFF8zqCbrrrtuVb84+/DDD0tynWJ+D7wcrOrHa6s5bI4cObIo36leunRpjBo1quDnlosNN9xwlX79+uuvX6Al5WXixInx4osvFuXsJUuWxMMPP1yUs1Pq2rVr6gkUgQAAZKOaA8CUKVOK/oJs0qRJRT2/2jRo0KBq30mLKF0AqPZ7T6xqAGjQoEHVPuZ03rx5MXTo0IKfe//998fHH39c8HPLxaq+a7uqAaGcXXvttUU596GHHqrKx+R26dIl9QSKQAAAslHNASAi4oknnijq+W+++WZRz69G1XzzpFLdRO3dd98tyXVS6dix4yqfUc1fN/njH/8YdXV1BT3zkksuKeh55aRp06ar/ASSav7Y9/XXX1+UR0z+6U9/KviZ5aBz586pJ1AEAgCQDQFg5dXV1cULL7xQtPOr1ap+F7ecLV68uCQvzkv1/O5UCvEzUs1PnHjhhRfipptuKth5w4cPL/rN4FIqxAu29dZbL+rXr1+ANeVn4cKFcfrppxf0zNGjR1ftV0o22WST1BMoAgEAyEa1B4D77ruvaGe/+eabVfnxxmKr9u9PPvvss0U9f/78+VX9yZN11lknmjVrtsrnrOo7vuXuxBNPjClTpqzyOVOnTo0f//jHBVhUvrbccstVPqNevXpV/c7vtddeG/fee29Bzvroo4/iyCOPLMhZ5aZevXqx8cYbp55BEQgAQDZW9dnI5W7ixInx+uuvF+Xsary7cSl069Yt9YSiGjt2bFHPf+KJJwr+8e9yUqhAVK2PbfvcnDlzYsCAAat0P4h33303dtppp5g1a1YBl5WfLbbYoiDnVPs7vz/60Y9i9OjRq3TG7NmzY7fddqvaG+R26dIlVl999dQzKAIBAMhG27ZtU08ouhtvvLEo51brxxuL7Tvf+U7qCUX14IMPFvX8kSNHFvX81Ar17lo137TtcxMmTIgtttjiW/9M1NXVxe233x5bb711vPHGG0VaVz622mqrgpyz6aabFuSccrVgwYLo379/nH/++bF06dJv9Wvr6upi6NCh0aNHjxg/fnyRFqbXo0eP1BMoEgEAyEY1f0/2c9ddd118+umnBT1zyZIlRX9+crXq2rVr1X6XNiLi1VdfjZdeeqkoZy9btizuuuuuopxdLgoVAKr9qyafmz59euy2227Ru3fvuOWWW7723fy33347Lr/88thiiy3iwAMPrPp3/iMiamtro3v37gU5q1DnlLOlS5fG6aefHhtssEH8/ve/jwkTJnzl37t8+fKYMGFCXHzxxdGjR4/4wQ9+ENOmTSvh2tIrVEyi/FTvn0oA/kO1f0w24p+PTLvuuuvi2GOPLdiZ9913X8yZM6dg5+VktdVWi+7du8fzzz+fekrRXHPNNXH55ZcX/Nx77703pk+fXvBzy0khvq8dkd+jusaMGRNjxoyJiIj27dtHx44do2nTprF06dKYP39+TJ48uWSPqSwn3bt3jyZNmhTkrJze/Z06dWqcccYZccYZZ0SzZs2ic+fO0bx582jUqFEsXLgwZs6cGe+8804sWrQo9dSS6tWrV+oJFIkAQNl79dVXY/DgwalnFM3GG28cu+66a+oZWaj2G2V97pxzzomDDz44mjZtuspn1dXVxYUXXliAVfnaaqutqjoA3HDDDXHWWWcV/BM2f/jDHwp6XrmpX79+bL755gU5q3PnzlFbWxvLly8vyHmVZOrUqTF16tTUM8rCzjvvXLCzNtxww1hrrbWyCynz58+P5557LvWM5OrVq+cTAFVMAKDsPf300/H000+nnlE0hx12mABQIuuss07qCSUxbdq0+NWvfhVXXHHFKp918803x7hx4wqwKl9bbbVV/OUvf0k9o2g++eST+M1vfhNXXnllwc4cOnRoPPXUUwU7rxx17969YDfYatiwYWy00UZZfMedr9a3b9+Cnrf11ltX/X04+HKbb755NGrUKPUMisQ9AIBs5PIJgIiIK6+8cpWfnT1p0qT42c9+VqBF+crhY5RXX311PProowU568MPP4yf/vSnBTmrnG2zzTYFPe973/teQc+jstSrVy922GGHgp7Zs2fPgp5H5dhuu+1ST6CIBAAgGw0bNswqAhx55JFx5513rtSvnTBhQvTp0yfmzZtX4FX52WyzzWKNNdZIPaOo6urqYr/99ovXXnttlc5ZuHBh7LnnnvHBBx8UaFn56t27d0HPEwDy9r3vfa8gX/v6Ii8C89WnT5/UEygiAQDISocOHVJPKJmlS5fG/vvvH6ecckp88sknK/RrFi1aFH/84x/ju9/9brz//vtFXpiH2tra2HbbbVPPKLrZs2fHdtttFw899NBK/frJkyfHdtttF//4xz8KvKw8CQAU0qBBgwp+5rbbbhsNGjQo+LmUt9ra2oL//kR5EQCArHTq1Cn1hJK76KKLYoMNNohf//rXMW7cuP/zmMDp06fHgw8+GMcff3ysv/768ctf/jKWLFmSaG11yuXdlI8++ij69esXBxxwwArf+PD999+PM844IzbZZJOqvlniF33nO9+Jtm3bFvTMTTbZJJo1a1bQM6kcP/zhDwt+ZuPGjX0NIENbbrlltGjRIvUMishNAIGs5BgAIiJmzpwZ5513Xpx33nkREbHmmmtGbW1tzJ8/34v9Eth9993j5JNPTj2jZO6444644447onPnztG3b9/o1q1brLPOOtGgQYOoq6uLmTNnxqRJk+LJJ5+M8ePHZ3f3+v79+xf8zHr16sUuu+wSQ4cOLfjZlLetttqqaJ9u22mnnWLs2LFFOZvyNGDAgNQTKDIBAMhK165dU08oCx999FHqCVnp1q1brL/++vH222+nnlJSkyZNikmTJqWeUXb22GOPopy75557CgAZ2nfffYt2dv/+/eO3v/1t0c6n/AgA1c9XAICsCACkUqwXfVSWZs2axfbbb1+Us3ffffeoqakpytmUp9ra2vjRj35UtPO32WabWHvttYt2PuVl7bXXjq222ir1DIpMAACy0q1bt9QTyNQ+++yTegJlYI899oiGDRsW5ew2bdr4znZmBgwYUNSb29bW1npHOCODBg2K2lovD6udf8NAVlq3bu3dDJLYYYcdol27dqlnkFgx362NiDjwwAOLej7l5dhjjy36NYrxhAHKUzFuJkn5EQCA7Gy55ZapJ5Ch2tpaL84yt8Yaa0S/fv2Keo2DDjqoaJ8woLx06tSpKDeU/E/9+vWLNdZYo+jXIa3WrVt7/F8mBAAgO1tssUXqCWTqoIMOSj2BhA444ICiP1d9rbXWir322quo16A8HHfccSX5uHbDhg1jv/32K/p1SGvfffeNevXqpZ5BCQgAQHa22Wab1BPI1JZbbhlbb7116hkkcswxx5TkOkceeWRJrkM6a621VvzkJz8p2fWK/dUV0jviiCNST6BEBAAgO9tuu23qCWSsFN/Zpfx873vfix49epTkWrvssosbnla5k08+OZo2bVqy622//fbRpUuXkl2P0urevXt897vfTT2DEhEAgOy0bt3aH2RI5oADDog111wz9QxKrJTv1tbW1sZpp51WsutRWi1atCh5SKypqYnjjjuupNekdHxqKC8CAJClHXfcMfUEMtW4ceP42c9+lnoGJdShQ4c44IADSnrNAw88MDp16lTSa1Iav/jFL6Jly5Ylv+7hhx8ejRs3Lvl1Ka4mTZrEYYcdlnoGJSQAAFkq9p244escf/zx0aRJk9QzKJFTTjkl6tevX9Jr1q9fP371q1+V9JoUX8eOHePkk09Ocu2WLVvGoYcemuTaFM/hhx+eJCiRjgAAZKlv374luXsyfJlWrVrFUUcdlXoGJdC2bdtkH6898sgjo3PnzkmuTXFceOGF0ahRo2TX/9WvfuVO8VWkpqYmjj/++NQzKDF/+gWytMYaa7gZIEn9+te/jmbNmqWeQZGdeeaZyV6wNWjQIC688MIk16bwdthhh+SP4+vUqZPHmVaRgQMHRteuXVPPoMQEACBbe++9d+oJZKxVq1bxy1/+MvUMiqhz587x4x//OOmGQYMGxe677550A6uufv368T//8z+pZ0RExOmnn+5TAFXi7LPPTj2BBAQAIFv77LNP6glk7qSTTor27dunnkGRXHjhhSX/7v+Xufzyy91zosKdddZZsdlmm6WeERERXbt2jWOOOSb1DFbRwIEDY4sttkg9gwQEACBbnTp1ip49e6aeUVHcAbqwmjRpEn/6059Sz6AIBgwYEIMGDUo9IyIi1l9//bjkkktSz2AlbbXVVmX3WMezzz47mjZtmnoGK6mmpiZ+85vfpJ5BIgIAkDV3NF5xrVu3jt69e6eeUXX23nvvGDBgQOoZFFCjRo3iiiuuSD3jfzn66KNjt912Sz2Db6lhw4Zxww03lMUnSb6oTZs2ceaZZ6aewUo6+OCDvfufMQEAyNr+++8fDRs2TD2jIhx++OHRoEGD1DOq0lVXXRUtWrRIPYMCueCCC6Jjx46pZ/wvNTU1ce2118Zaa62Vegrfwh/+8IfYdNNNU8/4Uj//+c9j8803Tz2Db2n11VeP8847L/UMEhIAgKyttdZaye+qXAlqamqS38ysmq233npx1VVXpZ5BAey6665l+1itddddN26//XaPQK0Qe++9d5xwwgmpZ3yl+vXrxzXXXOPnqcKceuqpsd5666WeQUL+iwWy99Of/jT1hLK35557xkYbbZR6RlU78MAD48ADD0w9g1Ww1lprxfXXXx81NTWpp3ylnXfeOc4///zUM/gG3bp1iyFDhpT1z1JExNZbbx2nn3566hmsoM6dO8epp56aegaJCQBA9nr16uVmgN/glFNOST0hC9dcc01ssskmqWewEmpra+OOO+6IddZZJ/WUb3TKKafED3/4w9Qz+AprrrlmDB8+PJo1a5Z6ygo5++yz43vf+17qGayAK6+8MlZbbbXUM0hMAACIKLs7LJeTnj17xnbbbZd6RhaaNm0aw4cPj5YtW6aewrd04YUXxk477ZR6xgqpqamJG264IXr16pV6Cv+hYcOGMXz48OjSpUvqKSusfv36ceutt8Yaa6yRegpf47jjjoudd9459QzKgAAAEP98Hq6bGX25c889N/WErGy00Ubxt7/9zQ0XK8gRRxwRv/jFL1LP+FYaN24c99xzT3Tr1i31FP6ltrY2br311th+++1TT/nWOnXqFHfddVfZPa2Af+ratWtcdNFFqWdQJgQAgPjnO2J//OMfU88oOzvuuKN3DBLYeeed46abbko9gxWw1157xTXXXJN6xkpp3bp1PPTQQ7HhhhumnkL88ytAP/jBD1LPWGl9+/aN//mf/0k9g/+w+uqrx2233RaNGzdOPYUyIQAA/MvOO+8ce+21V+oZZeWCCy5IPSFb+++/f1x55ZWpZ/A1+vTpE7feemvUq1cv9ZSV1r59+3jsscdEgMSuvPLKOPLII1PPWGXHHntsnHHGGaln8AV//etfY4sttkg9gzIiAAB8weDBg6NJkyapZ5SFQw891HeEEzvuuONEgDLVp0+fGDFiRFW8q/Z5BCjX581Xs9ra2hgyZEgcd9xxqacUzLnnnhsnnnhi6hlExK9+9av40Y9+lHoGZUYAAPiCTp06xaWXXpp6RnLNmjXz7n+ZOO644+Lqq6/2rO0yMmDAgKp58f+59u3bxxNPPBE77LBD6inZaNSoUQwbNiwOPfTQ1FMK7pJLLhEBEjvssMM88pMv5U8TAP/hqKOOioEDB6aekdTFF18c7dq1Sz2DfznmmGPi7rvv9umUMnDsscfG3XffXVUv/j/XsmXLePjhh+PYY49NPaXqtWnTJh599NGq/d+ampqauPTSS+P3v/996ilZ+uEPfxh//etfo6amJvUUypAAAPAfampqYsiQIdG5c+fUU5LYZZdd4qijjko9g/+wxx57xBNPPBHt27dPPSVLtbW1cdFFF8VVV11V0d/5/yYNGzaMq666Kq666ipPoiiSLbfcMsaPHx89e/ZMPaXoTjvttBgyZIhnz5fQ/vvvHzfffHNV/z7FqhEAAL5Ey5YtY/jw4dG8efPUU0pqrbXWiuuuu867BmVqiy22iOeffz522WWX1FOysvbaa8eoUaMq7lF/q+LYY4+NJ554ws0BC+yYY46JsWPHRseOHVNPKZlDDz00nnzyyejQoUPqKVXvhBNOiNtuuy0aNmyYegplTAAA+Aobb7xx3H///dGoUaPUU0rmpptu8g5zmWvVqlWMHDkyzj33XM/cLoFdd901XnjhhejTp0/qKSXXs2fPeOGFF3wiqADWWGONGDp0aFx99dVZ/W/K57baaqt49tlnq/YrD6nVq1cvLr300hg8eLCAzzcSAAC+xve///0YPnx4Fn9g+81vfhO77bZb6hmsgNra2jjjjDPi6aefjh49eqSeU5WaNWsW11xzTYwcOTLatm2bek4yTZs2jWuvvTbuuece7+CupJ122ileeuml2HvvvVNPSapVq1Zx9913x3XXXRfNmjVLPadqrL322jF69Gg3XWSFCQAA32DXXXeNkSNHVvXXAQ4++OA466yzUs/gW9p8883j6aefjnPOOSeLSFUqe+21V0yYMCGOPvpo76b9y5577hmvvfZanH766T5evIJat24dN954Yzz88MM+WfUF//Vf/xWvvPJK7LvvvqmnVLzdd989nn/+eU/v4FsRAABWwA477BCPP/54VX5vs2/fvu4WXMEaNGgQv/71r2PixIlxwAEHpJ5T0TbeeON48MEHY/jw4d7t/hKNGzeO8847L1566aXYa6+9Us8pWzU1NXH00UfH66+/HocccojfW7/EeuutF3/729/ioYceio033jj1nIrTokWLuP766+O+++7zxB6+NQEAYAX16NEjnn322aq6AVufPn3i3nvv9Y5eFVhvvfXitttui3/84x/Rr1+/1HMqyoYbbhjXX399vPjii7HrrrumnlP2unbtGsOHD4/x48f7WfsPAwYMiOeeey6uueaaWHPNNVPPKXu77LJLvPzyy3HjjTe64eQKqKmpiaOOOiomTpwYhx9+eOo5VCgBAOBbWGutteKBBx6ICy+8sOIfa9S/f/8YMWJEVT7PPGe9evWKkSNHxj/+8Y/Yc889vfv4Nbp16xbXX399vP7663H44Ye7qeK3tPXWW//7Z23QoEFZ/6ztsMMOMXbs2BgxYkRsvvnmqedUlNra2jjkkEPi9ddfjyFDhsRmm22WelJZ6tevXzz33HNx7bXXxtprr516DhVMAAD4lurVqxennHJKPPPMM9GrV6/Uc1bKf//3f8c999zjxX8V69WrV9xzzz3xxhtvxPHHH++mW/9SU1MTu+22W4wcOTJeeeUVL/wLoFevXjFs2LB466234qSTTqrq+6V8Ub169WLfffeNsWPHxpgxY+L73/9+6kkVrX79+nHooYfGiy++GI888kjstdde/tuMf96T5KmnnoqRI0eKSxSEAACwkjbddNP4+9//Hrfddlt06tQp9ZwVss4668SIESPisssuiwYNGqSeQwlstNFG8ac//Sk++OCDuOmmm2KnnXbK8p3azp07x3nnnRdTpkyJ+++/P/r165fl/x+Kaf31149LLrkkpk2bFjfccEPV/qy1bt06fvGLX8Sbb74Zf/vb37zwL4K+ffvG8OHDY+rUqXHxxRdn96mANdZYI0444YR45ZVXYvjw4dGzZ8/Uk6giAgDAKqipqYkDDjggJk6cGEOGDInu3bunnvSlVl999TjxxBPjlVdeiQEDBqSeQwKNGzeOgw8+OEaNGhVTp06NK6+8Mnbeeeeqfodtk002iV//+tfx7LPPxhtvvBGnn366u7GXQJMmTeKwww6LUaNGxZQpU+KCCy6IXr16VXQMWH311eOAAw6IESNGxPvvvx8XXXRRxYTfSrb22mvHz3/+83jxxRfjjTfeiD/84Q8V/7P0VRo2bBh77LFH3HzzzTFt2rQYPHiwGyRSFDV1dXV1qUdQGC1btoy5c+emnsG3dNhhh8UNN9zwrX/dCy+8EFtssUXhB5WJYcOGxaBBg1LPWCmPPfZY3HrrrXHXXXfFnDlzkm5p2rRpHHLIIXHaaafFeuutt8rnDRo0KO6+++4CLCtPe+21VwwfPjz1jJL6+OOP49FHH41HHnkkHn744XjjjTdST1pprVq1ij59+sQuu+wSO+20U2ywwQapJ/EFM2bMiPvuuy/uvffeGDNmTPLfH79Ju3bton///rH77rtHv379fI2mjHz44Yfx6KOPxqhRo2L06NExadKk1JNWSps2bWLnnXeO/v37x8CBA6NFixapJ5EBAaCKzJ07N/zrrDwNGzZcqe9hL1u2LObPn1+EReWhSZMmFf8R9U8//TRGjx4dDz30UIwaNSpefvnlkly3YcOGsf3228dBBx0U++23XzRp0qRgZ3/yySexdOnSgp1Xbho0aFDQ/39VopkzZ8a4cePiqaeeivHjx8fLL78cM2bMSD3r/2jSpElsvPHGsfXWW0evXr1im222iS5dulTlO4PVaPny5TFhwoR4/PHH44knnohnnnkmJk+enHRTp06domfPntGzZ8/o27dvbLbZZn6eKsTs2bPj6aefjnHjxsUzzzwTEyZMiHfffTf1rP9jo402im222Sa22Wab6N27t58xkhAAAErkww8/jOeeey6effbZeOGFF2LSpEkxefLk+Pjjj1fp3DZt2kSPHj2iR48e0bt37+jdu3f2L2IprNmzZ8eECRPitddei7fffjveeeedmDx5crz33nsxe/bsWL58eVGu27Jly1h33XWjU6dOsf7668f6668fXbp0iU033TQ6duzoD85VZv78+fHSSy/Fiy++GK+//nq8/fbb//55++STTwp2nXXWWSe6dOkSnTt3jq5du8Z3vvOd2HrrraNNmzYFuwbpLViwIF577bV47bXX4p133vn3X++9915Mnz69oD9TX9SiRYt//77VpUuX6NatW3Tt2jW6d+/u0ZCUBQEAILGPP/44pk6dGh9++GHMmjUrPvroo1i4cGF89tlnsWDBgn//fZ9/KqJZs2axzjrrxLrrrhvrrLNOtGrVKuF6crd8+fKYPXt2zJo1K2bNmhXz5s2L+fPnx4IFC2LRokX/5+f4c6uvvnqsvvrq0aBBg2jatGm0aNEimjdvHi1atIi2bdtG69ato2HDhgn+iShHs2fPjg8//PB//fV5PF28eHEsXrz4339v8+bNo7a2Nho3bhxNmjSJVq1aRZs2bWLttdeO1q1bV/ynyyiMRYsWxaxZs2LGjBkxf/78mDdvXsybNy8++eSTWLJkyVd+0rJZs2ZRr169WH311aNFixbRsmXLaNmyZay55prRvn17AZ6yJwAAAABABjwFAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIwP8PWIabnjgcWq0AAAAASUVORK5CYII=',
              scale: 3.508264580369843,
            },
            locked: false,
            child: [],
            parent: 'ROOT',
          },
        },
      },
    ],
    img: '/assets/images/qr-code/1.png',
  },
  {
    elements: [
      {
        rootId: '5c11f494-5d02-4d65-a816-c9669c519065',
        layers: {
          '5c11f494-5d02-4d65-a816-c9669c519065': {
            type: { resolvedName: 'GroupLayer' },
            props: {
              position: { x: 23.478260869565247, y: 100.89354861209551 },
              boxSize: { width: 395.04347826086956, height: 462 },
              scale: 1,
              rotate: 0,
            },
            locked: false,
            child: [
              'd78fb0c2-7a1c-4381-8571-c9871ccd9cac',
              '3f39efa2-4017-4c77-b3a9-c98456c629f0',
              '930dca01-95e0-4527-be58-ede3cc41bc2e',
            ],
            parent: 'ROOT',
          },
          'd78fb0c2-7a1c-4381-8571-c9871ccd9cac': {
            type: { resolvedName: 'SvgLayer' },
            props: {
              image:
                'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1OSIgaGVpZ2h0PSI2OSIgdmlld0JveD0iMCwtMC4wMDAwMDc2MjkzOTQ1MzEyNSwxNS40Nzk1NTMyMjI2NTYyNSwxOC4xNDQzNDA1MTUxMzY3MiIgdmVyc2lvbj0iMS4xIiB4bWw6c3BhY2U9InByZXNlcnZlIj4KICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNDcuNDQxODM1LC0xMTUuODQ5NjcpIj4KICAgIDxyZWN0IHdpZHRoPSIxNS40Nzk1NTEiIGhlaWdodD0iMTguMTQ0MzM5IiB4PSI0Ny40NDE4MzMiIHk9Ii0xMzMuOTk0IiB0cmFuc2Zvcm09InNjYWxlKDEsLTEpIiByeT0iMC40NTEwMTUxNyIvPgogICAgPHJlY3QgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIwLjUyNTg1OyIgaWQ9ImNyYXlvbi11bmlxdWUtaWQtMzQtNCIgd2lkdGg9IjE0LjU5MTQzNyIgaGVpZ2h0PSIxNC41NTI4MjQiIHg9IjQ3LjkwMzUiIHk9Ii0xMzAuODUzMDkiIHRyYW5zZm9ybT0ic2NhbGUoMSwtMSkiIHJ5PSIwLjMxNjk5OTkxIi8+CiAgPC9nPgo8L3N2Zz4K',
              position: { x: 0, y: 0 },
              boxSize: { width: 395.04347826086956, height: 462 },
              colors: ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'],
              rotate: 0,
            },
            locked: false,
            child: [],
            parent: '5c11f494-5d02-4d65-a816-c9669c519065',
          },
          '3f39efa2-4017-4c77-b3a9-c98456c629f0': {
            type: { resolvedName: 'QrCodeLayer' },
            props: {
              text: 'https://lidojs.com',
              position: { x: 22.108510111942678, y: 21.070943504373645 },
              boxSize: {
                width: 350.8264580369844,
                height: 350.8264580369844,
                x: 1082.586770981508,
                y: 155.9961470707375,
              },
              rotate: 0,
              bgColor: 'rgb(255, 255, 255)',
              textColor: 'rgb(30, 30, 45)',
              logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAYAAAB/HSuDAAAACXBIWXMAAC4jAAAuIwF4pT92AABbQ0lEQVR4nO3defzVc/7//8f73UJ7qBSpLNWEZBlqjKWyFJEYYxnrxzKYGR/MMGMZzAyGMZbMxzbMIDvjVyGERGRG2cmSCElpIS1apN6/P2bM12c+ltQ553nOeV6vl4t/Phc9X3cfb03nds55vWrq6urqAgAAAKhqtakHAAAAAMUnAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIgAAAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyIAAAAAAABkQAAAAACADAgAAAABkQAAAAACADAgAAAAAkAEBAAAAADIgAAAAAEAGBAAAAADIQP3UA4CV8/7778cf//jH1DOK5sgjj4zu3bunngEAAFVDAIAKNWvWrLjssstSzyia3r17CwAAAFBAvgIAAAAAGRAAAAAAIAMCAAAAAGRAAAAAAIAMCAAAAACQAQEAAAAAMiAAAAAAQAYEAAAAAMiAAAAAAAAZEAAAAAAgAwIAAAAAZEAAAAAAgAwIAAAAAJABAQAAAAAyIAAAAABABgQAAAAAyED91AMAAAAKZenSpTF58uR48803Y+rUqTF9+vSYPn16zJo1K+bOnfvvv5YuXRrLli2L+fPn//vXNm7cOBo2bBgREY0aNYpmzZr9+68111wzWrduHW3atIl27dpFmzZtolOnTtGxY8do0KBBqn9c+FYEAAAAoCJ98MEHMX78+HjhhRfi2WefjQkTJsS7774by5YtW6nz5s6d+61/TW1tbay33nqxwQYbRLdu3WKzzTaLzTbbLDbddNNo1qzZSu2AYhEAqshVV10VS5YsST2j4DbeeOPYddddU88AIuLee++Nt956K/WMotlwww1jzz33TD0DKIHBgwennlA0xx13XKy22mqpZxTFhx9+GCNHjoxHH300xowZE2+++WbqSbF8+fJ499134913341HH3303//3mpqa6NatW3z/+9//918bbbRRwqUQUVNXV1eXegSF0bJly5WqluXusMMOixtuuCH1jLLzwgsvxBZbbJF6RtEMGzYsBg0alHoG/2HQoEFx9913p55RNHvttVcMHz489QygBGpqalJPKJo5c+ZEy5YtU88omClTpsTtt98e9957b/z973+P5cuXp5600tZff/3Yfffdo3///rHTTjtFo0aNUk8iMz4BAAAAlJWFCxfGbbfdFjfddFM8/vjjUS3vWb799ttxxRVXxBVXXBFNmjSJPffcM370ox/FrrvuWrWf2qC8eAoAAABQFiZPnhw///nPY911142jjjoqxowZUzUv/v/TJ598ErfffnsMHDgw2rVrFyeddFK88cYbqWdR5QQAAAAgqYkTJ8bhhx8eXbp0iUsvvTQ+/vjj1JNKas6cOTF48ODo2rVr7LLLLjFy5MjUk6hSAgAAAJDE+++/H4cffnh069YthgwZstJ3768mo0aNit122y169uwZw4cPr9pPQJCGAAAAAJTU4sWL45xzzokuXbrEkCFDvMj9EuPHj4+99947tttuuxg/fnzqOVQJAQAAACiZJ598MjbffPM466yzYuHChannlL2///3v0bNnzzj44INj1qxZqedQ4QQAAACg6JYsWRK/+MUvYvvtt4+JEyemnlNxbrnllujWrVvccsstqadQwQQAAACgqN58883Ydttt45JLLvFx/1Xw4YcfxsEHHxw/+MEPYs6cOannUIEEAAAAoGjuu+++2HLLLeO5555LPaVqDB06NL773e/Gs88+m3oKFUYAAAAAiuLiiy+OgQMHxvz581NPqTqTJ0+O73//+3HrrbemnkIFEQAAAICCqqurixNOOCFOPvnkWL58eeo5VWvJkiVx0EEHxQUXXJB6ChVCAAAAAApm2bJlceSRR8af/vSn1FOycdppp8Xxxx/v/gp8IwEAAAAoiLq6ujj88MPj+uuvTz0lO5dffnmceuqpqWdQ5gQAAACgIH72s5/FzTffnHpGti688MI444wzUs+gjAkAAADAKvvd734XV155ZeoZ2fv9738f11xzTeoZlCkBAAAAWCV33HFHnH322aln8C8//elP45FHHkk9gzIkAAAAACvtmWeeif/6r/9KPYMv+Oyzz+KHP/xhvPfee6mnUGYEAAAAYKXMnTs39t9//1i0aFHqKfyHOXPmxIEHHhifffZZ6imUEQEAAABYKUcddVRMnjw59Qy+wpNPPhm/+93vUs+gjAgAAADAt3bDDTfEXXfdlXoG3+D3v/99PPfcc6lnUCYEAAAA4Fv54IMP4sQTT0w9gxWwbNmyOPLII30VgIgQAAAAgG/puOOOi7lz56aewQp64YUX4pJLLkk9gzIgAAAAACts5MiRMXz48NQz+JbOPffcmDFjRuoZJCYAAAAAK+Szzz6Lk046KfUMVsL8+fPj17/+deoZJFY/9QBg5ay55ppx2GGHpZ5RNB06dEg9AQD4D1deeWW8/vrrqWewkv7617/GSSedFBtvvHHqKSQiAECF6tChQ9xwww2pZwAAmVi0aFGcf/75qWewCurq6uJ3v/td3H777amnkIivAAAAAN/oz3/+c3zwwQepZ7CK7rzzznj11VdTzyARAQAAAPhaixcvjvPOOy/1DAqgrq7Ov8uMCQAAAMDXuvnmm2P27NmpZ1Agd955Z7z//vupZ5CAAAAAAHylurq6GDx4cOoZFNBnn30WV1xxReoZJCAAAAAAX2n06NHxyiuvpJ5Bgf35z3+OxYsXp55BiQkAAADAV/rLX/6SegJF8NFHH8Xw4cNTz6DEBAAAAOBLzZkzJ4YNG5Z6BkVy3XXXpZ5AiQkAAADAl7r11ltjyZIlqWdQJKNGjYopU6aknkEJCQAAAMCXuvPOO1NPoIjq6ur8O86MAAAAAPwfM2fOjLFjx6aeQZHdddddqSdQQvVTDwDIxfLly+Pdd9+Nt99+O955552YMWNGfPTRR/HRRx/FsmXL4uOPP46IiNVWWy0aNWoU9erVi2bNmkWrVq2idevW0aZNm1h33XWjS5cu0bJly6T/LPC5uXPnxrRp0+L999+PGTNmxIcffhhz5879919Lly6Nurq6mDt37r9/TZMmTaJBgwYREdGyZcto0aLFv/9q165dtGvXLtZZZ51o06ZN1NTUpPpHg+wNHTo0li9fnnoGRTZu3LiYMmVKdOjQIfUUSkAAACiSmTNnxuOPPx6PPfZYPPvss/Hyyy/HJ598UpCz11577fjOd74TW265ZWy33Xax3XbbRZs2bQpyNnyZyZMnx/PPPx8vvfRSvPnmmzFx4sSYNGlSzJs3r2jXbNCgQWywwQbRtWvX2GijjWLjjTeOLbbYIjbddNNo2LBh0a4L/NOIESNST6BEhg4dGieeeGLqGZSAAAAVatGiRTFx4sTUM4pm/fXXjxYtWqSe8a299957ceutt8bw4cNj3LhxUVdXV5TrzJgxI2bMmBFjxoyJSy+9NCIiNt5449hnn31in332iS222KIo1yUPixcvjqeeeirGjBkTY8eOjaeffvp/vYNfKkuXLo2JEyf+n9/r6tevH927d49tt902dtxxx9h+++2jbdu2Jd8H1WzJkiXx6KOPpp5BiTzwwAMCQCYEAKhQEydOrOoXecOGDYtBgwalnrFC6urq4oEHHoj/+Z//iQcffLBoL/q/yauvvhqvvvpqnHvuudG5c+f46U9/GkcccUQ0a9YsyR4qy9tvvx333ntvjBgxIh5//PGyvuv3Z599Fs8//3w8//zzccUVV0RExCabbBIDBgyIPfbYI7bddtuoV69e4pVQ2caOHRsLFy5MPYMSGTNmTCxatCgaNWqUegpF5iaAAKtg+PDhsfnmm8eAAQNi5MiRyV78/6dJkybFiSeeGOuuu2788pe//Pf9BeCL3nvvvbjwwgtj8803jw022CBOOOGEePjhh8v6xf9XeeWVV+LCCy+MHXbYIdq0aRPHHntsjB07tmz+m4RKM2rUqNQTKKElS5bEY489lnoGJSAAAKyECRMmxI477hh77713vPTSS6nnfKX58+fHH//4x9hwww3jsssui88++yz1JBL77LPP4q677oq+fftGx44d41e/+lW8+OKLqWcV1EcffRR//vOfY/vtt49OnTrFueeeGx988EHqWVBRnnzyydQTKLFHHnkk9QRKQAAA+BaWLVsW55xzTmy11Vbx+OOPp56zwj766KM48cQTo1evXvHqq6+mnkMCc+bMiXPOOSc6duwYP/zhD+PRRx/N4t3xKVOmxJlnnhkdOnSIAw44IJ555pnUk6DsffrppzF+/PjUMyixSvpzDStPAABYQR988EHsvPPOcdZZZ8Wnn36aes5KefbZZ2PrrbeOm2++OfUUSmTGjBlx6qmnxnrrrRdnnXVWTJs2LfWkJJYuXRp33HFHbL311tG/f/8YM2ZM6klQtp555pmK/CoQq+a5556LBQsWpJ5BkQkAACvg5Zdfjq233roqvh+3cOHCOOSQQ+LMM8/M4h3gXC1YsCDOPvvs2GCDDeIPf/hDwR5BWQ0efPDB6N27d/Tr16+sv8IDqTz33HOpJ5DAsmXL4qmnnko9gyITAAC+wdixY2O77baLqVOnpp5SUOeee24cdthhsWzZstRTKKC6urr461//Gp07d47f/e537uL9NR566KHYfPPN44gjjoiZM2emngNl4/nnn089gUTGjRuXegJF5jGAAF9j3LhxMWDAgJg3b17qKUVx0003xbJly+LGG2/02LQq8Nprr8WPf/zjGDt2bOopFaOuri6uv/76GD58eFx44YVx5JFHRk1NTepZkFQuAaBt27ax4447Rvfu3aNTp07RpEmTiPjnPRDee++9ePHFF2P06NHx/vvvJ15aOs8++2zqCRSZAADwFSZOnBj9+vWr2hf/n7v11lujtrY2brzxRi98KtSyZcvi/PPPj3POOadi70+R2pw5c+Loo4+Om266KYYMGRKdOnVKPQmSWLZsWbzyyiupZxTV7rvvHieffHLsuOOOUVv79R+IrqurizFjxsR5552XxaMR3Si1+vkKAMCXmDNnTgwcODDmzp2bekpJ3HzzzXHGGWeknsFKeOedd6J3795x5plnevFfAI8//nj06NEjbr311tRTIIl33nmnan8vadGiRQwdOjTuu+++6NOnzze++I+IqKmpid69e8fDDz8ct912WzRt2rQES9N57733YtasWalnUEQCAMCXOPzww+ONN95IPaOkzj///LjuuutSz+BbuOeee6JHjx4+8l9g8+bNi4MOOij+67/+KxYvXpx6DpTUxIkTU08oipYtW8YTTzwRe++990qfccABB8SYMWOiefPmBVxWfl5++eXUEygiAQDgP1x99dVxzz33pJ6RxHHHHef7fxWgrq4ufvOb38Ree+1V9V9RSemGG26I7bffPt57773UU6BkqjV+33rrrdG9e/dVPmfLLbeMu+66qwCLyle1fwUkdwIAwBe89957ccopp6Sekcynn34a++23XzZffahECxcujB/84Afx29/+NvWULDzzzDPx3e9+152xycZbb72VekLBHXHEEbHbbrsV7LxddtkljjzyyIKdV25ef/311BMoIgEA4AtOOOGEWLBgQeoZSU2ePDlOOOGE1DP4EnPmzIldd901hg0blnpKVmbOnBl9+vSJ+++/P/UUKLpqe+RtgwYN4uyzzy74uWeeeeYK3UOgEr322mupJ1BE1flTC7ASRo8e7YXVvwwZMiQefPDB1DP4gqlTp8Z2220XTz75ZOopWVq0aFEMHDgwbrnlltRToKimTJmSekJBDRo0KDp06FDwczt27Bh9+/Yt+LnloFrvA8E/CQAA8c/vVP/yl79MPaOsHH300fHJJ5+knkH888V/796949VXX009JWvLli2LQw89VASgqlXbJwD233//op3dr1+/op2d0rRp02LJkiWpZ1AkAgBARNx9991ufvcf3nvvvfjDH/6Qekb2pk2bFr17967K7+VWouXLl4sAVK1ly5bFzJkzU88oqB133LFoZ2+11VZFOzu1d999N/UEikQAALJXV1fnhmpf4aKLLqq6d4Mqydy5c2O33Xbz4r/MfB4BRowYkXoKFNSHH36YekJBrbPOOtGqVauind++ffuinZ3a22+/nXoCRSIAANm7//7744UXXkg9oywtWrQozjzzzNQzsvTpp5/GPvvsEy+99FLqKXyJ5cuXx3777Rf/+Mc/Uk+Bgqm2AFCM7/5/0ZprrlnU81MSAKqXAABkb/DgwaknlLWbbrop3nzzzdQzsnPEEUfE6NGjU8/ga3x+Y8DJkyenngIFMXv27NQTCmqNNdYo6vn16tUr6vkpTZ8+PfUEikQAALI2YcKEGDVqVOoZZW3ZsmVx0UUXpZ6RlYsvvth3zCvE7NmzY9CgQW6YSVX46KOPUk8oqIYNG6aeULE++OCD1BMoEgEAyNq1116bekJFuO6662LatGmpZ2ThkUce8USKCvPyyy/HEUccEXV1damnwCqptpA1b9681BMqlk8AVC8BAMjW4sWL46abbko9oyIsXbo0rrnmmtQzqt706dPjwAMPjOXLl6eewrd05513xhVXXJF6BqyShQsXpp5QUNX2lYZSmjFjRuoJFIkAAGRrxIgRMWfOnNQzKsY111wTS5cuTT2jatXV1cXhhx8es2bNSj2FlXTyySfHhAkTUs+AlbZgwYLUEwrqnXfe8cmclVRtj4Pk/xEAgGzdeuutqSdUlOnTp8djjz2WekbVGjx4cDz00EOpZ7AKlixZEgceeGAsWbIk9RRYKfPnz089oaDmz5/vMaorqdruB8H/IwAAWVqwYEHcd999qWdUnGr7eGi5ePPNN+OMM85IPYMCmDBhQpx77rmpZ8BKWbZsWeoJBff888+nnlCR5s2b5+toVUoAALL0wAMPxKeffpp6BkRdXV0cc8wxsWjRotRTKJALLrjAVwGgTPz9739PPaFi+ZpkdRIAgCzdfffdqSdAREQMGTIkRo8enXoGBfTZZ5/FUUcd5bvHUAb87/3K+/jjj1NPoAgEACA7y5cvjwcffDD1DIgFCxbEaaedlnoGRTBu3Li45ZZbUs+A7L399ts+kbOSqu2eEPyTAABk5/nnn/doIMrC+eefHx988EHqGRTJqaee6r4ZUAb+9re/pZ5Qkfz+VZ0EACA7Dz/8cOoJEFOmTIlLLrkk9QyK6P3334+LLroo9QxYYbW11fnS4Nprr43PPvss9YyK415J1ak6/ysH+Bpjx45NPQHi97//fSxevDj1DIrskksu8T1aKkbz5s1TTyiK6dOnx/Dhw1PPqDhz585NPYEiEACArCxfvtwdgUluypQpcd1116WeQQnMnTs3Bg8enHoGrJBq/QRARMSf/vSn1BMqjk8AVKfq/a8c4EtMmjTJY21I7g9/+EMsXbo09QxKZPDgwbFgwYLUM+AbtWrVKvWEonniiSfiscceSz2jonzyySepJ1AEAgCQlRdeeCH1BDI3Z86cGDJkSOoZlNDcuXPj+uuvTz0DvtFaa62VekJRnXbaaR7PSfYEACArL774YuoJZO4vf/mLd1UydNlll8Xy5ctTz4CvVe0B4KmnnhLjyJ4AAGTFJwBIafny5XH55ZennkECb731Vtx///2pZ8DXatu2beoJRffLX/7S41fJWv3UAwBKSQAgpYceeiimTJmSegaJ/OUvf4k99tgj9Qz4Sm3bto369etX9SPzPvzwwzj66KPj3nvvXeWz6tWrFz169CjAqvLUsmXL1BMoAgEAyMbs2bNj+vTpqWeQsRtuuCH1BBK67777YubMmdGmTZvUU+BL1a9fPzp06BCTJ09OPaWoRowYEZdddlmccMIJq3ROs2bNvLFAxfEVACAbb775ZuoJZGzOnDkxbNiw1DNI6LPPPotbbrkl9Qz4WhtssEHqCSVxyimnxLhx41LPgJLzCQAgG++8807qCck0adIkdtppp9hiiy2ic+fO0bx582jcuHHMmTMnpk2bFk8//XQ89NBDMXPmzNRTq9bw4cOze6Zy+/bto3///rHZZpvFeuutFy1atIgFCxbErFmz4pVXXolHHnkkuxtz3nHHHXHSSSelngFfaaONNopRo0alnlF0S5cujUGDBsXTTz8d7du3Tz0HSkYAALKRYwDo1KlTnH322bH//vtHo0aNvvbvXbp0aQwbNixOPfXUePvtt0u0MB9Dhw5NPaFkttpqqzjnnHOif//+UVNT87V/78svvxznnXde3HHHHSVal9a4ceNiypQp0aFDh9RT4EttuummqSeUzAcffBADBw6MsWPHRuPGjVPPgZLwFQAgG7kFgJNOOikmTpwYhx9++De++I+IaNCgQey3337xyiuvxOGHH178gRmZO3duPPTQQ6lnFF1NTU2ce+65MX78+Nhtt92+8cV/RET37t3j9ttvj/vuuy+bG04NHz489QT4SptvvnnqCSX1/PPPx7777htLly5NPQVKQgAAspFTADjvvPPikksuiYYNG37rX9uoUaO47rrr4tBDDy3Csjw9/PDDWXz8/4Ybbogzzjgjamu//R8vdt9993j88cdjzTXXLMKy8nL33XenngBfqUePHisU76rJAw88EEcffXTU1dWlngJFJwAA2cglAOy7775x+umnr9IZNTU18ec//zm6d+9eoFV5e/DBB1NPKLozzzxzlaNR9+7d429/+9tKBYRK8uSTT8bChQtTz4Av1bRp0+jcuXPqGSU3ZMiQ+MlPfiICUPWq+39hAb7go48+Sj2h6Bo3bhyDBw8uyFmrr756XHbZZQU5K3fV/vH/LbfcMs4+++yCnNW3b99VfjRXuVuyZEk8/vjjqWfAV9p+++1TT0ji6quvjlNPPTX1DCgqAQDIRg4B4Mgjj4x11123YOf16dMn+vTpU7DzcjRp0qSYMmVK6hlFdfHFF0e9evUKdt5ZZ50Va621VsHOK0c53GWdytW7d+/UE5K58MIL45RTTvFJAKqWAABkYd68ebFs2bLUM4ruiCO
Download .txt
gitextract_2xb0w4ev/

├── README.md
├── index.html
├── package.json
├── src/
│   ├── assets/
│   │   └── .gitkeep
│   ├── constant/
│   │   ├── data.ts
│   │   └── text-effects.ts
│   ├── features/
│   │   └── design/
│   │       ├── components/
│   │       │   ├── LidoJSEditor.tsx
│   │       │   ├── editor-content/
│   │       │   │   ├── EditorContent.tsx
│   │       │   │   └── index.ts
│   │       │   ├── editor-header/
│   │       │   │   ├── EditorHeader.tsx
│   │       │   │   └── index.ts
│   │       │   ├── index.ts
│   │       │   ├── layer-settings/
│   │       │   │   ├── LayerSettings.tsx
│   │       │   │   └── index.ts
│   │       │   ├── preview/
│   │       │   │   ├── PreviewModal.tsx
│   │       │   │   └── index.ts
│   │       │   ├── sidebar/
│   │       │   │   ├── DrawContent.tsx
│   │       │   │   ├── FrameContent.tsx
│   │       │   │   ├── GraphicContent.tsx
│   │       │   │   ├── IframeContent.tsx
│   │       │   │   ├── ImageContent.tsx
│   │       │   │   ├── Photo.tsx
│   │       │   │   ├── QrCodeContent.tsx
│   │       │   │   ├── ShapeContent.tsx
│   │       │   │   ├── Sidebar.tsx
│   │       │   │   ├── TableContent.tsx
│   │       │   │   ├── TemplateContent.tsx
│   │       │   │   ├── TextContent.tsx
│   │       │   │   ├── UploadContent.tsx
│   │       │   │   ├── VideoContent.tsx
│   │       │   │   └── index.ts
│   │       │   └── tabs/
│   │       │       ├── TabList.tsx
│   │       │       └── index.ts
│   │       ├── config/
│   │       │   ├── iframe.tsx
│   │       │   ├── line.tsx
│   │       │   ├── qrCode.tsx
│   │       │   └── shape.tsx
│   │       └── pages/
│   │           ├── DesignPage.tsx
│   │           └── index.ts
│   ├── main.tsx
│   ├── pages/
│   │   └── Main.tsx
│   ├── shared/
│   │   ├── components/
│   │   │   ├── index.ts
│   │   │   └── masonry/
│   │   │       ├── Masonry.tsx
│   │   │       └── index.ts
│   │   ├── icons/
│   │   │   └── pencil/
│   │   │       ├── Highlighter.tsx
│   │   │       ├── Marker.tsx
│   │   │       └── Pencil.tsx
│   │   └── theme/
│   │       ├── index.ts
│   │       ├── palette.ts
│   │       └── theme.ts
│   ├── styles.css
│   └── utils/
│       ├── download.ts
│       └── thumbnail.ts
├── tsconfig.json
└── vite.config.ts
Download .txt
SYMBOL INDEX (18 symbols across 16 files)

FILE: src/features/design/components/editor-header/EditorHeader.tsx
  type HeaderLayoutProps (line 14) | interface HeaderLayoutProps {

FILE: src/features/design/components/preview/PreviewModal.tsx
  type PreviewModalProps (line 5) | interface PreviewModalProps {

FILE: src/features/design/components/sidebar/FrameContent.tsx
  type Frame (line 9) | interface Frame {

FILE: src/features/design/components/sidebar/Photo.tsx
  type Props (line 3) | type Props = {

FILE: src/features/design/components/sidebar/TemplateContent.tsx
  type Template (line 9) | interface Template {

FILE: src/features/design/components/sidebar/TextContent.tsx
  type Text (line 15) | interface Text {

FILE: src/features/design/components/sidebar/UploadContent.tsx
  type UploadContentProps (line 7) | interface UploadContentProps {

FILE: src/features/design/components/tabs/TabList.tsx
  type SidebarTabProps (line 3) | interface SidebarTabProps {

FILE: src/features/design/config/iframe.tsx
  type IframeItem (line 3) | type IframeItem = {

FILE: src/features/design/config/line.tsx
  type Line (line 6) | type Line = {

FILE: src/features/design/config/qrCode.tsx
  type QrCodeItem (line 3) | type QrCodeItem = {

FILE: src/features/design/config/shape.tsx
  type Shape (line 21) | type Shape = {

FILE: src/features/design/pages/DesignPage.tsx
  type FontVariant (line 8) | type FontVariant =

FILE: src/pages/Main.tsx
  function Page (line 3) | function Page() {

FILE: src/shared/components/masonry/Masonry.tsx
  type MasonryProps (line 3) | interface MasonryProps {
  type ColumnData (line 15) | interface ColumnData {

FILE: src/shared/theme/theme.ts
  type Palette (line 7) | interface Palette {
  type PaletteOptions (line 15) | interface PaletteOptions {
Condensed preview — 55 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (315K chars).
[
  {
    "path": "README.md",
    "chars": 3691,
    "preview": "<img width=\"960\" alt=\"Screenshot 2023-04-22 101234\" src=\"https://github.com/lidojs/canva-clone/assets/19285404/06249d78-"
  },
  {
    "path": "index.html",
    "chars": 4662,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\" />\n  <title>LidoJS Design Editor</title>\n  <meta\n    con"
  },
  {
    "path": "package.json",
    "chars": 1049,
    "preview": "{\n  \"name\": \"@lidojs/react\",\n  \"version\": \"2.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vi"
  },
  {
    "path": "src/assets/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/constant/data.ts",
    "chars": 9555,
    "preview": "export const data = [\n  {\n    layers: {\n      ROOT: {\n        type: { resolvedName: 'RootLayer' },\n        props: {\n    "
  },
  {
    "path": "src/constant/text-effects.ts",
    "chars": 8536,
    "preview": "export const addAHeading = {\n  rootId: 'f2d33316-8857-4496-a0c7-3dcc9c4ff981',\n  layers: {\n    'f2d33316-8857-4496-a0c7-"
  },
  {
    "path": "src/features/design/components/LidoJSEditor.tsx",
    "chars": 4122,
    "preview": "'use client';\n\nimport type { FontData } from '@lidojs/design-core';\nimport { Editor, type GetFontQuery, PageControl } fr"
  },
  {
    "path": "src/features/design/components/editor-content/EditorContent.tsx",
    "chars": 182,
    "preview": "import { DesignFrame } from '@lidojs/design-editor';\nimport { data } from '../../../../constant/data';\n\nexport const Edi"
  },
  {
    "path": "src/features/design/components/editor-content/index.ts",
    "chars": 33,
    "preview": "export * from './EditorContent';\n"
  },
  {
    "path": "src/features/design/components/editor-header/EditorHeader.tsx",
    "chars": 5924,
    "preview": "import ArrowClockwiseIcon from '@duyank/icons/regular/ArrowClockwise';\nimport ArrowCounterClockwiseIcon from '@duyank/ic"
  },
  {
    "path": "src/features/design/components/editor-header/index.ts",
    "chars": 32,
    "preview": "export * from './EditorHeader';\n"
  },
  {
    "path": "src/features/design/components/index.ts",
    "chars": 32,
    "preview": "export * from './LidoJSEditor';\n"
  },
  {
    "path": "src/features/design/components/layer-settings/LayerSettings.tsx",
    "chars": 757,
    "preview": "import {\n  LayerSettings as EditorLayerSettings,\n  useSelectedLayers,\n} from '@lidojs/design-editor';\n\nexport const Laye"
  },
  {
    "path": "src/features/design/components/layer-settings/index.ts",
    "chars": 33,
    "preview": "export * from './LayerSettings';\n"
  },
  {
    "path": "src/features/design/components/preview/PreviewModal.tsx",
    "chars": 913,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport { Preview } from '@lidojs/design-editor';\nimport type { FC } from 'r"
  },
  {
    "path": "src/features/design/components/preview/index.ts",
    "chars": 32,
    "preview": "export * from './PreviewModal';\n"
  },
  {
    "path": "src/features/design/components/sidebar/DrawContent.tsx",
    "chars": 6697,
    "preview": "import XBoldIcon from '@duyank/icons/bold/XBold';\nimport { useEditor } from '@lidojs/design-editor';\nimport { useDraw } "
  },
  {
    "path": "src/features/design/components/sidebar/FrameContent.tsx",
    "chars": 4852,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-c"
  },
  {
    "path": "src/features/design/components/sidebar/GraphicContent.tsx",
    "chars": 6101,
    "preview": "import MagnifyingGlassIcon from '@duyank/icons/regular/MagnifyingGlass';\nimport XIcon from '@duyank/icons/regular/X';\nim"
  },
  {
    "path": "src/features/design/components/sidebar/IframeContent.tsx",
    "chars": 3620,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type {\n  LayerId,\n  LayerType,\n  SerializedLayerTree,\n  SerializedLa"
  },
  {
    "path": "src/features/design/components/sidebar/ImageContent.tsx",
    "chars": 7879,
    "preview": "import MagnifyingGlassIcon from '@duyank/icons/regular/MagnifyingGlass';\nimport XIcon from '@duyank/icons/regular/X';\nim"
  },
  {
    "path": "src/features/design/components/sidebar/Photo.tsx",
    "chars": 1575,
    "preview": "import { type FC, type HTMLProps, memo, useState } from 'react';\n\ntype Props = {\n  image: string;\n  name: string;\n  user"
  },
  {
    "path": "src/features/design/components/sidebar/QrCodeContent.tsx",
    "chars": 3620,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type {\n  LayerId,\n  LayerType,\n  SerializedLayerTree,\n  SerializedLa"
  },
  {
    "path": "src/features/design/components/sidebar/ShapeContent.tsx",
    "chars": 6632,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-c"
  },
  {
    "path": "src/features/design/components/sidebar/Sidebar.tsx",
    "chars": 4614,
    "preview": "import BrowserIcon from '@duyank/icons/regular/Browser';\nimport FrameCornersIcon from '@duyank/icons/regular/FrameCorner"
  },
  {
    "path": "src/features/design/components/sidebar/TableContent.tsx",
    "chars": 26533,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-c"
  },
  {
    "path": "src/features/design/components/sidebar/TemplateContent.tsx",
    "chars": 2718,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type { SerializedPage } from '@lidojs/design-core';\nimport { useEdit"
  },
  {
    "path": "src/features/design/components/sidebar/TextContent.tsx",
    "chars": 5560,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type { LayerId, SerializedLayers } from '@lidojs/design-core';\nimpor"
  },
  {
    "path": "src/features/design/components/sidebar/UploadContent.tsx",
    "chars": 4655,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport { useEditor } from '@lidojs/design-editor';\nimport { fetchSvgContent"
  },
  {
    "path": "src/features/design/components/sidebar/VideoContent.tsx",
    "chars": 5196,
    "preview": "import XIcon from '@duyank/icons/regular/X';\nimport type { LayerId, LayerType, SerializedLayers } from '@lidojs/design-c"
  },
  {
    "path": "src/features/design/components/sidebar/index.ts",
    "chars": 27,
    "preview": "export * from './Sidebar';\n"
  },
  {
    "path": "src/features/design/components/tabs/TabList.tsx",
    "chars": 3591,
    "preview": "import type { FC, ReactNode } from 'react';\n\ninterface SidebarTabProps {\n  tabs: {\n    name: string;\n    icon: ReactNode"
  },
  {
    "path": "src/features/design/components/tabs/index.ts",
    "chars": 27,
    "preview": "export * from './TabList';\n"
  },
  {
    "path": "src/features/design/config/iframe.tsx",
    "chars": 1096,
    "preview": "import type { LayerId, SerializedLayers } from '@lidojs/design-core';\n\nexport type IframeItem = {\n  elements: [\n    {\n  "
  },
  {
    "path": "src/features/design/config/line.tsx",
    "chars": 12930,
    "preview": "import type { LayerId } from '@lidojs/design-core';\nimport type { DeepPartial, LineLayerProps } from '@lidojs/design-edi"
  },
  {
    "path": "src/features/design/config/qrCode.tsx",
    "chars": 107120,
    "preview": "import type { LayerId, SerializedLayers } from '@lidojs/design-core';\n\nexport type QrCodeItem = {\n  elements: [\n    {\n  "
  },
  {
    "path": "src/features/design/config/shape.tsx",
    "chars": 5121,
    "preview": "import ArrowBottomIcon from '@duyank/icons/shape/ArrowBottom';\nimport ArrowLeftIcon from '@duyank/icons/shape/ArrowLeft'"
  },
  {
    "path": "src/features/design/pages/DesignPage.tsx",
    "chars": 1919,
    "preview": "'use client';\n\nimport type { FontData } from '@lidojs/design-core';\nimport axios from 'axios';\nimport { useEffect, useSt"
  },
  {
    "path": "src/features/design/pages/index.ts",
    "chars": 30,
    "preview": "export * from './DesignPage';\n"
  },
  {
    "path": "src/main.tsx",
    "chars": 293,
    "preview": "import * as ReactDOM from 'react-dom/client';\nimport './styles.css';\nimport axios from 'axios';\nimport Page from './page"
  },
  {
    "path": "src/pages/Main.tsx",
    "chars": 116,
    "preview": "import { DesignPage } from '../features/design/pages';\n\nexport default function Page() {\n  return <DesignPage />;\n}\n"
  },
  {
    "path": "src/shared/components/index.ts",
    "chars": 27,
    "preview": "export * from './masonry';\n"
  },
  {
    "path": "src/shared/components/masonry/Masonry.tsx",
    "chars": 4570,
    "preview": "import React, { useCallback, useEffect, useRef, useState } from 'react';\n\ninterface MasonryProps {\n  children: React.Rea"
  },
  {
    "path": "src/shared/components/masonry/index.ts",
    "chars": 27,
    "preview": "export * from './Masonry';\n"
  },
  {
    "path": "src/shared/icons/pencil/Highlighter.tsx",
    "chars": 4286,
    "preview": "import type { ComponentProps, FC } from 'react';\n\nexport const Highlighter: FC<ComponentProps<'svg'>> = (props) => {\n  r"
  },
  {
    "path": "src/shared/icons/pencil/Marker.tsx",
    "chars": 4971,
    "preview": "import type { ComponentProps, FC } from 'react';\n\nexport const Marker: FC<ComponentProps<'svg'>> = (props) => {\n  return"
  },
  {
    "path": "src/shared/icons/pencil/Pencil.tsx",
    "chars": 8273,
    "preview": "import type { ComponentProps, FC } from 'react';\n\nexport const Pencil: FC<ComponentProps<'svg'>> = (props) => {\n  return"
  },
  {
    "path": "src/shared/theme/index.ts",
    "chars": 52,
    "preview": "export * from './palette';\nexport * from './theme';\n"
  },
  {
    "path": "src/shared/theme/palette.ts",
    "chars": 3983,
    "preview": "// Helper function to convert hex to rgb values\nconst hexToRgb = (hex: string) => {\n  const result = /^#?([a-f\\d]{2})([a"
  },
  {
    "path": "src/shared/theme/theme.ts",
    "chars": 9726,
    "preview": "'use client';\n\nimport { alpha, createTheme } from '@mui/material/styles';\nimport { palette, rgba } from './palette';\n\nde"
  },
  {
    "path": "src/styles.css",
    "chars": 1559,
    "preview": "\nhtml, body {\n    margin: 0;\n    font-family: 'Nunito', sans-serif;\n    color: #5E6278;\n    font-size: 14px;\n}\n\nhtml,\nbo"
  },
  {
    "path": "src/utils/download.ts",
    "chars": 487,
    "preview": "export const downloadObjectAsJson = (exportName: string, data: unknown) => {\n  const dataStr = `data:text/json;charset=u"
  },
  {
    "path": "src/utils/thumbnail.ts",
    "chars": 353,
    "preview": "export const getThumbnail = (url: string) => {\n  const parts = url.split('/');\n\n  // get the filename\n  const fileName ="
  },
  {
    "path": "tsconfig.json",
    "chars": 766,
    "preview": "{\n  \"compilerOptions\": {\n    \"jsx\": \"preserve\",\n    \"allowJs\": false,\n    \"strict\": true,\n    \"jsxImportSource\": \"@emoti"
  },
  {
    "path": "vite.config.ts",
    "chars": 642,
    "preview": "import react from '@vitejs/plugin-react';\nimport { defineConfig } from 'vite';\nimport { analyzer } from 'vite-bundle-ana"
  }
]

About this extraction

This page contains the full source code of the lidojs/canva-clone GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 55 files (294.7 KB), approximately 114.2k tokens, and a symbol index with 18 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!