This is an automated email from the ASF dual-hosted git repository.

linkinstar pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-answer-plugins.git

commit bbda77a14ec7f4439b35b1dfd7a683a01600a4ce
Author: robin <[email protected]>
AuthorDate: Mon May 27 16:46:23 2024 +0800

    feat(embed-basic): add i18n support for basic embed plugin
---
 embed-basic/Component.tsx      |  64 +++++++++
 embed-basic/hooks.ts           | 246 ++++++++++++++++++++++++++++++++
 embed-basic/i18n/en_US.yaml    |  11 +-
 embed-basic/i18n/index.ts      |   7 +
 embed-basic/i18n/zh_CN.yaml    |  11 +-
 embed-basic/index.ts           |  34 +++++
 embed-basic/modal.tsx          | 149 +++++++++++++++++++
 embed-basic/package.json       |  17 +++
 embed-basic/pnpm-lock.yaml     | 315 +++++++++++++++++++++++++++++++++++++++++
 embed-basic/tsconfig.json      |  31 ++++
 embed-basic/tsconfig.node.json |  10 ++
 11 files changed, 893 insertions(+), 2 deletions(-)

diff --git a/embed-basic/Component.tsx b/embed-basic/Component.tsx
new file mode 100644
index 0000000..f58e2ce
--- /dev/null
+++ b/embed-basic/Component.tsx
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useState } from 'react';
+import { Button } from 'react-bootstrap';
+
+import EmbedModal from './modal';
+import { useRenderEmbed } from './hooks';
+
+const Component = ({ editor, previewElement }) => {
+  const [show, setShowState] = useState(false);
+
+  useRenderEmbed(previewElement);
+
+  const handleShow = () => {
+    setShowState(true);
+  };
+
+  const handleConfirm = ({ title, url }) => {
+    setShowState(false);
+    // 判断光标是否在行首
+    const cursor = editor.getCursor();
+    if (cursor.ch !== 0) {
+      editor.replaceSelection('\n');
+    }
+    const embed = `\n[${title}](${url} "@embed")\n`;
+    editor.replaceSelection(embed);
+    editor.focus();
+  };
+  return (
+    <>
+      <Button
+        variant="link"
+        className="p-0 b-0 btn-no-border d-flex justify-content-center 
align-items-center text-black"
+        style={{ width: '1.5rem', height: '1.5rem' }}
+        onClick={handleShow}>
+        <i className="bi bi-window" />
+      </Button>
+      <EmbedModal
+        show={show}
+        setShowState={setShowState}
+        onConfirm={handleConfirm}
+      />
+    </>
+  );
+};
+
+export default Component;
diff --git a/embed-basic/hooks.ts b/embed-basic/hooks.ts
new file mode 100644
index 0000000..1c3a981
--- /dev/null
+++ b/embed-basic/hooks.ts
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useEffect, useState } from 'react';
+
+interface Config {
+  platform: string;
+  enable: boolean;
+}
+const useRenderEmbed = (element: HTMLElement) => {
+  const [configs, setConfigs] = useState<Config[] | null>(null);
+
+  const embeds = [
+    {
+      name: 'YouTube',
+      regexs: [
+        /https:\/\/youtu\.be\/([a-zA-Z0-9_-]{11})/,
+        /https:\/\/www\.youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/,
+        /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/,
+      ],
+      embed: (videoId: string) => {
+        return `<iframe width="100%" height="380" 
src="https://www.youtube.com/embed/${videoId}"; title="YouTube video player" 
frameborder="0" allow="accelerometer; autoplay; clipboard-write; 
encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`;
+      },
+    },
+    {
+      name: 'Twitter',
+      regexs: [
+        /https:\/\/twitter\.com\/[a-zA-Z0-9_]+\/status\/([a-zA-Z0-9_]+)/,
+        /https:\/\/x\.com\/[a-zA-Z0-9_]+\/status\/([a-zA-Z0-9_]+)/,
+      ],
+      embed: (_, url, title = '') => {
+        const blockquoteElement = document.createElement('blockquote');
+        blockquoteElement.classList.add('twitter-tweet');
+
+        const anchorElement = document.createElement('a');
+        anchorElement.href = url.replace('x.com', 'twitter.com');
+
+        anchorElement.textContent = title;
+        blockquoteElement.appendChild(anchorElement);
+        const scriptElement = document.createElement('script');
+        scriptElement.src = 'https://platform.twitter.com/widgets.js';
+        scriptElement.async = true;
+
+        const styleElement = document.createElement('style');
+        styleElement.innerHTML = `
+          .twitter-tweet {
+            display: block;
+            margin: 0 auto;
+          }
+        `;
+
+        return [styleElement, blockquoteElement, scriptElement];
+      },
+    },
+    {
+      name: 'CodePen',
+      regexs: [
+        /https:\/\/codepen\.io\/[a-zA-Z0-9_]+\/pen\/([a-zA-Z0-9_]+)/,
+        /https:\/\/codepen\.io\/[a-zA-Z0-9_]+\/full\/([a-zA-Z0-9_]+)/,
+      ],
+      embed: (penId) => {
+        return `<iframe width="100%" height="380" scrolling="no" 
title="CodePen Embed" 
src="https://codepen.io/${penId}/embed/preview/${penId}?height=265&theme-id=0&default-tab=result";
 frameborder="no" allowtransparency="true" allowfullscreen="true"></iframe>`;
+      },
+    },
+    {
+      name: 'JSFiddle',
+      regexs: [
+        /https:\/\/jsfiddle\.net\/[a-zA-Z0-9_]+\/([a-zA-Z0-9_]+)/,
+        /https:\/\/jsfiddle\.net\/[a-zA-Z0-9_]+\/([a-zA-Z0-9_]+)\/embed/,
+      ],
+      embed: (fiddleId: string) => {
+        return `<iframe width="100%" height="380" 
src="https://jsfiddle.net/${fiddleId}/embedded/"; 
allowfullscreen="allowfullscreen" allowpaymentrequest 
frameborder="0"></iframe>`;
+      },
+    },
+    {
+      name: 'GithubGist',
+      regexs: [
+        /https:\/\/gist\.github\.com\/[a-zA-Z0-9_]+\/([a-zA-Z0-9_]+)/,
+        /https:\/\/gist\.github\.com\/[a-zA-Z0-9_]+\/([a-zA-Z0-9_]+)\.js/,
+      ],
+      embed: (_, url) => {
+        const scriptUrl = url.indexOf('.js') > -1 ? url : `${url}.js`;
+        console.log(scriptUrl);
+        return `<iframe 
+        width="100%"
+        height="350"    
+        src="data:text/html;charset=utf-8,
+        <head><base target='_blank' /></head>
+        <body style='margin:0;'><script src='${scriptUrl}'></script>
+        </body>">`;
+      },
+    },
+    {
+      name: 'Figma',
+      regexs: [
+        /https:\/\/www\.figma\.com\/design\/[a-zA-Z0-9_]+\/([a-zA-Z0-9_]+)/,
+        /https:\/\/www\.figma\.com\/file\/[a-zA-Z0-9_]+\/([a-zA-Z0-9_]+)/,
+      ],
+      embed: (_, url) => {
+        return `<iframe style="border: none;" width="100%" height="450
+" src="https://www.figma.com/embed?embed_host=share&url=${url}"; 
allowfullscreen></iframe>`;
+      },
+    },
+    {
+      name: 'Excalidraw',
+      regexs: [
+        /https:\/\/excalidraw\.com\/#json=([a-zA-Z0-9_,-]+)/,
+        /https:\/\/excalidraw\.com\/([a-zA-Z0-9_,-]+)/,
+      ],
+      embed: (excalidrawId: string) => {
+        return `<iframe width="100%" height="380" 
src="https://excalidraw.com/${excalidrawId}/embed"; frameborder="0"></iframe>`;
+      },
+    },
+    {
+      name: 'Loom',
+      regexs: [
+        /https:\/\/www\.loom\.com\/embed\/([a-zA-Z0-9_]+)/,
+        /https:\/\/www\.loom\.com\/share\/([a-zA-Z0-9_]+)/,
+      ],
+      embed: (loomId: string) => {
+        return `<iframe width="100%" height="380" 
src="https://www.loom.com/embed/${loomId}"; frameborder="0"></iframe>`;
+      },
+    },
+    {
+      name: 'Dropbox',
+      regexs: [
+        /https:\/\/www\.dropbox\.com\/s\/([a-zA-Z0-9_]+)\/[a-zA-Z0-9_]+/,
+      ],
+      embed: (dropboxId: string) => {
+        return `<iframe width="100%" height="380" 
src="https://www.dropbox.com/s/${dropboxId}?raw=1"; frameborder="0"></iframe>`;
+      },
+    },
+  ];
+
+  const filteredEmbeds = embeds.filter((embed) => {
+    const finded = configs?.find(
+      (config) => config.platform === embed.name && config.enable,
+    );
+    return finded;
+  });
+
+  const renderEmbed = (
+    url: string,
+    title: string,
+  ): string | HTMLElement | HTMLElement[] => {
+    let html: string | HTMLElement | HTMLElement[] = '';
+
+    filteredEmbeds.forEach((embed) => {
+      if (html) return;
+      embed.regexs.forEach((regex) => {
+        if (html) return;
+        const match = url.match(regex);
+        if (match) {
+          html = embed.embed(match[1], url, title);
+        }
+      });
+    });
+
+    return html;
+  };
+
+  const render = () => {
+    if (!element) {
+      return;
+    }
+
+    const links = element.querySelectorAll('a');
+    links.forEach((link) => {
+      const url = link.getAttribute('href') || '';
+      const title = link.getAttribute('title') || '';
+      if (!url) {
+        return;
+      }
+      if (title !== '@embed') {
+        return;
+      }
+      const embed = renderEmbed(url, link?.textContent || '');
+      if (embed) {
+        if (typeof embed === 'string') {
+          link.innerHTML = embed;
+        } else if (Array.isArray(embed)) {
+          link.innerHTML = '';
+          embed.forEach((item) => {
+            link.appendChild(item);
+          });
+        } else {
+          link.innerHTML = '';
+          link.appendChild(embed);
+        }
+      } else {
+        link.innerHTML = `
+          <div class="border rounded p-3">
+            <div class="text-secondary">${url}</div>
+            <div class="text-body">${link.textContent}</div>
+          </div>
+        `;
+      }
+    });
+  };
+
+  const getConfig = () => {
+    fetch('/answer/api/v1/embed/config')
+      .then((response) => response.json())
+      .then((result) => setConfigs(result.data));
+  };
+  useEffect(() => {
+    getConfig();
+  }, []);
+
+  useEffect(() => {
+    if (!element) {
+      return;
+    }
+
+    if (!configs) {
+      return;
+    }
+
+    render();
+    const observer = new MutationObserver(() => {
+      render();
+    });
+
+    observer.observe(element, {
+      childList: true,
+    });
+  }, [element, configs]);
+};
+
+export { useRenderEmbed };
diff --git a/embed-basic/i18n/en_US.yaml b/embed-basic/i18n/en_US.yaml
index c12aab1..c47eb2a 100644
--- a/embed-basic/i18n/en_US.yaml
+++ b/embed-basic/i18n/en_US.yaml
@@ -46,4 +46,13 @@ plugin:
           twitter:
             other: X(Twitter)
           youtube:
