diff --git a/docs/comment/src/config/README.md b/docs/comment/src/config/README.md
index 4b6e903f0..345f747a3 100644
--- a/docs/comment/src/config/README.md
+++ b/docs/comment/src/config/README.md
@@ -3,15 +3,13 @@ title: API Config
icon: config
---
-## type
+## provider
-- Type: `'valine' | 'vssue' | 'disable'`
-- Required: true
+- Type: `"Giscus" | "Twikoo" | "Waline" | 'Valine' | 'Vssue' | "None"`
+- Default: `"None"`
Comment service provider.
-Setting it to `'disable'` will only disable the comment feature.
-
## comment
- Type: `boolean`
@@ -19,10 +17,18 @@ Setting it to `'disable'` will only disable the comment feature.
Whether to enable comment feature by default.
+## Giscus config
+
+- [See here](giscus.md)
+
## Waline config
- [See here](waline.md)
+## Twikoo config
+
+- [See here](twikoo.md)
+
## Vssue config
- [See here](vssue.md)
diff --git a/docs/comment/src/config/giscus.md b/docs/comment/src/config/giscus.md
new file mode 100644
index 000000000..23a01a655
--- /dev/null
+++ b/docs/comment/src/config/giscus.md
@@ -0,0 +1,53 @@
+---
+title: Giscus Options
+icon: github
+---
+
+## repo
+
+- Type: `string`
+- Required: No
+
+The name of repository to store discussions.
+
+## repoId
+
+- Type: `string`
+- Required: No
+
+The ID of repository to store discussions. Generate through [Giscus Page](https://giscus.app/)
+
+## category
+
+- Type: `string`
+- Required: No
+
+The name of the discussion category.
+
+## categoryId
+
+- Type: `string`
+- Required: No
+
+The ID of the discussion category. Generate through [Giscus Page](https://giscus.app/)
+
+## mapping
+
+- Type: `string`
+- Default: `"pathname"`
+
+Page - Discussion mapping. For details see [Giscus Page](https://giscus.app/)
+
+## reactionsEnabled
+
+- Type: `boolean`
+- Default: `true`
+
+Whether enable reactions or not
+
+## inputPosition
+
+- Type: `"top" | "bottom"`
+- Default: `"top"`
+
+Input position
diff --git a/docs/comment/src/config/twikoo.md b/docs/comment/src/config/twikoo.md
new file mode 100644
index 000000000..09e672929
--- /dev/null
+++ b/docs/comment/src/config/twikoo.md
@@ -0,0 +1,11 @@
+---
+title: Twikoo Options
+icon: comment
+---
+
+## envId
+
+- Type: `string`
+- Required: Yes
+
+Vercel address
diff --git a/docs/comment/src/guide/README.md b/docs/comment/src/guide/README.md
index b91f6f5eb..5ad01d194 100644
--- a/docs/comment/src/guide/README.md
+++ b/docs/comment/src/guide/README.md
@@ -3,7 +3,7 @@ title: Guide
icon: creative
---
-This plugin register `` comonent globally.
+This plugin register `` component globally.
We recommended you to insert the comment component (``) after the `` component.
@@ -11,22 +11,27 @@ By default, `` component is enabled globally. You can disable
To keep it globally disabled, please set `comment` to `false` in the plugin options. Then you can set `comment: true` in page frontmatter to enable it locally.
-You can choose from 3 comment service provider: Waline, Vssue and Valine.
+Currently you can choose from Giscus, Waline, Valine, Vssue and Twikoo.
-::: tip Comparison between services
+::: tip Comment service selection
-- Waline uses a backend server to support comment and pageview statistics, and you can comment without logging in to any account. It needs extra configuration on backend, and you can deploy on vercel for free.
-- Vssue uses the issue panel of the code platform repo and requires the user to login or register the corresponding platform account.
-- Valine uses leancloud to support pageview statistics, and you can comment without logging in to any account
-
-If your site is for the general public rather than programmers, Waline is recommended, otherwise Vssue is recommended.
+- Giscus is recommended if your blog or documentation is primarily geared towards programmers.
+- If your blog or documentation is for the general public, Waline is recommended.
:::
-### Waline
+## Giscus
+
+See [Giscus Config Guide](giscus.md)
+
+## Waline
See [Waline Config Guide](waline.md)
+## Twikoo
+
+See [Twikoo Config Guide](twikoo.md)
+
### Vssue
See [Vssue Config Guide](vssue.md)
diff --git a/docs/comment/src/guide/giscus.md b/docs/comment/src/guide/giscus.md
new file mode 100644
index 000000000..517775168
--- /dev/null
+++ b/docs/comment/src/guide/giscus.md
@@ -0,0 +1,27 @@
+---
+title: Giscus
+icon: github
+---
+
+Giscus is a GitHub Discussion based commenting system that is easy to start.
+
+
+
+## Preparation
+
+1. You need to create a public repository and open discussion as a place to store comments
+1. You need to install the [Giscus App](https://github.com/apps/giscus) to have permission to access the corresponding repository.
+
+After completing the above steps, please go to the [Giscus page](https://giscus.app) to get your settings. You just need to fill in the repository and Discussion categories, then scroll to the "Enable giscus" section at the bottom of the page and copy the `data-repo`, `data-repo-id`, `data-category` and `data-category-id` four items as they are required.
+
+## Config
+
+Please pass `data-repo`, `data-repo-id`, `data-category` and `data-category-id` as plugin options as `repo`, `repoId`, `category` `categoryId`.
+
+::: info Darkmode
+
+To let Giscus use the correct theme, you need to pass a boolean value to `` via the `darkmode` property, indicating whether darkmode is currently enabled.
+
+:::
+
+For other options, see [Giscus Config](../config/giscus.md).
diff --git a/docs/comment/src/guide/twikoo.md b/docs/comment/src/guide/twikoo.md
new file mode 100644
index 000000000..3160d7909
--- /dev/null
+++ b/docs/comment/src/guide/twikoo.md
@@ -0,0 +1,24 @@
+---
+title: Twikoo
+icon: comment
+---
+
+A concise, safe and free static site commenting system, based on [Tencent Cloud Development](https://curl.qcloud.com/KnnJtUom).
+
+
+
+## Getting started
+
+### Vercel Deployment
+
+1. Apply for [MongoDB](https://www.mongodb.com/cloud/atlas/register) account
+1. Create a free MongoDB database, the recommended region is `AWS / N. Virginia (us-east-1)`
+1. Click CONNECT on the Clusters page, follow the steps to allow connections from all IP addresses ([Why?](https://vercel.com/support/articles/how-to-allowlist-deployment-ip-address)), create Database user, and record the database connection string, please change the `` in the connection string to the database password
+1. Sign up for a [Vercel](https://vercel.com/signup) account
+1. Click the button below to deploy Twikoo to Vercel in one click
+
+ [![Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/imaegoo/twikoo/tree/dev/src/vercel-min)
+
+1. Go to Settings - Environment Variables, add the environment variable `MONGODB_URI`, the value is the database connection string in step 3
+1. Go to Overview, click the link under Domains, if the environment configuration is correct, you can see the prompt "Twikoo cloud function is running normally"
+1. Vercel Domains (with `https://` prefix, eg `https://xxx.vercel.app`) is your environment ID
diff --git a/docs/comment/src/zh/config/README.md b/docs/comment/src/zh/config/README.md
index 82e3337e9..30442561a 100644
--- a/docs/comment/src/zh/config/README.md
+++ b/docs/comment/src/zh/config/README.md
@@ -5,10 +5,10 @@ icon: config
## type
-- 类型: `'valine' | 'vssue' | 'disable'`
-- 必填: 是
+- 类型: `"Giscus" | "Twikoo" | "Waline" | 'Valine' | 'Vssue' | "None"`
+- 默认值: `"None"`
-使用 Valine 还是 Vssue。设置为 `'disabled'` 仅会禁用评论功能。
+使用的评论服务。
## comment
@@ -17,10 +17,18 @@ icon: config
是否默认启用评论功能。
+## Giscus 选项
+
+- [点击查看](giscus.md)
+
## Waline 选项
- [点击查看](waline.md)
+## Twikoo 选项
+
+- [点击查看](twikoo.md)
+
## Vssue 选项
- [点击查看](vssue.md)
diff --git a/docs/comment/src/zh/config/giscus.md b/docs/comment/src/zh/config/giscus.md
new file mode 100644
index 000000000..b3b2cd87f
--- /dev/null
+++ b/docs/comment/src/zh/config/giscus.md
@@ -0,0 +1,53 @@
+---
+title: Giscus 选项
+icon: github
+---
+
+## repo
+
+- 类型: `string`
+- 必填: 是
+
+存放评论的仓库
+
+## repoId
+
+- 类型: `string`
+- 必填: 是
+
+仓库 ID,请从 [Giscus 页面](https://giscus.app/zh-CN) 生成。
+
+## category
+
+- 类型: `string`
+- 必填: 是
+
+讨论分类
+
+## categoryId
+
+- 类型: `string`
+- 必填: 是
+
+讨论分类 ID,请从 [Giscus 页面](https://giscus.app/zh-CN) 生成。
+
+## mapping
+
+- 类型: `string`
+- 默认值: `"pathname"`
+
+页面 ↔️ discussion 映射关系,详见 [Giscus 页面](https://giscus.app/zh-CN)。
+
+## reactionsEnabled
+
+- 类型: `boolean`
+- 默认值: `true`
+
+是否启用主帖子上的反应
+
+## inputPosition
+
+- 类型: `"top" | "bottom"`
+- 默认值: `"top"`
+
+输入框的位置
diff --git a/docs/comment/src/zh/config/twikoo.md b/docs/comment/src/zh/config/twikoo.md
new file mode 100644
index 000000000..85737a370
--- /dev/null
+++ b/docs/comment/src/zh/config/twikoo.md
@@ -0,0 +1,18 @@
+---
+title: Twikoo 选项
+icon: comment
+---
+
+## envId
+
+- 类型: `string`
+- 必填: 是
+
+腾讯云环境 ID 或 Vercel 地址。
+
+## repoId
+
+- 类型: `string`
+- 默认值: `"ap-shanghai"`
+
+腾讯云区域。
diff --git a/docs/comment/src/zh/guide/README.md b/docs/comment/src/zh/guide/README.md
index fde46062d..baefc7eed 100644
--- a/docs/comment/src/zh/guide/README.md
+++ b/docs/comment/src/zh/guide/README.md
@@ -11,22 +11,27 @@ icon: creative
如果你需要保持全局禁用,请在插件选项中设置 `comment` 为 `false`。这样你可以在特定页面的 frontmatter 中设置 `comment: true` 来局部启用它。
-有三个评论插件可以选择: Waline、Vssue 和 Valine。
+目前可以从 Giscus、Waline 和 Twikoo 中选择。
-::: tip 评论服务的比较
+::: tip 评论服务选择
-- Waline 需要后端服务器以及额外的配置,支持页面访问量统计,无需登录账号即可评论。可以使用 Vercel。
-- Vssue 使用代码平台仓库的 issue 面板,需要用户登录或注册相应平台账号。
-- Valine 使用 leancloud,支持页面访问量统计,无需登录账号即可评论
-
-如果你的站点面向大众而非程序员,推荐使用 Waline;反之推荐 Vssue。
+- 如果你的博客或文档主要面向程序员,建议使用 Giscus。
+- 如果你的博客或文档面向大众,建议使用 Waline。
:::
-### Waline
+## Giscus
+
+[详见 Giscus 指南](giscus.md)
+
+## Waline
[详见 Waline 指南](waline.md)
+## Twikoo
+
+[详见 Twikoo 指南](twikoo.md)
+
### Vssue
[详见 Vssue 指南](vssue.md)
diff --git a/docs/comment/src/zh/guide/giscus.md b/docs/comment/src/zh/guide/giscus.md
new file mode 100644
index 000000000..5999ca5dc
--- /dev/null
+++ b/docs/comment/src/zh/guide/giscus.md
@@ -0,0 +1,27 @@
+---
+title: Giscus
+icon: github
+---
+
+Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。
+
+
+
+## 准备工作
+
+1. 你需要创建一个公开仓库,并开启评论区,以作为评论存放的地点
+1. 你需要安装 [Giscus App](https://github.com/apps/giscus),使其有权限访问对应仓库。
+
+在完成以上步骤后,请前往 [Giscus 页面](https://giscus.app/zh-CN) 获得你的设置。你只需要填写仓库和 Discussion 分类,之后滚动到页面下部的 “启用 giscus” 部分,复制 `data-repo`, `data-repo-id`, `data-category` 和 `data-category-id` 四项,因为它们是必须的。
+
+## 配置
+
+请将 `data-repo`, `data-repo-id`, `data-category` 和 `data-category-id` 作为插件选项传入 `repo`, `repoId`, `category` `categoryId`。
+
+::: info 夜间模式
+
+为了能使 Giscus 使用正确的主题,你需要为 `` 通过 `darkmode` 属性传入一个布尔值,代表当前是否开启夜间模式。
+
+:::
+
+其他的配置项详见 [Giscus 配置](../config/giscus.md)。
diff --git a/docs/comment/src/zh/guide/twikoo.md b/docs/comment/src/zh/guide/twikoo.md
new file mode 100644
index 000000000..1c5b0d88e
--- /dev/null
+++ b/docs/comment/src/zh/guide/twikoo.md
@@ -0,0 +1,124 @@
+---
+title: Twikoo
+icon: comment
+---
+
+一个简洁、安全、免费的静态网站评论系统,基于 [腾讯云开发](https://curl.qcloud.com/KnnJtUom)。
+
+
+
+## 快速上手
+
+部署共有四种方式。
+
+| 部署方式 | 描述 |
+| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [一键部署](#一键部署) | \[不建议\] 虽然方便,但是仅支持按量计费环境——也就是说,**一键部署的环境,当免费资源用尽后,将会产生费用**。且按量计费环境无法切换为包年包月环境。免费额度数据库读操作数只有 500 次 / 天,**无法支撑 Twikoo 的运行需求**。 |
+| [手动部署](#手动部署) | \[建议\] 手动部署到腾讯云云开发环境,在中国大陆访问速度较快。由于基础版 1 已从 0 元涨价至 6.9 元 / 月,需要付费购买环境才能部署。 |
+| [命令行部署](#命令行部署) | \[不建议\] 仅针对有 Node.js 经验的开发者。 |
+| [Vercel 部署](#vercel-部署) | \[建议\] 适用于想要免费部署的用户,在中国大陆访问速度较慢。 |
+
+### 一键部署
+
+1. 点击以下按钮将 Twikoo 一键部署到云开发
+
+ [![部署到云开发](https://main.qcloudimg.com/raw/67f5a389f1ac6f3b4d04c7256438e44f.svg)](https://console.cloud.tencent.com/tcb/env/index?action=CreateAndDeployCloudBaseProject&appUrl=https%3A%2F%2Fgithub.com%2Fimaegoo%2Ftwikoo&branch=dev)
+
+1. 进入[环境 - 登录授权](https://console.cloud.tencent.com/tcb/env/login),启用“匿名登录”
+1. 进入[环境 - 安全配置](https://console.cloud.tencent.com/tcb/env/safety),将网站域名添加到“WEB 安全域名”
+
+### 手动部署
+
+如果你打算部署到一个现有的云开发环境,请直接从第 3 步开始。
+
+1. 进入[云开发 CloudBase](https://curl.qcloud.com/KnnJtUom)活动页面,滚动到“新用户专享”部分,选择适合的套餐,点击“立即购买”,按提示创建好环境。
+
+ ::: tip 提示
+
+ - 推荐创建上海环境。如选择广州环境,需要在 `twikoo.init()` 时额外指定环境 `region: "ap-guangzhou"`
+ - 环境名称自由填写
+ - 推荐选择计费方式`包年包月`,套餐版本`基础版 1`,超出免费额度不会收费
+ - 如果提示选择“应用模板”,请选择“空模板”
+
+ :::
+
+1. 进入[云开发控制台](https://console.cloud.tencent.com/tcb/)
+1. 进入[环境-登录授权](https://console.cloud.tencent.com/tcb/env/login),启用“匿名登录”
+1. 进入[环境-安全配置](https://console.cloud.tencent.com/tcb/env/safety),将网站域名添加到“WEB 安全域名”
+1. 进入[环境-云函数](https://console.cloud.tencent.com/tcb/scf/index),点击“新建云函数”
+1. 函数名称请填写 `twikoo`,创建方式请选择 `空白函数`,运行环境请选择 `Nodejs 10.15`,函数内存请选择 `128MB`,点击“下一步”
+1. 清空输入框中的示例代码,复制以下代码、粘贴到“函数代码”输入框中,点击“确定”
+
+ ```js
+ exports.main = require("twikoo-func").main;
+ ```
+
+1. 创建完成后,点击“twikoo”进入云函数详情页,进入“函数代码”标签,点击“文件 - 新建文件”,输入 `package.json`,回车
+1. 复制以下代码、粘贴到代码框中,点击“保存并安装依赖”
+
+ ```json
+ { "dependencies": { "twikoo-func": "1.5.0" } }
+ ```
+
+### 命令行部署
+
+::: warning 注意
+
+- 请确保你已经安装了 [Node.js](https://nodejs.org/en/download/)
+- 请将命令、代码中“你的环境 ID”替换为你自己的环境 ID
+- 第 7 步会弹出浏览器要求授权,需在有图形界面的系统下进行
+- 请勿在 Termux 下操作。虽然可以部署成功,但是使用时会报错 `[FUNCTIONS_EXECUTE_FAIL] Error: EACCES: permission denied, open '/var/user/index.js'`
+
+:::
+
+如果你打算部署到一个现有的云开发环境,请直接从第 3 步开始。
+
+1. 进入[云开发 CloudBase](https://curl.qcloud.com/KnnJtUom)活动页面,滚动到“新用户专享”部分,选择适合的套餐 (一般 0 元套餐即可) ,点击“立即购买”,按提示创建好环境。
+1. 进入[云开发控制台](https://console.cloud.tencent.com/tcb/)
+1. 进入[环境 - 登录授权](https://console.cloud.tencent.com/tcb/env/login),启用“匿名登录”
+1. 进入[环境 - 安全配置](https://console.cloud.tencent.com/tcb/env/safety),将网站域名添加到“WEB 安全域名”
+1. 克隆本仓库
+
+ ```sh
+ git clone https://github.com/imaegoo/twikoo.git # 或 git clone https://e.coding.net/imaegoo/twikoo/twikoo.git
+ cd twikoo
+ ```
+
+ > 如果你没有安装 Git,也可以从 [Release](https://github.com/imaegoo/twikoo/releases) 页面下载最新的 Source code
+ >
+ > 如果你所在的地区访问 GitHub 速度慢,也可以尝试另一个仓库地址: [https://imaegoo.coding.net/public/twikoo/twikoo/git](https://imaegoo.coding.net/public/twikoo/twikoo/git)
+
+1. 安装依赖项
+
+ ```sh
+ npm install -g yarn # 如 yarn 已安装,可以跳过此步
+ yarn install
+ ```
+
+1. 授权云开发环境 (此命令会弹出浏览器要求授权,需在有图形界面的系统下进行)
+
+ ```sh
+ yarn run login
+ ```
+
+1. 自动部署
+
+ ```sh
+ yarn deploy -e 你的环境id
+ ```
+
+### Vercel 部署
+
+[查看视频教程](https://www.bilibili.com/video/BV1Fh411e7ZH)
+
+1. 申请 [MongoDB](https://www.mongodb.com/cloud/atlas/register) 账号
+1. 创建免费 MongoDB 数据库,区域推荐选择 `AWS / N. Virginia (us-east-1)`
+1. 在 Clusters 页面点击 CONNECT,按步骤设置允许所有 IP 地址的连接 ([为什么?](https://vercel.com/support/articles/how-to-allowlist-deployment-ip-address)) ,创建数据库用户,并记录数据库连接字符串,请将连接字符串中的 `` 修改为数据库密码
+1. 申请 [Vercel](https://vercel.com/signup) 账号
+1. 点击以下按钮将 Twikoo 一键部署到 Vercel
+
+ [![Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/imaegoo/twikoo/tree/dev/src/vercel-min)
+
+1. 进入 Settings - Environment Variables,添加环境变量 `MONGODB_URI`,值为第 3 步的数据库连接字符串
+1. 进入 Overview,点击 Domains 下方的链接,如果环境配置正确,可以看到 “Twikoo 云函数运行正常” 的提示
+1. Vercel Domains (包含 `https://` 前缀,例如 `https://xxx.vercel.app`) 即为你的环境 ID
diff --git a/packages/comment/package.json b/packages/comment/package.json
index f6d2a1d37..d32cbaf75 100644
--- a/packages/comment/package.json
+++ b/packages/comment/package.json
@@ -47,6 +47,8 @@
"@vssue/api-gitlab-v4": "^1.4.7",
"@vssue/vuepress-plugin-vssue": "^1.4.8",
"@waline/client": "^2.5.1",
+ "giscus": "^1.0.5",
+ "twikoo": "^1.5.11",
"valine": "^1.4.18",
"vssue": "^1.4.8",
"vuepress-shared": "workspace:*"
diff --git a/packages/comment/src/client/components/Giscus.ts b/packages/comment/src/client/components/Giscus.ts
new file mode 100644
index 000000000..86853e82d
--- /dev/null
+++ b/packages/comment/src/client/components/Giscus.ts
@@ -0,0 +1,111 @@
+import Vue from "vue";
+
+import type { VNode } from "vue";
+import type { GiscusLang, GiscusMapping, GiscusProps } from "../utils";
+import type { GiscusOptions } from "../../types";
+
+import "../styles/giscus.styl";
+
+const SUPPORTED_LANGUAGES: GiscusLang[] = [
+ "de",
+ "gsw",
+ "en",
+ "es",
+ "fr",
+ "id",
+ "it",
+ "ja",
+ "ko",
+ "pl",
+ "ro",
+ "ru",
+ "vi",
+ "zh-CN",
+ "zh-TW",
+];
+
+const giscusOption = COMMENT_OPTIONS as GiscusOptions;
+
+const enableGiscus = Boolean(
+ giscusOption.repo &&
+ giscusOption.repoId &&
+ giscusOption.category &&
+ giscusOption.categoryId
+);
+
+export default Vue.extend({
+ name: "GiscusComment",
+
+ props: {
+ darkmode: Boolean,
+ },
+
+ data: () => ({
+ loaded: false,
+ }),
+
+ computed: {
+ giscusLang(): GiscusLang {
+ const lang = this.$lang as GiscusLang;
+
+ if (SUPPORTED_LANGUAGES.includes(lang)) return lang;
+
+ const shortCode = lang.split("-")[0] as GiscusLang;
+
+ if (SUPPORTED_LANGUAGES.includes(shortCode)) return shortCode;
+
+ return "en";
+ },
+
+ enableComment(): boolean {
+ if (!enableGiscus) return false;
+ const pluginConfig = giscusOption.comment !== false;
+ const pageConfig = this.$frontmatter.comment;
+
+ return (
+ // Enable in page
+ Boolean(pageConfig) ||
+ // not disabled in anywhere
+ (pluginConfig !== false && pageConfig !== false)
+ );
+ },
+
+ config(): GiscusProps {
+ return {
+ repo: giscusOption.repo as `${string}/${string}`,
+ repoId: giscusOption.repoId,
+ category: giscusOption.category,
+ categoryId: giscusOption.categoryId,
+ lang: this.giscusLang,
+ theme: this.darkmode ? "dark" : "light",
+ mapping: (giscusOption.mapping || "specific") as GiscusMapping,
+ term: this.$withBase(this.$route.path),
+ inputPosition: giscusOption.inputPosition || "top",
+ reactionsEnabled: giscusOption.reactionsEnabled !== false ? "1" : "0",
+ emitMetadata: "0",
+ };
+ },
+ },
+
+ mounted() {
+ void import("giscus").then(() => {
+ this.loaded = true;
+ });
+ },
+
+ render(h): VNode {
+ return h(
+ "div",
+ {
+ class: [
+ "giscus-wrapper",
+ { "input-top": giscusOption.inputPosition !== "bottom" },
+ ],
+ style: {
+ display: this.enableComment ? "block" : "none",
+ },
+ },
+ [this.loaded ? h("giscus-widget", { props: this.config }) : ""]
+ );
+ },
+});
diff --git a/packages/comment/src/client/components/Twikoo.ts b/packages/comment/src/client/components/Twikoo.ts
new file mode 100644
index 000000000..25a22ecfc
--- /dev/null
+++ b/packages/comment/src/client/components/Twikoo.ts
@@ -0,0 +1,65 @@
+import Vue from "vue";
+
+import type { TwikooOptions } from "../../types";
+
+import "../styles/twikoo.styl";
+
+const twikooOption = COMMENT_OPTIONS as TwikooOptions;
+
+const enableTwikoo = Boolean(twikooOption.envId);
+
+export default Vue.extend({
+ name: "TwikooComment",
+
+ data: () => ({ id: 0 }),
+
+ computed: {
+ enableComment(): boolean {
+ if (!enableTwikoo) return false;
+ const pluginConfig = twikooOption.comment !== false;
+ const pageConfig = this.$frontmatter.comment;
+
+ return (
+ // Enable in page
+ Boolean(pageConfig) ||
+ // not disabled in anywhere
+ (pluginConfig !== false && pageConfig !== false)
+ );
+ },
+ },
+
+ mounted() {
+ if (enableTwikoo) this.initTwikoo();
+ },
+
+ methods: {
+ initTwikoo(): void {
+ const timeID = (this.id = new Date().getTime());
+
+ void Promise.all([
+ import("twikoo"),
+ new Promise((resolve) => {
+ setTimeout(resolve, twikooOption.delay);
+ }),
+ ]).then(([{ init }]) => {
+ if (timeID === this.id)
+ void init({
+ lang: this.$lang === "zh-CN" ? "zh-CN" : "en",
+ ...twikooOption,
+ el: "#twikoo-comment",
+ });
+ });
+ },
+ },
+
+ render(h) {
+ return h(
+ "div",
+ {
+ class: "twikoo-wrapper",
+ style: { display: this.enableComment ? "block" : "none" },
+ },
+ [h("div", { attrs: { id: "twikoo-comment" } })]
+ );
+ },
+});
diff --git a/packages/comment/src/client/components/Waline.ts b/packages/comment/src/client/components/Waline.ts
index 76f2e8c35..7a2c0e76f 100644
--- a/packages/comment/src/client/components/Waline.ts
+++ b/packages/comment/src/client/components/Waline.ts
@@ -4,6 +4,8 @@ import { Route } from "vue-router";
import type { WalineLocale, WalineInstance } from "@waline/client";
import type { WalineOptions } from "../../types";
+import "../styles/waline.styl";
+
let timeout: NodeJS.Timeout | null = null;
const options = COMMENT_OPTIONS as WalineOptions;
@@ -91,4 +93,15 @@ export default Vue.extend({
if (timeout) clearTimeout(timeout);
this.waline?.destroy();
},
+
+ render(h) {
+ return h(
+ "div",
+ {
+ class: "waline-wrapper",
+ style: { display: this.enableComment ? "block" : "none" },
+ },
+ [h("div", { attrs: { id: "waline-comment" } })]
+ );
+ },
});
diff --git a/packages/comment/src/client/styles/giscus.styl b/packages/comment/src/client/styles/giscus.styl
new file mode 100644
index 000000000..f6a4b0c43
--- /dev/null
+++ b/packages/comment/src/client/styles/giscus.styl
@@ -0,0 +1,9 @@
+@require '~vuepress-shared/styles/wrapper';
+
+.page .giscus-wrapper {
+ wrapper();
+
+ &.input-top .giscus {
+ margin-bottom: -3rem;
+ }
+}
diff --git a/packages/comment/src/client/styles/twikoo.styl b/packages/comment/src/client/styles/twikoo.styl
new file mode 100644
index 000000000..5af41ce75
--- /dev/null
+++ b/packages/comment/src/client/styles/twikoo.styl
@@ -0,0 +1,5 @@
+@require '~vuepress-shared/styles/wrapper';
+
+.page .twikoo-wrapper {
+ wrapper();
+}
diff --git a/packages/comment/src/client/components/Waline.vue b/packages/comment/src/client/styles/waline.styl
similarity index 74%
rename from packages/comment/src/client/components/Waline.vue
rename to packages/comment/src/client/styles/waline.styl
index db42627de..5293a7a68 100644
--- a/packages/comment/src/client/components/Waline.vue
+++ b/packages/comment/src/client/styles/waline.styl
@@ -1,12 +1,3 @@
-
-
-
-
-
-
-
-
-
diff --git a/packages/comment/src/client/utils/giscus.ts b/packages/comment/src/client/utils/giscus.ts
new file mode 100644
index 000000000..63d407e94
--- /dev/null
+++ b/packages/comment/src/client/utils/giscus.ts
@@ -0,0 +1,61 @@
+type BooleanString = "0" | "1";
+
+export type GiscusRepo = `${string}/${string}`;
+
+export type GiscusMapping =
+ | "url"
+ | "title"
+ | "og:title"
+ | "specific"
+ | "number"
+ | "pathname";
+
+export type GiscusTheme =
+ | "light"
+ | "light_high_contrast"
+ | "light_protanopia"
+ | "dark"
+ | "dark_high_contrast"
+ | "dark_protanopia"
+ | "dark_dimmed"
+ | "transparent_dark"
+ | "preferred_color_scheme"
+ | `https://${string}`;
+
+export type GiscusInputPosition = "top" | "bottom";
+
+export type GiscusLang =
+ | "de"
+ | "gsw"
+ | "en"
+ | "es"
+ | "fr"
+ | "id"
+ | "it"
+ | "ja"
+ | "ko"
+ | "pl"
+ | "ro"
+ | "ru"
+ | "tr"
+ | "vi"
+ | "zh-CN"
+ | "zh-TW";
+
+export type GiscusLoading = "lazy" | "eager";
+
+export interface GiscusProps {
+ id?: string | undefined;
+ repo: GiscusRepo;
+ repoId: string;
+ category?: string | undefined;
+ categoryId?: string | undefined;
+ mapping: GiscusMapping;
+ term?: string | undefined;
+ theme?: GiscusTheme | undefined;
+ reactionsEnabled?: BooleanString | undefined;
+ emitMetadata?: BooleanString | undefined;
+ inputPosition?: GiscusInputPosition | undefined;
+ lang?: GiscusLang | undefined;
+ loading?: GiscusLoading | undefined;
+}
diff --git a/packages/comment/src/client/utils/index.ts b/packages/comment/src/client/utils/index.ts
new file mode 100644
index 000000000..990bd9af2
--- /dev/null
+++ b/packages/comment/src/client/utils/index.ts
@@ -0,0 +1 @@
+export * from "./giscus";
diff --git a/packages/comment/src/node/compact.ts b/packages/comment/src/node/compact.ts
index 03b2f10cf..6717e2308 100644
--- a/packages/comment/src/node/compact.ts
+++ b/packages/comment/src/node/compact.ts
@@ -1,10 +1,20 @@
-import type { WalineOptions } from "../types";
+import type { CommentOptions } from "../types";
/** @deprecated */
-export const covertWalineOptions = (
- options: WalineOptions & Record
+export const covertOptions = (
+ options: CommentOptions & Record
): void => {
- if (options.type === "waline") {
+ if ("type" in options) {
+ console.warn(`"type" is deprecated, please use "provider".`);
+ if (options["type"] === "waline") options.provider = "Waline";
+ else if (options["type"] === "vssue") options.provider = "Vssue";
+ else if (options["type"] === "valine") options.provider = "Valine";
+
+ delete options["type"];
+ }
+
+ // covert Waline options
+ if (options.provider === "Waline") {
[
// valine
["emojiCDN", "emoji"],
diff --git a/packages/comment/src/node/plugin.ts b/packages/comment/src/node/plugin.ts
index f4e0f4321..8ed9edf3f 100644
--- a/packages/comment/src/node/plugin.ts
+++ b/packages/comment/src/node/plugin.ts
@@ -1,19 +1,17 @@
import { getLocales, noopModule } from "vuepress-shared";
import { resolve } from "path";
-import { covertWalineOptions } from "./compact";
+import { covertOptions } from "./compact";
import { walineLocales, valineLocales } from "./locales";
-import type { CommentOptions, WalineOptions } from "../types";
+import type { CommentOptions } from "../types";
import type { Plugin, PluginEntry } from "@mr-hope/vuepress-types";
export const commentPlugin: Plugin = (options, context) => {
- // FIXME: This is a compact code
- if (options.type === "waline")
- covertWalineOptions(options as WalineOptions & Record);
+ covertOptions(options as CommentOptions & Record);
const PLUGIN_NAME = "vuepress-plugin-comment1";
const userValineLocales =
- options.type === "valine"
+ options.provider === "Valine"
? getLocales({
context,
config: options.valineLocales,
@@ -22,7 +20,7 @@ export const commentPlugin: Plugin = (options, context) => {
})
: {};
const userWalineLocales =
- options.type === "waline"
+ options.provider === "Waline"
? getLocales({
context,
config: options.walineLocales,
@@ -46,11 +44,15 @@ export const commentPlugin: Plugin = (options, context) => {
alias: {
"@CommentService":
- options.type === "valine"
+ options.provider === "Giscus"
+ ? resolve(__dirname, "../client/components/Giscus.js")
+ : options.provider === "Waline"
+ ? resolve(__dirname, "../client/components/Waline.js")
+ : options.provider === "Valine"
? resolve(__dirname, "../client/components/Valine.vue")
- : options.type === "waline"
- ? resolve(__dirname, "../client/components/Waline.vue")
- : options.type === "vssue"
+ : options.provider === "Twikoo"
+ ? resolve(__dirname, "../client/components/Twikoo.js")
+ : options.provider === "Vssue"
? resolve(__dirname, "../client/components/Vssue.js")
: noopModule,
},
@@ -58,7 +60,7 @@ export const commentPlugin: Plugin = (options, context) => {
enhanceAppFiles: resolve(__dirname, "../client/enhanceAppFile.js"),
};
- if (options.type === "vssue")
+ if (options.provider === "Vssue")
config.plugins = [["@vssue/vuepress-plugin-vssue", options]];
return config;
diff --git a/packages/comment/src/types/declare.d.ts b/packages/comment/src/types/declare.d.ts
index 21834bd25..6066ab5bc 100644
--- a/packages/comment/src/types/declare.d.ts
+++ b/packages/comment/src/types/declare.d.ts
@@ -1,3 +1,145 @@
+declare module "twikoo" {
+ export interface TwikooInitOptions {
+ /**
+ * Environment ID for tencloud
+ *
+ * Link for Vercel
+ */
+ envId: string;
+
+ /**
+ * Tencloud region
+ *
+ * @default 'ap-shanghai'
+ */
+ region?: string;
+
+ /**
+ * Container Element selector
+ */
+ el: string;
+
+ /**
+ * Article path
+ *
+ * @default window.location.pathname
+ */
+ path?: string;
+
+ /**
+ * Diplay language
+ */
+ lang?: string;
+
+ /**
+ * Calback when comment loaded
+ */
+ onCommentLoaded?: () => void;
+ }
+ export interface TwikooCommentCountOptions {
+ /**
+ * Environment ID for tencloud
+ *
+ * Link for Vercel
+ */
+ envId: string;
+
+ /**
+ * Tencloud region
+ *
+ * @default 'ap-shanghai'
+ */
+ region?: string;
+
+ /**
+ * @description protocal, domain and url should not be included
+ */
+ urls: string[];
+ /**
+ * Whether include reply
+ *
+ * @default false
+ */
+ includeReply?: boolean;
+ }
+
+ export interface TwikooCommentCountResult {
+ url: string;
+ count: number;
+ }
+
+ export interface TwikooRecentCommentsOptions {
+ /**
+ * Environment ID for tencloud
+ *
+ * Link for Vercel
+ */
+ envId: string;
+
+ /**
+ * Tencloud region
+ *
+ * @default 'ap-shanghai'
+ */
+ region?: string;
+
+ /**
+ * @description max to 100
+ *
+ * @default 10
+ */
+ pageSize?: number;
+ /**
+ * Whether include reply
+ *
+ * @default false
+ */
+ includeReply?: boolean;
+ }
+
+ export interface TwikooRecentCommentsResult {
+ /** 评论 ID */
+ id: string;
+ /** 评论地址 */
+ url: string;
+ /** 昵称 */
+ nick: string;
+ /** 邮箱的 MD5 值,可用于展示头像 */
+ mailMd5: string;
+ /** 网址 */
+ link: string;
+ /** HTML 格式的评论内容 */
+ comment: string;
+ /** 纯文本格式的评论内容 */
+ commentText: string;
+ /** 评论时间,格式为毫秒级时间戳 */
+ created: number;
+ /**
+ * 头像地址
+ *
+ * @version >= 0.2.9
+ */
+ avatar: string;
+ /**
+ * 相对评论时间,如 “1 小时前”
+ *
+ * @version >= 0.2.9
+ */
+ relativeTime: string;
+ }
+
+ export const version: string;
+ export const init: (options: TwikooInitOptions) => Promise;
+ export const getCommentsCount: (
+ options: TwikooCommentCountOptions
+ ) => Promise;
+ export const getRecentComments: (
+ options: TwikooRecentCommentsOptions
+ ) => Promise;
+
+ export default init;
+}
+
declare module "valine" {
export interface ValineOption {
/**
diff --git a/packages/comment/src/types/frontmatter.d.ts b/packages/comment/src/types/frontmatter.d.ts
new file mode 100644
index 000000000..85a058f21
--- /dev/null
+++ b/packages/comment/src/types/frontmatter.d.ts
@@ -0,0 +1,23 @@
+import { BasePageFrontMatter } from "vuepress-shared";
+
+export interface CommentPluginFrontmatter extends BasePageFrontMatter {
+ /**
+ * 是否启用评论
+ *
+ * Whether Enable Comment
+ *
+ * @default true
+ */
+ comment?: boolean;
+
+ /**
+ * @description Only available when using valine
+ *
+ * 是否启用访问量
+ *
+ * Whether enable pageviews
+ *
+ * @default true
+ */
+ pageview?: boolean;
+}
diff --git a/packages/comment/src/types/index.d.ts b/packages/comment/src/types/index.d.ts
index c9cf98390..eaf479c7a 100644
--- a/packages/comment/src/types/index.d.ts
+++ b/packages/comment/src/types/index.d.ts
@@ -1,13 +1,11 @@
import type { CommentOptions } from "./options";
-import type { WalineLocaleConfig } from "./waline";
-import type { ValineLocaleConfig } from "./valine";
+import type { WalineLocaleConfig } from "./options/waline";
+import type { ValineLocaleConfig } from "./options/valine";
import "./declare";
+export * from "./frontmatter";
export * from "./options";
-export * from "./waline";
-export * from "./valine";
-export * from "./vssue";
declare global {
const COMMENT_OPTIONS: CommentOptions;
diff --git a/packages/comment/src/types/base.d.ts b/packages/comment/src/types/options/base.d.ts
similarity index 90%
rename from packages/comment/src/types/base.d.ts
rename to packages/comment/src/types/options/base.d.ts
index 333ab93df..e1150eb51 100644
--- a/packages/comment/src/types/base.d.ts
+++ b/packages/comment/src/types/options/base.d.ts
@@ -1,10 +1,12 @@
+import type { Author } from "vuepress-shared";
+
export interface BaseCommentOptions {
/**
- * 默认作者
- *
* Default author
+ *
+ * 默认作者
*/
- author?: string;
+ author?: Author;
/**
* Whether enable comment by default
diff --git a/packages/comment/src/types/disable.d.ts b/packages/comment/src/types/options/disable.d.ts
similarity index 75%
rename from packages/comment/src/types/disable.d.ts
rename to packages/comment/src/types/options/disable.d.ts
index 2db1e23f8..9b1f4e8f3 100644
--- a/packages/comment/src/types/disable.d.ts
+++ b/packages/comment/src/types/options/disable.d.ts
@@ -1,5 +1,6 @@
import type { BaseCommentOptions } from "./base";
export interface DisableCommentOptions extends BaseCommentOptions {
- type: "disable";
+ provider?: "None";
+ comment?: never;
}
diff --git a/packages/comment/src/types/options/giscus.d.ts b/packages/comment/src/types/options/giscus.d.ts
new file mode 100644
index 000000000..3883ab289
--- /dev/null
+++ b/packages/comment/src/types/options/giscus.d.ts
@@ -0,0 +1,60 @@
+import type { BaseCommentOptions } from "./base";
+
+export interface GiscusOptions extends BaseCommentOptions {
+ provider: "Giscus";
+
+ /**
+ * The name of repository to store discussions.
+ *
+ * 存放评论的仓库
+ */
+ repo: string;
+
+ /**
+ * The ID of repository to store discussions.
+ *
+ * 仓库 ID
+ */
+ repoId: string;
+
+ /**
+ * The name of the discussion category.
+ *
+ * 讨论分类
+ */
+ category: string;
+
+ /**
+ * The ID of the discussion category.
+ *
+ * 分类 ID
+ */
+ categoryId: string;
+
+ /**
+ * Page - discussion mapping.
+ *
+ * 页面 ↔️ discussion 映射关系
+ *
+ * @default "pathname"
+ */
+ mapping?: string;
+
+ /**
+ * Whether enable reactions or not
+ *
+ * 是否启用主帖子上的反应
+ *
+ * @default true
+ */
+ reactionsEnabled?: boolean;
+
+ /**
+ * Input position
+ *
+ * 输入框的位置
+ *
+ * @default 'top'
+ */
+ inputPosition?: "top" | "bottom";
+}
diff --git a/packages/comment/src/types/options/index.d.ts b/packages/comment/src/types/options/index.d.ts
new file mode 100644
index 000000000..b8e01c04a
--- /dev/null
+++ b/packages/comment/src/types/options/index.d.ts
@@ -0,0 +1,6 @@
+export * from "./giscus";
+export * from "./options";
+export * from "./twikoo";
+export * from "./valine";
+export * from "./vssue";
+export * from "./waline";
diff --git a/packages/comment/src/types/options.d.ts b/packages/comment/src/types/options/options.d.ts
similarity index 72%
rename from packages/comment/src/types/options.d.ts
rename to packages/comment/src/types/options/options.d.ts
index 1134558cf..d84b29c62 100644
--- a/packages/comment/src/types/options.d.ts
+++ b/packages/comment/src/types/options/options.d.ts
@@ -1,7 +1,9 @@
import type { DisableCommentOptions } from "./disable";
+import type { GiscusOptions } from "./giscus";
+import type { TwikooOptions } from "./twikoo";
+import type { WalineOptions } from "./waline";
import type { ValineOptions } from "./valine";
import type { VssueOptions } from "./vssue";
-import type { WalineOptions } from "./waline";
/**
* 评论选项
@@ -9,7 +11,9 @@ import type { WalineOptions } from "./waline";
* Comment options
*/
export type CommentOptions =
- | ValineOptions
+ | GiscusOptions
+ | TwikooOptions
| WalineOptions
+ | ValineOptions
| VssueOptions
| DisableCommentOptions;
diff --git a/packages/comment/src/types/options/twikoo.d.ts b/packages/comment/src/types/options/twikoo.d.ts
new file mode 100644
index 000000000..84c3430d5
--- /dev/null
+++ b/packages/comment/src/types/options/twikoo.d.ts
@@ -0,0 +1,23 @@
+import type { BaseCommentOptions } from "./base";
+
+export interface TwikooInitOptions {
+ /**
+ * Environment ID for tencloud or Link for Vercel
+ *
+ * 腾讯云环境链接或 Vercel Link
+ */
+ envId: string;
+
+ /**
+ * Tencloud region
+ *
+ * 腾讯云区域
+ *
+ * @default 'ap-shanghai'
+ */
+ region?: string;
+}
+
+export interface TwikooOptions extends BaseCommentOptions, TwikooInitOptions {
+ provider: "Twikoo";
+}
diff --git a/packages/comment/src/types/valine.d.ts b/packages/comment/src/types/options/valine.d.ts
similarity index 95%
rename from packages/comment/src/types/valine.d.ts
rename to packages/comment/src/types/options/valine.d.ts
index e025f6a89..b2f5beb5a 100644
--- a/packages/comment/src/types/valine.d.ts
+++ b/packages/comment/src/types/options/valine.d.ts
@@ -11,6 +11,6 @@ export type ValineLocaleConfig = ConvertLocaleConfig;
export interface ValineOptions
extends BaseCommentOptions,
Omit {
- type: "valine";
+ provider: "Valine";
valineLocales?: LocaleConfig;
}
diff --git a/packages/comment/src/types/vssue.d.ts b/packages/comment/src/types/options/vssue.d.ts
similarity index 99%
rename from packages/comment/src/types/vssue.d.ts
rename to packages/comment/src/types/options/vssue.d.ts
index 0fd6a78c1..ec9fcd281 100644
--- a/packages/comment/src/types/vssue.d.ts
+++ b/packages/comment/src/types/options/vssue.d.ts
@@ -3,7 +3,7 @@ import type { BaseCommentOptions } from "./base";
/** Vssue 配置 */
export interface VssueOptions extends BaseCommentOptions {
- type: "vssue";
+ provider: "Vssue";
/**
* 平台 API 包
diff --git a/packages/comment/src/types/waline.d.ts b/packages/comment/src/types/options/waline.d.ts
similarity index 65%
rename from packages/comment/src/types/waline.d.ts
rename to packages/comment/src/types/options/waline.d.ts
index 9df140853..1b881a4c7 100644
--- a/packages/comment/src/types/waline.d.ts
+++ b/packages/comment/src/types/options/waline.d.ts
@@ -8,11 +8,20 @@ export type WalineLocaleConfig = ConvertLocaleConfig;
export interface WalineOptions
extends BaseCommentOptions,
- Omit {
- type: "waline";
+ Omit {
+ provider: "Waline";
/**
- * Locales config for waline
+ * 是否启用访问量
+ *
+ * Whether enable page views count by default
+ *
+ * @default true
+ */
+ pageview?: boolean;
+
+ /**
+ * Locale config for waline
*/
walineLocales?: LocaleConfig;
}
diff --git a/packages/theme/node/alias.ts b/packages/theme/node/alias.ts
index a7186f342..354459340 100644
--- a/packages/theme/node/alias.ts
+++ b/packages/theme/node/alias.ts
@@ -19,8 +19,8 @@ export const getAlias = (
const blogEnabled = themeConfig.blog !== false;
const commentEnabled =
themeConfig.comment &&
- themeConfig.comment.type &&
- themeConfig.comment.type !== "disable";
+ themeConfig.comment.provider &&
+ themeConfig.comment.provider !== "None";
const themeColorEnabled = !(
themeConfig.themeColor === false && themeConfig.darkmode === "disable"
diff --git a/packages/theme/node/plugins.ts b/packages/theme/node/plugins.ts
index 3bb544364..4cba102c1 100644
--- a/packages/theme/node/plugins.ts
+++ b/packages/theme/node/plugins.ts
@@ -31,7 +31,7 @@ const resolveCommentOptions = (
return themeConfig.comment === false
? false
: {
- type: "disable",
+ provider: "None",
...(themeConfig.comment || null),
};
};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 46b42513e..7e9f33bf8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -290,6 +290,8 @@ importers:
'@vssue/api-gitlab-v4': ^1.4.7
'@vssue/vuepress-plugin-vssue': ^1.4.8
'@waline/client': ^2.5.1
+ giscus: ^1.0.5
+ twikoo: ^1.5.11
valine: ^1.4.18
vssue: ^1.4.8
vuepress-shared: workspace:*
@@ -305,6 +307,8 @@ importers:
'@vssue/api-gitlab-v4': 1.4.7
'@vssue/vuepress-plugin-vssue': 1.4.8
'@waline/client': 2.5.1
+ giscus: 1.0.5
+ twikoo: 1.5.11
valine: 1.4.18
vssue: 1.4.8
vuepress-shared: link:../shared
@@ -2177,6 +2181,10 @@ packages:
miniprogram-api-typings: 2.12.0
dev: false
+ /@lit/reactive-element/1.3.2:
+ resolution: {integrity: sha512-A2e18XzPMrIh35nhIdE4uoqRzoIpEU5vZYuQN4S3Ee1zkGdYC27DP12pewbw/RLgPHzaE4kx/YqxMzebOpm0dA==}
+ dev: false
+
/@mrmlnc/readdir-enhanced/2.2.1:
resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==}
engines: {node: '>=4'}
@@ -8470,6 +8478,12 @@ packages:
dependencies:
assert-plus: 1.0.0
+ /giscus/1.0.5:
+ resolution: {integrity: sha512-jg+4qUtyn2r9gCZJthbgPn/hLaoZAcZVhWx6d7+TYrrojMjAltrlSzYdsL++4iutVmGEYktp+nUJn2rQgZzvTg==}
+ dependencies:
+ lit: 2.2.5
+ dev: false
+
/git-hooks-list/1.0.3:
resolution: {integrity: sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==}
dev: true
@@ -10163,6 +10177,27 @@ packages:
wrap-ansi: 7.0.0
dev: true
+ /lit-element/3.2.0:
+ resolution: {integrity: sha512-HbE7yt2SnUtg5DCrWt028oaU4D5F4k/1cntAFHTkzY8ZIa8N0Wmu92PxSxucsQSOXlODFrICkQ5x/tEshKi13g==}
+ dependencies:
+ '@lit/reactive-element': 1.3.2
+ lit-html: 2.2.5
+ dev: false
+
+ /lit-html/2.2.5:
+ resolution: {integrity: sha512-e56Y9V+RNA+SGYsWP2DGb/wad5Ccd3xUZYjmcmbeZcnc0wP4zFQRXeXn7W3bbfBekmHDK2dOnuYNYkg0bQjh/w==}
+ dependencies:
+ '@types/trusted-types': 2.0.2
+ dev: false
+
+ /lit/2.2.5:
+ resolution: {integrity: sha512-Ln463c0xJZfzVxBcHddNvFQQ8Z22NK7KgNmrzwFF1iESHUud412RRExzepj18wpTbusgwoTnOYuoTpo9uyNBaQ==}
+ dependencies:
+ '@lit/reactive-element': 1.3.2
+ lit-element: 3.2.0
+ lit-html: 2.2.5
+ dev: false
+
/load-json-file/4.0.0:
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
engines: {node: '>=4'}
@@ -14382,6 +14417,10 @@ packages:
/tweetnacl/0.14.5:
resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=}
+ /twikoo/1.5.11:
+ resolution: {integrity: sha512-TABJvFIucfcI/auXFji/u8zMuCKqRiwhkYN0bQMO5S9bT9bDcvN1rviZ2E+4wdbivBa5Evgj3ABZY7sjNt+sVQ==}
+ dev: false
+
/type-check/0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}