-            other: YouTube
\ No newline at end of file
+            other: YouTube
+    frontend:
+      header: Add Embed
+      title: Title
+      url: URL
+      cancel: Cancel
+      add: Add
+      required_title: Title is required 
+      required_url: URL is required
+      invalid_url: Invalid URL
\ No newline at end of file
diff --git a/embed-basic/i18n/index.ts b/embed-basic/i18n/index.ts
new file mode 100644
index 0000000..89fd071
--- /dev/null
+++ b/embed-basic/i18n/index.ts
@@ -0,0 +1,7 @@
+import en_US from './en_US.yaml';
+import zh_CN from './zh_CN.yaml';
+
+export default {
+  en_US,
+  zh_CN,
+};
diff --git a/embed-basic/i18n/zh_CN.yaml b/embed-basic/i18n/zh_CN.yaml
index b9cec97..f28ef41 100644
--- a/embed-basic/i18n/zh_CN.yaml
+++ b/embed-basic/i18n/zh_CN.yaml
@@ -46,4 +46,13 @@ plugin:
           twitter:
             other: X(Twitter)
           youtube:
-            other: YouTube
\ No newline at end of file
+            other: YouTube
+    frontend:
+      header: 添加嵌入
+      title: 标题
+      url: URL
+      cancel: 取消
+      add: 添加
+      required_title: 标题是必需的
+      required_url: URL 是必需的
+      invalid_url: 无效的 URL
diff --git a/embed-basic/index.ts b/embed-basic/index.ts
new file mode 100644
index 0000000..cba01d2
--- /dev/null
+++ b/embed-basic/index.ts
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Component from './Component';
+import { useRenderEmbed } from './hooks';
+import i18nConfig from './i18n';
+
+export default {
+  info: {
+    slug_name: 'basic_embed',
+    type: 'editor',
+  },
+  component: Component,
+  i18nConfig,
+  hooks: {
+    useRender: [useRenderEmbed],
+  },
+};
diff --git a/embed-basic/modal.tsx b/embed-basic/modal.tsx
new file mode 100644
index 0000000..f156be1
--- /dev/null
+++ b/embed-basic/modal.tsx
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { useState } from 'react';
+import { Modal, Button, Form } from 'react-bootstrap';
+import { useTranslation } from 'react-i18next';
+
+const EmbedModal = ({ show, setShowState, onConfirm }) => {
+  const [title, setTitle] = useState({
+    value: '',
+    isInvalid: false,
+    errorMsg: '',
+  });
+  const [url, setUrl] = useState({
+    value: '',
+    isInvalid: false,
+    errorMsg: '',
+  });
+  const { t } = useTranslation('plugin', {
+    keyPrefix: 'basic_embed.frontend',
+  });
+
+  const handleHide = () => {
+    setShowState(false);
+  };
+
+  const handleChangeTitle = (e) => {
+    setTitle({
+      value: e.target.value,
+      isInvalid: false,
+      errorMsg: '',
+    });
+  };
+
+  const handleChangeUrl = (e) => {
+    setUrl({
+      value: e.target.value,
+      isInvalid: false,
+      errorMsg: '',
+    });
+  };
+
+  const handleSubmit = () => {
+    if (!title.value) {
+      setTitle({
+        ...title,
+        isInvalid: true,
+        errorMsg: t('required_title'),
+      });
+      return;
+    }
+    if (!url.value) {
+      setUrl({
+        ...url,
+        isInvalid: true,
+        errorMsg: t('required_url'),
+      });
+      return;
+    }
+    const urlRegex =
+      /^(https?:\/\/)?([\da-z\\.-]+)\.([a-z\\.]{2,6})([\\/\w \\.-]*)*\/?$/;
+    if (!urlRegex.test(url.value)) {
+      setUrl({
+        ...url,
+        isInvalid: true,
+        errorMsg: t('invalid_url'),
+      });
+      return;
+    }
+    onConfirm({
+      title: title.value,
+      url: url.value,
+    });
+    setShowState(false);
+
+    setTitle({
+      value: '',
+      isInvalid: false,
+      errorMsg: '',
+    });
+
+    setUrl({
+      value: '',
+      isInvalid: false,
+      errorMsg: '',
+    });
+  };
+  return (
+    <Modal show={show} onHide={handleHide}>
+      <Modal.Header closeButton>
+        <Modal.Title>{t('header')}</Modal.Title>
+      </Modal.Header>
+      <Modal.Body>
+        <Form>
+          <Form.Group className="mb-3" controlId="editor.plugin.embed.title">
+            <Form.Label>{t('title')}</Form.Label>
+            <Form.Control
+              type="text"
+              value={title.value}
+              isInvalid={title.isInvalid}
+              onChange={handleChangeTitle}
+            />
+            <Form.Control.Feedback type="invalid">
+              {title.errorMsg}
+            </Form.Control.Feedback>
+          </Form.Group>
+          <Form.Group className="mb-3" controlId="editor.plugin.embed.url">
+            <Form.Label>{t('url')}</Form.Label>
+            <Form.Control
+              type="url"
+              value={url.value}
+              isInvalid={url.isInvalid}
+              onChange={handleChangeUrl}
+            />
+            <Form.Control.Feedback type="invalid">
+              {url.errorMsg}
+            </Form.Control.Feedback>
+          </Form.Group>
+        </Form>
+      </Modal.Body>
+      <Modal.Footer>
+        <Button variant="link" onClick={handleHide}>
+          {t('cancel')}
+        </Button>
+        <Button variant="primary" onClick={handleSubmit}>
+          {t('add')}
+        </Button>
+      </Modal.Footer>
+    </Modal>
+  );
+};
+
+export default EmbedModal;
diff --git a/embed-basic/package.json b/embed-basic/package.json
new file mode 100644
index 0000000..b46ca78
--- /dev/null
+++ b/embed-basic/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "embed-basic",
+  "version": "0.0.1",
+  "description": "",
+  "main": "index.ts",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "Answer.dev",
+  "peerDependencies": {
+    "react": ">=16.14.0",
+    "react-bootstrap":">2.0.0",
+    "react-dom": ">=16.14.0",
+    "react-i18next":"^11.18.3"
+  }
+}
diff --git a/embed-basic/pnpm-lock.yaml b/embed-basic/pnpm-lock.yaml
new file mode 100644
index 0000000..321e769
--- /dev/null
+++ b/embed-basic/pnpm-lock.yaml
@@ -0,0 +1,315 @@
+lockfileVersion: '6.0'
+
+dependencies:
+  react:
+    specifier: '>=16.14.0'
+    version: 16.14.0
+  react-bootstrap:
+    specifier: '>2.0.0'
+    version: 2.0.1([email protected])([email protected])
+  react-dom:
+    specifier: '>=16.14.0'
+    version: 16.14.0([email protected])
+  react-i18next:
+    specifier: ^11.18.3
+    version: 11.18.3([email protected])([email protected])([email protected])
+
+packages:
+
+  /@babel/[email protected]:
+    resolution: {integrity: 
sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      regenerator-runtime: 0.14.1
+    dev: false
+
+  /@popperjs/[email protected]:
+    resolution: {integrity: 
sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
+    dev: false
+
+  /@react-aria/[email protected]([email protected]):
+    resolution: {integrity: 
sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==}
+    engines: {node: '>= 12'}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+    dependencies:
+      '@swc/helpers': 0.5.11
+      react: 16.14.0
+    dev: false
+
+  /@restart/[email protected]([email protected]):
+    resolution: {integrity: 
sha512-INJYZQJP7g+IoDUh/475NlGiTeMfwTXUEr3tmRneckHIxNolGOW9CTq83S8cxq0CgJwwcMzMJFchxvlwe7Rk8Q==}
+    peerDependencies:
+      react: '>=16.3.2'
+    dependencies:
+      react: 16.14.0
+    dev: false
+
+  /@restart/[email protected]([email protected]):
+    resolution: {integrity: 
sha512-s984xV/EapUIfkjlf8wz9weP2O9TNKR96C68FfMEy2bE69+H4cNv3RD4Mf97lW7Htt7PjZrYTjSC8f3SB9VCXw==}
+    peerDependencies:
+      react: '>=16.8.0'
+    dependencies:
+      dequal: 2.0.3
+      react: 16.14.0
+    dev: false
+
+  /@restart/[email protected]([email protected]):
+    resolution: {integrity: 
sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==}
+    peerDependencies:
+      react: '>=16.8.0'
+    dependencies:
+      dequal: 2.0.3
+      react: 16.14.0
+    dev: false
+
+  /@restart/[email protected]([email protected])([email protected]):
+    resolution: {integrity: 
sha512-lcutEWPvsxz0uEyRxuysCbHBfXDFnMKNMNTsnuPmLFjZXgW9fVmhksS6rpFklXHMwxOM9g6hRTBq0gS3QRKgzQ==}
+    peerDependencies:
+      react: '>=16.14.0'
+      react-dom: '>=16.14.0'
+    dependencies:
+      '@babel/runtime': 7.24.6
+      '@popperjs/core': 2.11.8
+      '@react-aria/ssr': 3.9.4([email protected])
+      '@restart/hooks': 0.4.16([email protected])
+      '@types/warning': 3.0.3
+      dequal: 2.0.3
+      dom-helpers: 5.2.1
+      prop-types: 15.8.1
+      react: 16.14.0
+      react-dom: 16.14.0([email protected])
+      uncontrollable: 7.2.1([email protected])
+      warning: 4.0.3
+    dev: false
+
+  /@swc/[email protected]:
+    resolution: {integrity: 
sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==}
+    dependencies:
+      tslib: 2.6.2
+    dev: false
+
+  /@types/[email protected]:
+    resolution: {integrity: 
sha512-IwpIMieE55oGWiXkQPSBY1nw1nFs6bsKXTFskNY8sdS17K24vyEBRQZEwlRS7ZmXCWnJcQtbxWzly+cODWGs2A==}
+    dev: false
+
+  /@types/[email protected]:
+    resolution: {integrity: 
sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
+    dev: false
+
+  /@types/[email protected]:
+    resolution: {integrity: 
sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
+    dependencies:
+      '@types/react': 18.3.3
+    dev: false
+
+  /@types/[email protected]:
+    resolution: {integrity: 
sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==}
+    dependencies:
+      '@types/prop-types': 15.7.12
+      csstype: 3.1.3
+    dev: false
+
+  /@types/[email protected]:
+    resolution: {integrity: 
sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
+    dependencies:
+      '@babel/runtime': 7.24.6
+      csstype: 3.1.3
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
+    dependencies:
+      void-elements: 3.1.0
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==}
+    dependencies:
+      '@babel/runtime': 7.24.6
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+    dependencies:
+      loose-envify: 1.4.0
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+    dependencies:
+      js-tokens: 4.0.0
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
+  /[email protected]([email protected]):
+    resolution: {integrity: 
sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==}
+    peerDependencies:
+      react: '>=0.14.0'
+    dependencies:
+      react: 16.14.0
+      react-is: 16.13.1
+      warning: 4.0.3
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+      react-is: 16.13.1
+    dev: false
+
+  /[email protected]([email protected])([email protected]):
+    resolution: {integrity: 
sha512-vx4fi3mHWWIzaAOJEzFfmTn/GHtirpASRk0C1PDBW5OZAt/VxLgUt+bvrSSh83eRmInq3wy8mERJMwuyI61KPA==}
+    peerDependencies:
+      react: '>=16.14.0'
+      react-dom: '>=16.14.0'
+    dependencies:
+      '@babel/runtime': 7.24.6
+      '@restart/context': 2.1.4([email protected])
+      '@restart/hooks': 0.3.27([email protected])
+      '@restart/ui': 0.2.6([email protected])([email protected])
+      '@types/invariant': 2.2.37
+      '@types/prop-types': 15.7.12
+      '@types/react': 18.3.3
+      '@types/react-transition-group': 4.4.10
+      '@types/warning': 3.0.3
+      classnames: 2.5.1
+      dom-helpers: 5.2.1
+      invariant: 2.2.4
+      prop-types: 15.8.1
+      prop-types-extra: 1.1.1([email protected])
+      react: 16.14.0
+      react-dom: 16.14.0([email protected])
+      react-transition-group: 4.4.5([email protected])([email protected])
+      uncontrollable: 7.2.1([email protected])
+      warning: 4.0.3
+    dev: false
+
+  /[email protected]([email protected]):
+    resolution: {integrity: 
sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==}
+    peerDependencies:
+      react: ^16.14.0
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+      prop-types: 15.8.1
+      react: 16.14.0
+      scheduler: 0.19.1
+    dev: false
+
+  /[email protected]([email protected])([email protected])([email protected]):
+    resolution: {integrity: 
sha512-EttTX31HbqzZymUM3SIrMPuvamfSXFZVsDHm/ZAqoDfTLjhzlwyxqfbDNxcKNAGOi2mjZaXfR7hSNMlvLNpB/g==}
+    peerDependencies:
+      i18next: '>= 19.0.0'
+      react: '>= 16.8.0'
+      react-dom: '*'
+      react-native: '*'
+    peerDependenciesMeta:
+      react-dom:
+        optional: true
+      react-native:
+        optional: true
+    dependencies:
+      '@babel/runtime': 7.24.6
+      html-parse-stringify: 3.0.1
+      i18next: 23.11.5
+      react: 16.14.0
+      react-dom: 16.14.0([email protected])
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
+    dev: false
+
+  /[email protected]([email protected])([email protected]):
+    resolution: {integrity: 
sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
+    peerDependencies:
+      react: '>=16.6.0'
+      react-dom: '>=16.6.0'
+    dependencies:
+      '@babel/runtime': 7.24.6
+      dom-helpers: 5.2.1
+      loose-envify: 1.4.0
+      prop-types: 15.8.1
+      react: 16.14.0
+      react-dom: 16.14.0([email protected])
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+      prop-types: 15.8.1
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==}
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
+    dev: false
+
+  /[email protected]([email protected]):
+    resolution: {integrity: 
sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==}
+    peerDependencies:
+      react: '>=15.0.0'
+    dependencies:
+      '@babel/runtime': 7.24.6
+      '@types/react': 18.3.3
+      invariant: 2.2.4
+      react: 16.14.0
+      react-lifecycles-compat: 3.0.4
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
+  /[email protected]:
+    resolution: {integrity: 
sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
+    dependencies:
+      loose-envify: 1.4.0
+    dev: false
diff --git a/embed-basic/tsconfig.json b/embed-basic/tsconfig.json
new file mode 100644
index 0000000..f6c88fb
--- /dev/null
+++ b/embed-basic/tsconfig.json
@@ -0,0 +1,31 @@
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "lib": [
+      "ES2020",
+      "DOM",
+      "DOM.Iterable"
+    ],
+    "module": "ESNext",
+    "skipLibCheck": true,
+    /* Bundler mode */
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noImplicitAny": false,
+    "noEmit": true,
+    "jsx": "react-jsx",
+    /* Linting */
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+  },
+  "references": [
+    {
+      "path": "./tsconfig.node.json"
+    }
+  ]
+}
diff --git a/embed-basic/tsconfig.node.json b/embed-basic/tsconfig.node.json
new file mode 100644
index 0000000..42872c5
--- /dev/null
+++ b/embed-basic/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "skipLibCheck": true,
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "allowSyntheticDefaultImports": true
+  },
+  "include": ["vite.config.ts"]
+}

Reply via email to