├── pnpm-workspace.yaml
├── README.md
├── packages
└── app
│ ├── src
│ ├── scss
│ │ └── style.scss
│ ├── plugins
│ │ ├── index.ts
│ │ └── vuetify.ts
│ ├── main.ts
│ ├── theme.ts
│ ├── generators
│ │ ├── index.ts
│ │ ├── helper.ts
│ │ ├── python_dict.ts
│ │ ├── nonebot_store.ts
│ │ ├── nonebot_scheduler.ts
│ │ ├── nonebot_request.ts
│ │ ├── nonebot_basic.ts
│ │ └── nonebot_alconna.ts
│ ├── components
│ │ ├── CodeTab.vue
│ │ ├── BlocklyTab.vue
│ │ ├── TutorialTab.vue
│ │ ├── ContentCard.vue
│ │ ├── ButtonPanel.vue
│ │ └── ConfigTab.vue
│ ├── blocks
│ │ ├── index.ts
│ │ ├── fields
│ │ │ ├── alconna_helper.ts
│ │ │ ├── serialization_helper.ts
│ │ │ ├── field_minus.ts
│ │ │ └── field_plus.ts
│ │ ├── nonebot_store.ts
│ │ ├── nonebot_basic.ts
│ │ ├── nonebot_request.ts
│ │ ├── nonebot_scheduler.ts
│ │ ├── nonebot_alconna.ts
│ │ └── python_dict.ts
│ ├── App.vue
│ ├── workspace.ts
│ ├── toolbox.ts
│ └── default.ts
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── index.html
│ ├── package.json
│ └── vite.config.mjs
├── .github
├── actions
│ └── setup-node
│ │ └── action.yml
└── workflows
│ ├── website-preview-ci.yml
│ ├── website-deploy.yml
│ └── website-preview-cd.yml
├── package.json
├── LICENSE
├── .gitignore
└── pnpm-lock.yaml
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/*"
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # noneblockly
2 |
3 | Craft NoneBot plugins with ease: Embrace low-code development through Blockly.
4 |
--------------------------------------------------------------------------------
/packages/app/src/scss/style.scss:
--------------------------------------------------------------------------------
1 | @use "sass:meta";
2 |
3 | html[hljs-theme-dark="true"] {
4 | @include meta.load-css("highlight.js/scss/github-dark");
5 | }
6 |
7 | html:not([hljs-theme-dark="true"]) {
8 | @include meta.load-css("highlight.js/scss/github");
9 | }
10 |
--------------------------------------------------------------------------------
/packages/app/src/plugins/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * plugins/index.ts
3 | *
4 | * Automatically included in `./src/main.ts`
5 | */
6 |
7 | // Plugins
8 | import vuetify from "./vuetify";
9 |
10 | // Types
11 | import type { App } from "vue";
12 |
13 | export function registerPlugins(app: App) {
14 | app.use(vuetify);
15 | }
16 |
--------------------------------------------------------------------------------
/.github/actions/setup-node/action.yml:
--------------------------------------------------------------------------------
1 | name: Setup Node
2 | description: Setup Node
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - uses: pnpm/action-setup@v4
8 |
9 | - uses: actions/setup-node@v4
10 | with:
11 | node-version: "20"
12 | cache: "pnpm"
13 |
14 | - run: pnpm install
15 | shell: bash
16 |
--------------------------------------------------------------------------------
/packages/app/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Dependency directories
9 | node_modules/
10 |
11 | # Generated files
12 | dist/
13 | build/
14 | .DS_Store
15 |
16 | # Optional npm cache directory
17 | .npm
18 |
19 | # IDEs and editors
20 | .idea
21 | *.sublime-workspace
22 | .vscode/*
23 |
--------------------------------------------------------------------------------
/packages/app/src/main.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * main.ts
3 | */
4 |
5 | // Components
6 | import App from "./App.vue";
7 | // Composables
8 | import { createApp } from "vue";
9 | // Plugins
10 | import { registerPlugins } from "@/plugins";
11 | // Styles
12 | import "@/scss/style.scss";
13 |
14 | const app = createApp(App);
15 | registerPlugins(app);
16 | app.mount("#app");
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@noneblockly/workspace-root",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "preinstall": "npx only-allow pnpm",
7 | "app": "pnpm --filter app",
8 | "format": "prettier --write ."
9 | },
10 | "license": "MIT",
11 | "engines": {
12 | "node": ">=18.0.0"
13 | },
14 | "packageManager": "pnpm@9.4.0",
15 | "devDependencies": {
16 | "prettier": "^3.3.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/app/src/theme.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2022 ICILS
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | import * as Blockly from "blockly/core";
8 | // @ts-ignore
9 | import ThemeDark from "@blockly/theme-dark";
10 |
11 | export const themeLight = Blockly.Theme.defineTheme("light", {
12 | base: Blockly.Themes.Classic,
13 | name: "Light",
14 | });
15 |
16 | export const themeDark = Blockly.Theme.defineTheme("dark", {
17 | base: ThemeDark,
18 | name: "Dark",
19 | });
20 |
--------------------------------------------------------------------------------
/packages/app/src/generators/index.ts:
--------------------------------------------------------------------------------
1 | import { forBlock as pythonDict } from "./python_dict";
2 | import { forBlock as nonebotBasic } from "./nonebot_basic";
3 | import { forBlock as nonebotAlconna } from "./nonebot_alconna";
4 | import { forBlock as nonebotStore } from "./nonebot_store";
5 | import { forBlock as nonebotScheduler } from "./nonebot_scheduler";
6 | import { forBlock as nonebotRequest } from "./nonebot_request";
7 |
8 | export const generators = [
9 | pythonDict,
10 | nonebotBasic,
11 | nonebotAlconna,
12 | nonebotStore,
13 | nonebotScheduler,
14 | nonebotRequest,
15 | ];
16 |
--------------------------------------------------------------------------------
/packages/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "outDir": "dist",
5 | "declaration": true,
6 | "declarationMap": true,
7 | "module": "ES2015",
8 | "moduleResolution": "node",
9 | "resolveJsonModule": true,
10 | "target": "ES2015",
11 | "strict": true,
12 | // avoid jszip error
13 | "allowSyntheticDefaultImports": true,
14 | "paths": {
15 | "@/*": ["./src/*"]
16 | }
17 | },
18 | "include": [
19 | "./src/**/*.ts",
20 | "./src/**/*.d.ts",
21 | "./src/**/*.tsx",
22 | "./src/**/*.vue",
23 | "./src/**/*.vue.ts",
24 | "./src/**/*.json"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/packages/app/src/components/CodeTab.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
22 |
23 |
32 |
--------------------------------------------------------------------------------
/packages/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | NoneBlockly for NoneBot2
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
29 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/index.ts:
--------------------------------------------------------------------------------
1 | import * as Blockly from "blockly/core";
2 | import { BlockDefinition } from "blockly/core/blocks";
3 |
4 | import { pythonDict } from "./python_dict";
5 | import { definitions as nonebotBasic } from "./nonebot_basic";
6 | import { definitions as nonebotAlconna } from "./nonebot_alconna";
7 | import { definitions as nonebotStore } from "./nonebot_store";
8 | import { definitions as nonebotScheduler } from "./nonebot_scheduler";
9 | import { definitions as nonebotRequest } from "./nonebot_request";
10 |
11 | // Array of all block definitions
12 | let blockDefinitions: BlockDefinition[] = [];
13 | blockDefinitions = blockDefinitions
14 | .concat(pythonDict)
15 | .concat(nonebotBasic)
16 | .concat(nonebotAlconna)
17 | .concat(nonebotStore)
18 | .concat(nonebotScheduler)
19 | .concat(nonebotRequest);
20 |
21 | export const blocks =
22 | Blockly.common.createBlockDefinitionsFromJsonArray(blockDefinitions);
23 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/fields/alconna_helper.ts:
--------------------------------------------------------------------------------
1 | import * as Blockly from "blockly/core";
2 |
3 | export function getAlconnaArg(block: Blockly.Block): string[] {
4 | let args: string[] = [];
5 | // get top block
6 | let parent = block.getParent();
7 | if (parent == null) {
8 | return [];
9 | }
10 | while (parent.type != "nonebot_on_alconna") {
11 | parent = parent.getParent();
12 | if (parent == null) {
13 | return [];
14 | }
15 | }
16 | // get all arg blocks of alconna top block
17 | for (let n = 0; n < (parent as any).itemCount_; n++) {
18 | const arg_block = parent
19 | .getInput("ARG" + String(n))
20 | ?.connection?.targetConnection?.getSourceBlock();
21 | const arg_type = arg_block?.type;
22 | if (arg_type === "alconna_arg") {
23 | const arg_name = arg_block?.getFieldValue("NAME");
24 | if (arg_name) {
25 | args.push(arg_name);
26 | }
27 | }
28 | }
29 | return args;
30 | }
31 |
--------------------------------------------------------------------------------
/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@noneblockly/app",
3 | "version": "0.1.0",
4 | "main": "main.ts",
5 | "private": true,
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "keywords": [
12 | "blockly"
13 | ],
14 | "author": "",
15 | "license": "MIT",
16 | "devDependencies": {
17 | "@blockly/theme-dark": "^7.0.1",
18 | "@highlightjs/vue-plugin": "^2.1.0",
19 | "@mdi/js": "^7.4.47",
20 | "@types/file-saver": "^2.0.7",
21 | "@vitejs/plugin-vue": "^5.0.5",
22 | "typescript": "^5.5.2",
23 | "vite": "^5.3.1",
24 | "vite-plugin-static-copy": "^1.0.5",
25 | "vite-plugin-vuetify": "^2.0.3",
26 | "vue-tsc": "^2.0.21"
27 | },
28 | "dependencies": {
29 | "blockly": "^11.1.1",
30 | "file-saver": "^2.0.5",
31 | "highlight.js": "^11.9.0",
32 | "jszip": "^3.10.1",
33 | "sass": "^1.77.6",
34 | "vue": "^3.4.29",
35 | "vuetify": "^3.6.10"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/.github/workflows/website-preview-ci.yml:
--------------------------------------------------------------------------------
1 | name: Site Deploy (Preview CI)
2 |
3 | on:
4 | pull_request:
5 |
6 | jobs:
7 | preview-ci:
8 | runs-on: ubuntu-latest
9 | concurrency:
10 | group: pull-request-preview-${{ github.event.number }}
11 | cancel-in-progress: true
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | ref: ${{ github.event.pull_request.head.sha }}
17 | fetch-depth: 0
18 |
19 | - name: Setup Node Environment
20 | uses: ./.github/actions/setup-node
21 |
22 | - name: Build Website
23 | run: pnpm app build
24 |
25 | - name: Export Context
26 | run: |
27 | echo "PR_NUMBER=${{ github.event.number }}" >> ./action.env
28 |
29 | - name: Upload Artifact
30 | uses: actions/upload-artifact@v4
31 | with:
32 | name: website-preview
33 | path: |
34 | ./packages/app/dist
35 | ./action.env
36 | retention-days: 1
37 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/fields/serialization_helper.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2022 Google LLC
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | import * as Blockly from "blockly/core";
8 |
9 | /**
10 | * Returns the extra state of the given block (either as XML or a JSO, depending
11 | * on the block's definition).
12 | * @param {!Blockly.BlockSvg} block The block to get the extra state of.
13 | * @returns {string} A stringified version of the extra state of the given
14 | * block.
15 | */
16 | export function getExtraBlockState(block: Blockly.BlockSvg): string {
17 | // TODO: This is a dupe of the BlockChange.getExtraBlockState code, do we
18 | // want to make that public?
19 | if (block.saveExtraState) {
20 | const state = block.saveExtraState();
21 | return state ? JSON.stringify(state) : "";
22 | } else if (block.mutationToDom) {
23 | const state = block.mutationToDom();
24 | return state ? Blockly.Xml.domToText(state) : "";
25 | }
26 | return "";
27 | }
28 |
--------------------------------------------------------------------------------
/packages/app/src/generators/helper.ts:
--------------------------------------------------------------------------------
1 | import { PythonGenerator } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | export function getGlobalStatement(
5 | block: Blockly.Block,
6 | generator: PythonGenerator,
7 | ) {
8 | const globals = [];
9 | const workspace = block.workspace;
10 | const usedVariables = Blockly.Variables.allUsedVarModels(workspace) || [];
11 | for (const variable of usedVariables) {
12 | const varName = variable.name;
13 | globals.push(generator.getVariableName(varName));
14 | }
15 | // Add developer variables.
16 | const devVarList = Blockly.Variables.allDeveloperVariables(workspace);
17 | for (let i = 0; i < devVarList.length; i++) {
18 | globals.push(
19 | generator.nameDB_!.getName(
20 | devVarList[i],
21 | Blockly.Names.NameType.DEVELOPER_VARIABLE,
22 | ),
23 | );
24 | }
25 | const globalString = globals.length
26 | ? "global " + globals.join(", ") + "\n"
27 | : "";
28 | return globalString;
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 NoneBot
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/app/src/components/BlocklyTab.vue:
--------------------------------------------------------------------------------
1 |
31 |
32 |
33 |
39 |
40 |
41 |
48 |
--------------------------------------------------------------------------------
/.github/workflows/website-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Site Deploy
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 | concurrency:
12 | group: website-deploy-${{ github.ref }}
13 | cancel-in-progress: true
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Setup Node Environment
21 | uses: ./.github/actions/setup-node
22 |
23 | - name: Build Website
24 | run: pnpm app build
25 |
26 | - name: Get Branch Name
27 | run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
28 |
29 | - name: Deploy to Netlify
30 | uses: nwtgck/actions-netlify@v3
31 | with:
32 | publish-dir: "./packages/app/dist"
33 | production-deploy: true
34 | github-token: ${{ secrets.GITHUB_TOKEN }}
35 | deploy-message: "Deploy ${{ env.BRANCH_NAME }}@${{ github.sha }}"
36 | enable-commit-comment: false
37 | alias: ${{ env.BRANCH_NAME }}
38 | env:
39 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
40 | NETLIFY_SITE_ID: ${{ secrets.SITE_ID }}
41 |
--------------------------------------------------------------------------------
/packages/app/src/components/TutorialTab.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | 使用方法
7 |
8 |
9 | 点击编程标签页左侧的工具箱标签,在对应工具箱中拖出块,在工作区中进行拼接完成图形化编程。
10 |
11 |
12 |
13 |
14 | 点此按钮加载示例
15 |
16 | (会覆盖当前工作区)
17 |
18 | 暂存与回复
19 |
20 |
21 | 点击下方工具栏的“暂存”按钮可将当前设计暂存到浏览器中,点击“恢复”按钮可恢复上次暂存的设计。
22 |
23 |
24 | 已存在暂存的设计时,刷新页面或重新打开页面时会自动恢复上次暂存的设计。
25 |
26 |
27 | 配置与导入导出
28 |
29 |
30 | 点击下方工具栏的“导入导出”按钮或点击“配置”标签,可进行参数配置、设计文件的导入导出、以及
31 | NoneBot 工程的导出操作。
32 |
33 |
34 | 导出设计文件将生成一个 JSON 文件并下载到本地;导入设计文件需要选择已导出的
35 | JSON 文件上传。
36 |
37 |
38 | 导出 NoneBot
39 | 工程将生成一个压缩文件并下载到本地,使用时需要先进行解压,在项目根目录运行对应平台的脚本
40 | ( Windows 平台为./install.ps1 和 ./run.ps1;
41 | Linux 平台为 bash ./install.sh 和
42 | bash ./run.sh
43 | )进行环境配置与运行。
44 |
45 |
46 |
47 |
48 | 详细使用文档正在编写中。
49 |
50 |
51 |
--------------------------------------------------------------------------------
/packages/app/src/components/ContentCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 | 编程
11 | 教程
12 | 代码
13 | 配置
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
61 |
62 |
65 |
--------------------------------------------------------------------------------
/packages/app/vite.config.mjs:
--------------------------------------------------------------------------------
1 | // Plugins
2 | import vue from "@vitejs/plugin-vue";
3 | import vuetify from "vite-plugin-vuetify";
4 |
5 | // Utilities
6 | import { defineConfig } from "vite";
7 | import { fileURLToPath, URL } from "node:url";
8 |
9 | // Vite
10 | import { viteStaticCopy } from "vite-plugin-static-copy";
11 |
12 | // https://vitejs.dev/config/
13 | export default defineConfig({
14 | // base: "/",
15 | // ssr: {
16 | // noExternal: ["@blockly/blockly-component"],
17 | // },
18 | build: {
19 | chunkSizeWarningLimit: 700,
20 | rollupOptions: {
21 | output: {
22 | manualChunks: {
23 | "blockly/blockly": ["blockly"],
24 | "vuetify/vuetify": ["vuetify"],
25 | },
26 | },
27 | },
28 | },
29 | plugins: [
30 | vue({
31 | template: {
32 | compilerOptions: {
33 | isCustomElement: (tag) =>
34 | [
35 | "field",
36 | "block",
37 | "category",
38 | "xml",
39 | "mutation",
40 | "value",
41 | "sep",
42 | "shadow",
43 | ].includes(tag),
44 | },
45 | },
46 | }),
47 | // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
48 | vuetify({
49 | autoImport: true,
50 | }),
51 | // Blockly vite plugin
52 | viteStaticCopy({
53 | targets: [
54 | {
55 | src: fileURLToPath(
56 | new URL("./node_modules/blockly/media/*", import.meta.url),
57 | ),
58 | dest: "media",
59 | },
60 | ],
61 | }),
62 | ],
63 | define: { "process.env": {} },
64 | resolve: {
65 | alias: {
66 | "@": fileURLToPath(new URL("./src", import.meta.url)),
67 | },
68 | extensions: [".js", ".json", ".jsx", ".mjs", ".ts", ".tsx", ".vue"],
69 | },
70 | server: {
71 | // port: process.env.PORT || 8080
72 | },
73 | });
74 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/nonebot_store.ts:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from "blockly/core/blocks";
2 |
3 | export const definitions: BlockDefinition[] = [
4 | {
5 | type: "store_save_json",
6 | tooltip: "",
7 | helpUrl: "",
8 | message0: "保存字典 %1 到文件 %2 %3",
9 | args0: [
10 | {
11 | type: "input_value",
12 | name: "DICT",
13 | check: "dict",
14 | },
15 | {
16 | type: "field_input",
17 | name: "FILE",
18 | text: "save.json",
19 | },
20 | {
21 | type: "input_dummy",
22 | name: "PARAMS",
23 | },
24 | ],
25 | previousStatement: null,
26 | nextStatement: null,
27 | colour: 150,
28 | inputsInline: true,
29 | },
30 | {
31 | type: "store_load_json",
32 | tooltip: "",
33 | helpUrl: "",
34 | message0: "读取文件 %1 %2 的字典",
35 | args0: [
36 | {
37 | type: "field_input",
38 | name: "FILE",
39 | text: "save.json",
40 | },
41 | {
42 | type: "input_dummy",
43 | name: "PARAMS",
44 | },
45 | ],
46 | output: "DICT",
47 | colour: 150,
48 | inputsInline: true,
49 | },
50 | {
51 | type: "store_save_text",
52 | tooltip: "",
53 | helpUrl: "",
54 | message0: "保存文本 %1 到文件 %2 %3",
55 | args0: [
56 | {
57 | type: "input_value",
58 | name: "TEXT",
59 | check: "String",
60 | },
61 | {
62 | type: "field_input",
63 | name: "FILE",
64 | text: "save.txt",
65 | },
66 | {
67 | type: "input_dummy",
68 | name: "PARAMS",
69 | },
70 | ],
71 | previousStatement: null,
72 | nextStatement: null,
73 | colour: 180,
74 | inputsInline: true,
75 | },
76 | {
77 | type: "store_load_text",
78 | tooltip: "",
79 | helpUrl: "",
80 | message0: "读取文件 %1 %2 的文本",
81 | args0: [
82 | {
83 | type: "field_input",
84 | name: "FILE",
85 | text: "save.txt",
86 | },
87 | {
88 | type: "input_dummy",
89 | name: "PARAMS",
90 | },
91 | ],
92 | output: "String",
93 | colour: 180,
94 | inputsInline: true,
95 | },
96 | ];
97 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/fields/field_minus.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2020 Google LLC
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | /**
8 | * @fileoverview A function that creates a minus button used for mutation.
9 | */
10 |
11 | import * as Blockly from "blockly/core";
12 | import { getExtraBlockState } from "./serialization_helper";
13 |
14 | /**
15 | * Creates a minus image field used for mutation.
16 | * @param {Object=} args Untyped args passed to block.minus when the field
17 | * is clicked.
18 | * @returns {Blockly.FieldImage} The minus field.
19 | */
20 | export function createMinusField(args?: Object): Blockly.FieldImage {
21 | const minus = new Blockly.FieldImage(minusImage, 15, 15, undefined, onClick_);
22 | /**
23 | * Untyped args passed to block.minus when the field is clicked.
24 | * @type {?(Object|undefined)}
25 | * @private
26 | */
27 | (minus as any).args_ = args;
28 | return minus;
29 | }
30 |
31 | /**
32 | * Calls block.minus(args) when the minus field is clicked.
33 | * @param {Blockly.FieldImage} minusField The field being clicked.
34 | * @private
35 | */
36 | function onClick_(minusField: Blockly.FieldImage) {
37 | // TODO: This is a dupe of the mutator code, anyway to unify?
38 | const block = minusField.getSourceBlock() as Blockly.BlockSvg;
39 |
40 | if (block.isInFlyout) {
41 | return;
42 | }
43 |
44 | Blockly.Events.setGroup(true);
45 | const oldExtraState = getExtraBlockState(block);
46 | (block as any).minus((minusField as any).args_);
47 | const newExtraState = getExtraBlockState(block);
48 |
49 | if (oldExtraState != newExtraState) {
50 | Blockly.Events.fire(
51 | new Blockly.Events.BlockChange(
52 | block,
53 | "mutation",
54 | null,
55 | oldExtraState,
56 | newExtraState,
57 | ),
58 | );
59 | }
60 | Blockly.Events.setGroup(false);
61 | }
62 |
63 | const minusImage =
64 | "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAw" +
65 | "MC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cGF0aCBkPS" +
66 | "JNMTggMTFoLTEyYy0xLjEwNCAwLTIgLjg5Ni0yIDJzLjg5NiAyIDIgMmgxMmMxLjEwNCAw" +
67 | "IDItLjg5NiAyLTJzLS44OTYtMi0yLTJ6IiBmaWxsPSJ3aGl0ZSIgLz48L3N2Zz4K";
68 |
--------------------------------------------------------------------------------
/packages/app/src/generators/python_dict.ts:
--------------------------------------------------------------------------------
1 | import { Order } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | import { DictCreateWithBlock, DictGetMultiBlock } from "@/blocks/python_dict";
5 |
6 | export const forBlock = Object.create(null);
7 |
8 | forBlock["dicts_get"] = function (
9 | block: Blockly.Block,
10 | generator: Blockly.CodeGenerator,
11 | ) {
12 | const dict = generator.valueToCode(block, "DICT", Order.MEMBER) || "{}";
13 | const key = block.getFieldValue("KEY");
14 | if (!key) {
15 | return ["None", Order.ATOMIC];
16 | }
17 | const code = `${dict}.get("${key}")`;
18 | return [code, Order.ATOMIC];
19 | };
20 |
21 | forBlock["dicts_get_multi"] = function (
22 | block: DictGetMultiBlock,
23 | generator: Blockly.CodeGenerator,
24 | ) {
25 | const dict = generator.valueToCode(block, "DICT", Order.MEMBER) || "{}";
26 | let code = "";
27 | let key = block.getFieldValue("KEY0");
28 | if (!key) {
29 | return ["None", Order.ATOMIC];
30 | }
31 | code = `${dict}.get("${key}")`;
32 | for (let n = 1; n < block.itemCount_; n++) {
33 | key = block.getFieldValue("KEY" + n);
34 | if (key) {
35 | code = `(${code} or {}).get(${key})`;
36 | }
37 | }
38 | return [code, Order.ATOMIC];
39 | };
40 |
41 | forBlock["dicts_set"] = function (
42 | block: Blockly.Block,
43 | generator: Blockly.CodeGenerator,
44 | ) {
45 | const dict = generator.valueToCode(block, "DICT", Order.MEMBER) || "{}";
46 | const key = block.getFieldValue("KEY");
47 | if (!key) {
48 | return ["None", Order.ATOMIC];
49 | }
50 | const value = generator.valueToCode(block, "VALUE", Order.NONE) || "None";
51 | const code = `${dict}["${key}"] = ${value}\n`;
52 | return code;
53 | };
54 |
55 | forBlock["dicts_create_with"] = function (
56 | block: DictCreateWithBlock,
57 | generator: Blockly.CodeGenerator,
58 | ) {
59 | let items = new Array();
60 | for (let n = 0; n < block.itemCount_; n++) {
61 | let key = generator.valueToCode(block, "KEY" + n, Order.NONE);
62 | let value = generator.valueToCode(block, "VALUE" + n, Order.NONE) || "None";
63 | if (key) {
64 | items.push(`${key}: ${value}`);
65 | }
66 | }
67 | const code = "{" + items.join(", ") + "}";
68 | return [code, Order.ATOMIC];
69 | };
70 |
--------------------------------------------------------------------------------
/packages/app/src/generators/nonebot_store.ts:
--------------------------------------------------------------------------------
1 | import { PythonGenerator, Order } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | export const forBlock = Object.create(null);
5 |
6 | forBlock["store_save_json"] = function (
7 | block: Blockly.Block,
8 | generator: PythonGenerator,
9 | ) {
10 | const dict = generator.valueToCode(block, "DICT", Order.ATOMIC);
11 | const file = block.getFieldValue("FILE");
12 | generator["definitions_"]["import json"] = "import json";
13 | generator["definitions_"]["import nonebot_plugin_localstore as store"] =
14 | "import nonebot_plugin_localstore as store";
15 | const code = `store.get_plugin_data_file("${file}").write_text(json.dumps(${dict}))\n`;
16 | return code;
17 | };
18 |
19 | forBlock["store_load_json"] = function (
20 | block: Blockly.Block,
21 | generator: PythonGenerator,
22 | ) {
23 | const file = block.getFieldValue("FILE");
24 | generator["definitions_"]["import json"] = "import json";
25 | generator["definitions_"]["import nonebot_plugin_localstore as store"] =
26 | "import nonebot_plugin_localstore as store";
27 | const code = `json.loads(store.get_plugin_data_file("${file}").read_text() if store.get_plugin_data_file("${file}").exists() else "{}")`;
28 | return [code, Order.ATOMIC];
29 | };
30 |
31 | forBlock["store_save_text"] = function (
32 | block: Blockly.Block,
33 | generator: PythonGenerator,
34 | ) {
35 | const text = generator.valueToCode(block, "TEXT", Order.ATOMIC);
36 | const file = block.getFieldValue("FILE");
37 | generator["definitions_"]["import nonebot_plugin_localstore as store"] =
38 | "import nonebot_plugin_localstore as store";
39 | const code = `store.get_plugin_data_file("${file}").write_text(${text})\n`;
40 | return code;
41 | };
42 |
43 | forBlock["store_load_text"] = function (
44 | block: Blockly.Block,
45 | generator: PythonGenerator,
46 | ) {
47 | const file = block.getFieldValue("FILE");
48 | generator["definitions_"]["import json"] = "import json";
49 | generator["definitions_"]["import nonebot_plugin_localstore as store"] =
50 | "import nonebot_plugin_localstore as store";
51 | const code = `store.get_plugin_data_file("${file}").read_text() if store.get_plugin_data_file("${file}").exists() else ""`;
52 | return [code, Order.ATOMIC];
53 | };
54 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/fields/field_plus.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @license
3 | * Copyright 2020 Google LLC
4 | * SPDX-License-Identifier: Apache-2.0
5 | */
6 |
7 | /**
8 | * @fileoverview A field for a plus button used for mutation.
9 | */
10 |
11 | import * as Blockly from "blockly/core";
12 | import { getExtraBlockState } from "./serialization_helper";
13 |
14 | /**
15 | * Creates a plus image field used for mutation.
16 | * @param {Object=} args Untyped args passed to block.minus when the field
17 | * is clicked.
18 | * @returns {Blockly.FieldImage} The Plus field.
19 | */
20 | export function createPlusField(args?: Object): Blockly.FieldImage {
21 | const plus = new Blockly.FieldImage(plusImage, 15, 15, undefined, onClick_);
22 | /**
23 | * Untyped args passed to block.plus when the field is clicked.
24 | * @type {?(Object|undefined)}
25 | * @private
26 | */
27 | (plus as any).args_ = args;
28 | return plus;
29 | }
30 |
31 | /**
32 | * Calls block.plus(args) when the plus field is clicked.
33 | * @param {!Blockly.FieldImage} plusField The field being clicked.
34 | * @private
35 | */
36 | function onClick_(plusField: Blockly.FieldImage) {
37 | // TODO: This is a dupe of the mutator code, anyway to unify?
38 | const block = plusField.getSourceBlock() as Blockly.BlockSvg;
39 |
40 | if (block.isInFlyout) {
41 | return;
42 | }
43 |
44 | Blockly.Events.setGroup(true);
45 | const oldExtraState = getExtraBlockState(block);
46 | (block as any).plus((plusField as any).args_);
47 | const newExtraState = getExtraBlockState(block);
48 |
49 | if (oldExtraState != newExtraState) {
50 | Blockly.Events.fire(
51 | new Blockly.Events.BlockChange(
52 | block,
53 | "mutation",
54 | null,
55 | oldExtraState,
56 | newExtraState,
57 | ),
58 | );
59 | }
60 | Blockly.Events.setGroup(false);
61 | }
62 |
63 | const plusImage =
64 | "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC" +
65 | "9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cGF0aCBkPSJNMT" +
66 | "ggMTBoLTR2LTRjMC0xLjEwNC0uODk2LTItMi0ycy0yIC44OTYtMiAybC4wNzEgNGgtNC4wNz" +
67 | "FjLTEuMTA0IDAtMiAuODk2LTIgMnMuODk2IDIgMiAybDQuMDcxLS4wNzEtLjA3MSA0LjA3MW" +
68 | "MwIDEuMTA0Ljg5NiAyIDIgMnMyLS44OTYgMi0ydi00LjA3MWw0IC4wNzFjMS4xMDQgMCAyLS" +
69 | "44OTYgMi0ycy0uODk2LTItMi0yeiIgZmlsbD0id2hpdGUiIC8+PC9zdmc+Cg==";
70 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/nonebot_basic.ts:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from "blockly/core/blocks";
2 |
3 | export const definitions: BlockDefinition[] = [
4 | {
5 | type: "nonebot_on_message",
6 | tooltip: "",
7 | helpUrl: "",
8 | message0: "消息处理 %1 仅与我相关 %2 %3 %4",
9 | args0: [
10 | {
11 | type: "input_dummy",
12 | name: "NAME",
13 | },
14 | {
15 | type: "field_checkbox",
16 | name: "TOME",
17 | checked: "FALSE",
18 | },
19 | {
20 | type: "input_dummy",
21 | name: "PARAMS",
22 | },
23 | {
24 | type: "input_statement",
25 | name: "HANDLE",
26 | },
27 | ],
28 | colour: 0,
29 | },
30 | {
31 | type: "nonebot_on_command",
32 | tooltip:
33 | "处理指定前缀(默认为'/')与命令字符串起始的消息,处理块内的消息文本为命令参数",
34 | helpUrl: "",
35 | message0: "命令处理 %1 命令字符串 %2 仅与我相关 %3 %4 %5",
36 | args0: [
37 | {
38 | type: "input_dummy",
39 | name: "NAME",
40 | },
41 | {
42 | type: "field_input",
43 | name: "COMMAND",
44 | text: "hello",
45 | },
46 | {
47 | type: "field_checkbox",
48 | name: "TOME",
49 | checked: "FALSE",
50 | },
51 | {
52 | type: "input_dummy",
53 | name: "PARAMS",
54 | },
55 | {
56 | type: "input_statement",
57 | name: "HANDLE",
58 | },
59 | ],
60 | colour: 0,
61 | },
62 | {
63 | type: "nonebot_param_text",
64 | tooltip: "",
65 | helpUrl: "",
66 | message0: "消息文本 %1",
67 | args0: [
68 | {
69 | type: "input_dummy",
70 | name: "NAME",
71 | },
72 | ],
73 | output: null,
74 | colour: 30,
75 | },
76 | {
77 | type: "nonebot_send",
78 | tooltip: "",
79 | helpUrl: "",
80 | message0: "发送消息 %1 %2 结束处理流程 %3 %4",
81 | args0: [
82 | {
83 | type: "input_dummy",
84 | name: "NAME",
85 | },
86 | {
87 | type: "input_value",
88 | name: "MESSAGE",
89 | },
90 | {
91 | type: "field_checkbox",
92 | name: "FINISH",
93 | checked: "FALSE",
94 | },
95 | {
96 | type: "input_dummy",
97 | name: "FINISH",
98 | },
99 | ],
100 | previousStatement: null,
101 | nextStatement: null,
102 | colour: 60,
103 | inputsInline: true,
104 | },
105 | ];
106 |
--------------------------------------------------------------------------------
/packages/app/src/components/ButtonPanel.vue:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 |
31 |
36 |
43 |
44 |
45 |
46 | {{ outputsStore.snackbarMsg }}
47 |
48 |
49 |
50 |
51 | 暂存
52 |
53 | 暂存项目到浏览器
54 |
55 |
56 |
57 |
58 |
59 | 恢复
60 |
61 | 恢复上次暂存的项目
62 |
63 |
64 |
65 |
66 |
67 | 导入导出
68 |
69 | 导入导出项目设计文件,或生成 NoneBot 工程
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
89 |
90 |
98 |
--------------------------------------------------------------------------------
/packages/app/src/generators/nonebot_scheduler.ts:
--------------------------------------------------------------------------------
1 | import { PythonGenerator, Order } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | import { getGlobalStatement } from "./helper";
5 |
6 | export const forBlock = Object.create(null);
7 |
8 | forBlock["scheduler_add"] = function (
9 | block: Blockly.Block,
10 | generator: PythonGenerator,
11 | ) {
12 | const time = generator.valueToCode(block, "TIME", Order.ATOMIC);
13 | const id = generator.valueToCode(block, "ID", Order.ATOMIC);
14 | if (time === "" || time === "None" || id === "" || id === "None") {
15 | return "";
16 | }
17 | generator["definitions_"][
18 | "from nonebot_plugin_apscheduler import scheduler"
19 | ] = "from nonebot_plugin_apscheduler import scheduler";
20 | const globalStatement =
21 | generator.INDENT + getGlobalStatement(block, generator);
22 | const handleStatement =
23 | generator.statementToCode(block, "HANDLE") || generator.PASS;
24 | const code = `@scheduler.scheduled_job(${time}, id=${id})\nasync def _():\n${globalStatement}${handleStatement}\n`;
25 | return code;
26 | };
27 |
28 | forBlock["scheduler_remove"] = function (
29 | block: Blockly.Block,
30 | generator: PythonGenerator,
31 | ) {
32 | const id = generator.valueToCode(block, "ID", Order.ATOMIC);
33 | generator["definitions_"][
34 | "from nonebot_plugin_apscheduler import scheduler"
35 | ] = "from nonebot_plugin_apscheduler import scheduler";
36 | const code = `scheduler.remove_job(${id})\n`;
37 | return code;
38 | };
39 |
40 | forBlock["scheduler_time_interval"] = function (
41 | block: Blockly.Block,
42 | _: PythonGenerator,
43 | ) {
44 | const number = block.getFieldValue("NUMBER");
45 | const unit = block.getFieldValue("UNIT");
46 | const code = `"interval", ${unit}=${number}`;
47 | return [code, Order.ATOMIC];
48 | };
49 |
50 | forBlock["scheduler_time_cron_daily"] = function (
51 | block: Blockly.Block,
52 | _: PythonGenerator,
53 | ) {
54 | const hour = block.getFieldValue("HOUR");
55 | const minute = block.getFieldValue("MINUTE");
56 | const second = block.getFieldValue("SECOND");
57 | const code = `"cron", hour=${hour}, minute=${minute}, second=${second}`;
58 | return [code, Order.ATOMIC];
59 | };
60 |
61 | forBlock["scheduler_time_cron"] = function (
62 | block: Blockly.Block,
63 | _: PythonGenerator,
64 | ) {
65 | const month = block.getFieldValue("MONTH");
66 | const day = block.getFieldValue("DAY");
67 | const hour = block.getFieldValue("HOUR");
68 | const minute = block.getFieldValue("MINUTE");
69 | const second = block.getFieldValue("SECOND");
70 | const code = `"cron", month=${month}, day=${day}, hour=${hour}, minute=${minute}, second=${second}`;
71 | return [code, Order.ATOMIC];
72 | };
73 |
--------------------------------------------------------------------------------
/packages/app/src/App.vue:
--------------------------------------------------------------------------------
1 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
81 |
82 |
88 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/nonebot_request.ts:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from "blockly/core/blocks";
2 |
3 | export const definitions: BlockDefinition[] = [
4 | {
5 | type: "request_get",
6 | tooltip: "",
7 | helpUrl: "",
8 | message0:
9 | "网络请求 GET %1 链接 %2 (可选)参数字典 %3 (可选)标头字典 %4 返回 %5 %6 %7 秒超时 %8",
10 | args0: [
11 | {
12 | type: "input_dummy",
13 | name: "NAME",
14 | },
15 | {
16 | type: "input_value",
17 | name: "URL",
18 | align: "RIGHT",
19 | check: "String",
20 | },
21 | {
22 | type: "input_value",
23 | name: "PARAMS",
24 | align: "RIGHT",
25 | check: "dict",
26 | },
27 | {
28 | type: "input_value",
29 | name: "HEADERS",
30 | check: "dict",
31 | },
32 | {
33 | type: "field_dropdown",
34 | name: "TYPE",
35 | options: [
36 | ["字典", "dict"],
37 | ["列表", "list"],
38 | ["文本", "string"],
39 | ["二进制", "binary"],
40 | ],
41 | },
42 | {
43 | type: "input_dummy",
44 | name: "TYPE",
45 | align: "RIGHT",
46 | },
47 | {
48 | type: "field_number",
49 | name: "TIMEOUT",
50 | value: 60,
51 | min: 0,
52 | },
53 | {
54 | type: "input_dummy",
55 | name: "TIMEOUT",
56 | align: "RIGHT",
57 | },
58 | ],
59 | output: null,
60 | colour: 270,
61 | inputsInline: false,
62 | },
63 | {
64 | type: "request_post",
65 | tooltip: "",
66 | helpUrl: "",
67 | message0:
68 | "网络请求 POST %1 链接 %2 数据字典 %3 (可选)参数字典 %4 (可选)标头字典 %5 返回 %6 %7 %8 秒超时 %9",
69 | args0: [
70 | {
71 | type: "input_dummy",
72 | name: "NAME",
73 | },
74 | {
75 | type: "input_value",
76 | name: "URL",
77 | align: "RIGHT",
78 | check: "String",
79 | },
80 | {
81 | type: "input_value",
82 | name: "JSON",
83 | align: "RIGHT",
84 | check: "dict",
85 | },
86 | {
87 | type: "input_value",
88 | name: "PARAMS",
89 | align: "RIGHT",
90 | check: "dict",
91 | },
92 | {
93 | type: "input_value",
94 | name: "HEADERS",
95 | align: "RIGHT",
96 | check: "dict",
97 | },
98 | {
99 | type: "field_dropdown",
100 | name: "TYPE",
101 | options: [
102 | ["字典", "dict"],
103 | ["列表", "list"],
104 | ["文本", "string"],
105 | ["二进制", "binary"],
106 | ],
107 | },
108 | {
109 | type: "input_dummy",
110 | name: "TYPE",
111 | align: "RIGHT",
112 | },
113 | {
114 | type: "field_number",
115 | name: "TIMEOUT",
116 | value: 60,
117 | min: 0,
118 | },
119 | {
120 | type: "input_dummy",
121 | name: "TIMEOUT",
122 | align: "RIGHT",
123 | },
124 | ],
125 | output: null,
126 | colour: 270,
127 | inputsInline: false,
128 | },
129 | ];
130 |
--------------------------------------------------------------------------------
/packages/app/src/generators/nonebot_request.ts:
--------------------------------------------------------------------------------
1 | import { PythonGenerator, Order } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | export const forBlock = Object.create(null);
5 |
6 | forBlock["request_get"] = function (
7 | block: Blockly.Block,
8 | generator: PythonGenerator,
9 | ) {
10 | const url = generator.valueToCode(block, "URL", Order.ATOMIC);
11 | const params = generator.valueToCode(block, "PARAMS", Order.ATOMIC) || "None";
12 | const headers =
13 | generator.valueToCode(block, "HEADERS", Order.ATOMIC) || "None";
14 | const type = block.getFieldValue("TYPE");
15 | const timeout = block.getFieldValue("TIMEOUT");
16 | if (url === "" || url === "None") {
17 | return ["None", Order.ATOMIC];
18 | }
19 | generator["definitions_"]["from nonebot import get_driver"] =
20 | "from nonebot import get_driver";
21 | generator["definitions_"]["from nonebot.drivers import Request"] =
22 | "from nonebot.drivers import Request";
23 | generator["definitions_"]["from nonebot.drivers import HTTPClientMixin"] =
24 | "from nonebot.drivers import HTTPClientMixin";
25 | generator["definitions_"]["nonebot_request_driver"] =
26 | 'driver = get_driver() \n\
27 | if not isinstance(driver, HTTPClientMixin): \n\
28 | raise RuntimeError( \n\
29 | f"Current driver {driver} does not support http client requests!" \n\
30 | )';
31 | const request = `Request("GET", ${url}, params=${params}, headers=${headers}, timeout=${timeout})`;
32 | const content = `(await driver.request(${request})).content`;
33 | let code = content;
34 | if (type === "dict") {
35 | generator["definitions_"]["import json"] = "import json";
36 | code = `json.loads(${content} or "{}")`;
37 | } else if (type === "list") {
38 | generator["definitions_"]["import json"] = "import json";
39 | code = `json.loads(${content} or "[]")`;
40 | }
41 | return [code, Order.ATOMIC];
42 | };
43 |
44 | forBlock["request_post"] = function (
45 | block: Blockly.Block,
46 | generator: PythonGenerator,
47 | ) {
48 | const url = generator.valueToCode(block, "URL", Order.ATOMIC);
49 | const json = generator.valueToCode(block, "JSON", Order.ATOMIC) || "None";
50 | const params = generator.valueToCode(block, "PARAMS", Order.ATOMIC) || "None";
51 | const headers =
52 | generator.valueToCode(block, "HEADERS", Order.ATOMIC) || "None";
53 | const type = block.getFieldValue("TYPE");
54 | const timeout = block.getFieldValue("TIMEOUT");
55 | if (url === "" || url === "None") {
56 | return ["None", Order.ATOMIC];
57 | }
58 | generator["definitions_"]["from nonebot import get_driver"] =
59 | "from nonebot import get_driver";
60 | generator["definitions_"]["from nonebot.drivers import Request"] =
61 | "from nonebot.drivers import Request";
62 | generator["definitions_"]["from nonebot.drivers import HTTPClientMixin"] =
63 | "from nonebot.drivers import HTTPClientMixin";
64 | generator["definitions_"]["nonebot_request_driver"] =
65 | 'driver = get_driver() \n\
66 | if not isinstance(driver, HTTPClientMixin): \n\
67 | raise RuntimeError( \n\
68 | f"Current driver {driver} does not support http client requests!" \n\
69 | )';
70 | const request = `Request("POST", ${url}, json=${json}, params=${params}, headers=${headers}, timeout=${timeout})`;
71 | const content = `(await driver.request(${request})).content`;
72 | let code = content;
73 | if (type === "dict") {
74 | generator["definitions_"]["import json"] = "import json";
75 | code = `json.loads(${content} or "{}")`;
76 | } else if (type === "list") {
77 | generator["definitions_"]["import json"] = "import json";
78 | code = `json.loads(${content} or "[]")`;
79 | }
80 | return [code, Order.ATOMIC];
81 | };
82 |
--------------------------------------------------------------------------------
/.github/workflows/website-preview-cd.yml:
--------------------------------------------------------------------------------
1 | name: Site Deploy (Preview CD)
2 |
3 | on:
4 | workflow_run:
5 | workflows: ["Site Deploy (Preview CI)"]
6 | types:
7 | - completed
8 |
9 | jobs:
10 | preview-cd:
11 | runs-on: ubuntu-latest
12 | concurrency:
13 | group: pull-request-preview-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
14 | cancel-in-progress: true
15 |
16 | if: ${{ github.event.workflow_run.conclusion == 'success' }}
17 |
18 | environment: pull request
19 |
20 | permissions:
21 | actions: read
22 | statuses: write
23 | pull-requests: write
24 |
25 | steps:
26 | - name: Set Commit Status
27 | uses: actions/github-script@v7
28 | with:
29 | script: |
30 | github.rest.repos.createCommitStatus({
31 | owner: context.repo.owner,
32 | repo: context.repo.repo,
33 | sha: context.payload.workflow_run.head_sha,
34 | context: 'Website Preview',
35 | description: 'Deploying...',
36 | state: 'pending',
37 | })
38 |
39 | - name: Download Artifact
40 | uses: actions/download-artifact@v4
41 | with:
42 | name: website-preview
43 | github-token: ${{ secrets.GITHUB_TOKEN }}
44 | run-id: ${{ github.event.workflow_run.id }}
45 |
46 | - name: Restore Context
47 | run: |
48 | cat action.env >> $GITHUB_ENV
49 |
50 | - name: Set Deploy Name
51 | run: |
52 | echo "DEPLOY_NAME=deploy-preview-${{ env.PR_NUMBER }}" >> $GITHUB_ENV
53 |
54 | - name: Deploy to Netlify
55 | id: deploy
56 | uses: nwtgck/actions-netlify@v3
57 | with:
58 | publish-dir: ./packages/app/dist
59 | production-deploy: false
60 | deploy-message: "Deploy ${{ env.DEPLOY_NAME }}@${{ github.event.workflow_run.head_sha }}"
61 | alias: ${{ env.DEPLOY_NAME }}
62 | env:
63 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
64 | NETLIFY_SITE_ID: ${{ secrets.SITE_ID }}
65 |
66 | # action netlify has no pull request context, so we need to comment by ourselves
67 | - name: Comment on Pull Request
68 | uses: marocchino/sticky-pull-request-comment@v2
69 | with:
70 | header: website
71 | number: ${{ env.PR_NUMBER }}
72 | message: |
73 | :rocket: Deployed to ${{ steps.deploy.outputs.deploy-url }}
74 |
75 | - name: Set Commit Status
76 | uses: actions/github-script@v7
77 | if: always()
78 | with:
79 | script: |
80 | if (`${{ job.status }}` === 'success') {
81 | github.rest.repos.createCommitStatus({
82 | owner: context.repo.owner,
83 | repo: context.repo.repo,
84 | sha: context.payload.workflow_run.head_sha,
85 | context: 'Website Preview',
86 | description: `Deployed to ${{ steps.deploy.outputs.deploy-url }}`,
87 | state: 'success',
88 | target_url: `${{ steps.deploy.outputs.deploy-url }}`,
89 | })
90 | } else {
91 | github.rest.repos.createCommitStatus({
92 | owner: context.repo.owner,
93 | repo: context.repo.repo,
94 | sha: context.payload.workflow_run.head_sha,
95 | context: 'Website Preview',
96 | description: `Deploy ${{ job.status }}`,
97 | state: 'failure',
98 | })
99 | }
100 |
--------------------------------------------------------------------------------
/packages/app/src/plugins/vuetify.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * plugins/vuetify.ts
3 | *
4 | * Framework documentation: https://vuetifyjs.com`
5 | */
6 |
7 | // Styles
8 | import "vuetify/styles";
9 | import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
10 |
11 | // Composables
12 | import { createVuetify, ThemeDefinition } from "vuetify";
13 |
14 | const LightTheme: ThemeDefinition = {
15 | dark: false,
16 | colors: {
17 | primary: "#4459A9",
18 | "primary-darken-1": "#2A4190",
19 | "primary-darken-2": "#0D2878",
20 | "primary-darken-3": "#001452",
21 | "primary-lighten-1": "#5D72C4",
22 | "primary-lighten-2": "#778CE0",
23 | "primary-lighten-3": "#92A7FD",
24 | "primary-lighten-4": "#B7C4FF",
25 | "primary-container": "#DCE1FF",
26 | "on-primary-container": "#001552",
27 | "on-primary": "#FFFFFF",
28 | secondary: "#5A5D72",
29 | "secondary-darken-1": "#424659",
30 | "secondary-darken-2": "#2B3042",
31 | "secondary-darken-3": "#171B2C",
32 | "secondary-lighten-1": "#72768B",
33 | "secondary-lighten-2": "#8C90A6",
34 | "secondary-lighten-3": "#A7AAC1",
35 | "secondary-lighten-4": "#C2C5DD",
36 | "secondary-container": "#DEE1F9",
37 | "on-secondary-container": "#171B2C",
38 | tertiary: "#75546F",
39 | "tertiary-container": "#FFD7F5",
40 | "on-tertiary-container": "#2C1229",
41 | background: "#FEFBFF",
42 | "on-background": "#1B1B1E",
43 | surface: "#FEFBFF",
44 | "surface-variant": "#E3E1E9",
45 | "on-surface-variant": "#46464C",
46 | error: "#BA1A1A",
47 | "on-error": "#FFFFFF",
48 | "error-container": "#FFDAD6",
49 | "on-error-container": "#410002",
50 | info: "#275EA7",
51 | success: "#008770",
52 | warning: "#FF897D",
53 | },
54 | };
55 |
56 | const DarkTheme: ThemeDefinition = {
57 | dark: true,
58 | colors: {
59 | primary: "#B7C4FE",
60 | "primary-darken-1": "#2A4190",
61 | "primary-darken-2": "#0C2878",
62 | "primary-darken-3": "#001552",
63 | "primary-lighten-1": "#5D72C4",
64 | "primary-lighten-2": "#778CE0",
65 | "primary-lighten-3": "#92A7FD",
66 | "primary-lighten-4": "#B7C4FF",
67 | "primary-lighten-5": "#DCE1FF",
68 | "primary-container": "#374476",
69 | "on-primary-container": "#DCE1FF",
70 | "on-primary": "#202D5E",
71 | secondary: "#C4C5D5",
72 | "secondary-darken-1": "#424659",
73 | "secondary-darken-2": "#2B3042",
74 | "secondary-darken-3": "#171B2C",
75 | "secondary-lighten-1": "#72768B",
76 | "secondary-lighten-2": "#8C90A6",
77 | "secondary-lighten-3": "#A7AAC1",
78 | "secondary-lighten-4": "#C2C5DD",
79 | "secondary-container": "#444653",
80 | "on-secondary-container": "#E1E1F2",
81 | tertiary: "#DCBED3",
82 | "tertiary-container": "#564051",
83 | "on-tertiary-container": "#F9DAEF",
84 | background: "#1B1B1E",
85 | "on-background": "#E4E1E4",
86 | surface: "#1B1B1E",
87 | "surface-variant": "#46464C",
88 | "on-surface-variant": "#C7C6CC",
89 | error: "#FFB4AB",
90 | "on-error": "#690005",
91 | "error-container": "#93000A",
92 | "on-error-container": "#FFB4AB",
93 | info: "#275EA7",
94 | success: "#008770",
95 | warning: "#FF897D",
96 | },
97 | };
98 |
99 | // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
100 | export default createVuetify({
101 | ssr: false,
102 | // blueprint: md3,
103 | icons: {
104 | defaultSet: "mdi",
105 | aliases,
106 | sets: {
107 | mdi,
108 | },
109 | },
110 | theme: {
111 | defaultTheme: "LightTheme",
112 | themes: {
113 | LightTheme,
114 | DarkTheme,
115 | },
116 | },
117 | });
118 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # monorepo modules
2 | /node_modules
3 |
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 | *$py.class
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | share/python-wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 | MANIFEST
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .nox/
46 | .coverage
47 | .coverage.*
48 | .cache
49 | nosetests.xml
50 | coverage.xml
51 | *.cover
52 | *.py,cover
53 | .hypothesis/
54 | .pytest_cache/
55 | cover/
56 |
57 | # Translations
58 | *.mo
59 | *.pot
60 |
61 | # Django stuff:
62 | *.log
63 | local_settings.py
64 | db.sqlite3
65 | db.sqlite3-journal
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | .pybuilder/
79 | target/
80 |
81 | # Jupyter Notebook
82 | .ipynb_checkpoints
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | # For a library or package, you might want to ignore these files since the code is
90 | # intended to run in multiple environments; otherwise, check them in:
91 | # .python-version
92 |
93 | # pipenv
94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
97 | # install all needed dependencies.
98 | #Pipfile.lock
99 |
100 | # poetry
101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
102 | # This is especially recommended for binary packages to ensure reproducibility, and is more
103 | # commonly ignored for libraries.
104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
105 | #poetry.lock
106 |
107 | # pdm
108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
109 | #pdm.lock
110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
111 | # in version control.
112 | # https://pdm.fming.dev/#use-with-ide
113 | .pdm.toml
114 |
115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
116 | __pypackages__/
117 |
118 | # Celery stuff
119 | celerybeat-schedule
120 | celerybeat.pid
121 |
122 | # SageMath parsed files
123 | *.sage.py
124 |
125 | # Environments
126 | .env
127 | .venv
128 | env/
129 | venv/
130 | ENV/
131 | env.bak/
132 | venv.bak/
133 |
134 | # Spyder project settings
135 | .spyderproject
136 | .spyproject
137 |
138 | # Rope project settings
139 | .ropeproject
140 |
141 | # mkdocs documentation
142 | /site
143 |
144 | # mypy
145 | .mypy_cache/
146 | .dmypy.json
147 | dmypy.json
148 |
149 | # Pyre type checker
150 | .pyre/
151 |
152 | # pytype static type analyzer
153 | .pytype/
154 |
155 | # Cython debug symbols
156 | cython_debug/
157 |
158 | # PyCharm
159 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
160 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
161 | # and can be added to the global gitignore or merged into this file. For a more nuclear
162 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
163 | #.idea/
164 |
--------------------------------------------------------------------------------
/packages/app/src/generators/nonebot_basic.ts:
--------------------------------------------------------------------------------
1 | import { PythonGenerator, Order } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | import { getGlobalStatement } from "./helper";
5 |
6 | export const forBlock = Object.create(null);
7 |
8 | forBlock["nonebot_on_message"] = function (
9 | block: Blockly.Block,
10 | generator: PythonGenerator,
11 | ) {
12 | const tomeCheckbox = block.getFieldValue("TOME") === "TRUE";
13 | const globalStatement =
14 | generator.INDENT + getGlobalStatement(block, generator);
15 | const handleStatement =
16 | generator.statementToCode(block, "HANDLE") || generator.PASS;
17 | generator["definitions_"]["from typing import Annotated"] =
18 | "from typing import Annotated";
19 | // generator["definitions_"]["from nonebot.adapters import Bot"] = "from nonebot.adapters import Bot";
20 | // generator["definitions_"]["from nonebot.adapters import Event"] = "from nonebot.adapters import Event";
21 | generator["definitions_"]["from nonebot.matcher import Matcher"] =
22 | "from nonebot.matcher import Matcher";
23 | generator["definitions_"]["from nonebot.adapters import Message"] =
24 | "from nonebot.adapters import Message";
25 | generator["definitions_"]["from nonebot.params import EventMessage"] =
26 | "from nonebot.params import EventMessage";
27 | generator["definitions_"]["from nonebot.plugin import on_message"] =
28 | "from nonebot.plugin import on_message";
29 | let tomeStatement = "";
30 | if (tomeCheckbox) {
31 | generator["definitions_"]["from nonebot.rule import to_me"] =
32 | "from nonebot.rule import to_me";
33 | tomeStatement = "rule=to_me()";
34 | }
35 | let code = `@on_message(${tomeStatement}).handle()\n`;
36 | // code += `async def _(matcher: Matcher, bot: Bot, event: Event, message: Annotated[Message, EventMessage()]):\n`;
37 | code += `async def _(matcher: Matcher, message: Annotated[Message, EventMessage()]):\n${globalStatement}${handleStatement}\n`;
38 | return code;
39 | };
40 |
41 | forBlock["nonebot_on_command"] = function (
42 | block: Blockly.Block,
43 | generator: PythonGenerator,
44 | ) {
45 | const commandText = block.getFieldValue("COMMAND");
46 | const tomeCheckbox = block.getFieldValue("TOME") === "TRUE";
47 | const globalStatement =
48 | generator.INDENT + getGlobalStatement(block, generator);
49 | const handleStatement =
50 | generator.statementToCode(block, "HANDLE") || generator.PASS;
51 | generator["definitions_"]["from typing import Annotated"] =
52 | "from typing import Annotated";
53 | // generator["definitions_"]["from nonebot.adapters import Bot"] = "from nonebot.adapters import Bot";
54 | // generator["definitions_"]["from nonebot.adapters import Event"] = "from nonebot.adapters import Event";
55 | generator["definitions_"]["from nonebot.matcher import Matcher"] =
56 | "from nonebot.matcher import Matcher";
57 | generator["definitions_"]["from nonebot.adapters import Message"] =
58 | "from nonebot.adapters import Message";
59 | generator["definitions_"]["from nonebot.params import CommandArg"] =
60 | "from nonebot.params import CommandArg";
61 | generator["definitions_"]["from nonebot.plugin import on_command"] =
62 | "from nonebot.plugin import on_command";
63 | let tome_statement = "";
64 | if (tomeCheckbox) {
65 | generator["definitions_"]["from nonebot.rule import to_me"] =
66 | "from nonebot.rule import to_me";
67 | tome_statement = ", rule=to_me()";
68 | }
69 | let code = `@on_command("${commandText}"${tome_statement}).handle()\n`;
70 | // code += `async def _(matcher: Matcher, bot: Bot, event: Event, message: Annotated[Message, CommandArg()]):\n`;
71 | code += `async def _(matcher: Matcher, message: Annotated[Message, CommandArg()]):\n${globalStatement}${handleStatement}\n`;
72 | return code;
73 | };
74 |
75 | forBlock["nonebot_param_text"] = function () {
76 | const code = "message.extract_plain_text()";
77 | return [code, Order.NONE];
78 | };
79 |
80 | forBlock["nonebot_send"] = function (
81 | block: Blockly.Block,
82 | generator: PythonGenerator,
83 | ) {
84 | const message = generator.valueToCode(block, "MESSAGE", Order.ATOMIC);
85 | const checkbox_tome = block.getFieldValue("FINISH") === "TRUE";
86 | if (checkbox_tome) {
87 | return `await matcher.finish(${message})\n`;
88 | } else {
89 | return `await matcher.send(${message})\n`;
90 | }
91 | };
92 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/nonebot_scheduler.ts:
--------------------------------------------------------------------------------
1 | import { BlockDefinition } from "blockly/core/blocks";
2 |
3 | export const definitions: BlockDefinition[] = [
4 | {
5 | type: "scheduler_add",
6 | tooltip: "",
7 | helpUrl: "",
8 | message0: "添加定时任务 %1 定时时间 %2 ID %3 %4",
9 | args0: [
10 | {
11 | type: "input_dummy",
12 | name: "NAME",
13 | },
14 | {
15 | type: "input_value",
16 | name: "TIME",
17 | align: "RIGHT",
18 | check: "time",
19 | },
20 | {
21 | type: "input_value",
22 | name: "ID",
23 | align: "RIGHT",
24 | check: "String",
25 | },
26 | {
27 | type: "input_statement",
28 | name: "HANDLE",
29 | },
30 | ],
31 | colour: 210,
32 | },
33 | {
34 | type: "scheduler_remove",
35 | tooltip: "",
36 | helpUrl: "",
37 | message0: "移除定时任务 %1 ID %2",
38 | args0: [
39 | {
40 | type: "input_dummy",
41 | name: "NAME",
42 | },
43 | {
44 | type: "input_value",
45 | name: "ID",
46 | check: "String",
47 | },
48 | ],
49 | previousStatement: null,
50 | nextStatement: null,
51 | colour: 210,
52 | inputsInline: true,
53 | },
54 | {
55 | type: "scheduler_time_interval",
56 | tooltip: "",
57 | helpUrl: "",
58 | message0: "每 %1 %2 %3",
59 | args0: [
60 | {
61 | type: "field_number",
62 | name: "NUMBER",
63 | value: 30,
64 | min: 0,
65 | },
66 | {
67 | type: "field_dropdown",
68 | name: "UNIT",
69 | options: [
70 | ["秒", "seconds"],
71 | ["分", "minutes"],
72 | ["时", "hours"],
73 | ["天", "days"],
74 | ["周", "weeks"],
75 | ],
76 | },
77 | {
78 | type: "input_dummy",
79 | name: "NAME",
80 | },
81 | ],
82 | output: "time",
83 | colour: 240,
84 | },
85 | {
86 | type: "scheduler_time_cron_daily",
87 | tooltip: "",
88 | helpUrl: "",
89 | message0: "每天 %1 时 %2 分 %3 秒 %4",
90 | args0: [
91 | {
92 | type: "field_dropdown",
93 | name: "HOUR",
94 | options: [["任意", '"*"']].concat(
95 | Array.from({ length: 24 }, (_, i) => [String(i), String(i)]),
96 | ),
97 | },
98 | {
99 | type: "field_dropdown",
100 | name: "MINUTE",
101 | options: [["任意", '"*"']].concat(
102 | Array.from({ length: 60 }, (_, i) => [String(i), String(i)]),
103 | ),
104 | },
105 | {
106 | type: "field_dropdown",
107 | name: "SECOND",
108 | options: Array.from({ length: 60 }, (_, i) => [String(i), String(i)]),
109 | },
110 | {
111 | type: "input_dummy",
112 | name: "NAME",
113 | },
114 | ],
115 | output: "time",
116 | colour: 240,
117 | },
118 | {
119 | type: "scheduler_time_cron",
120 | tooltip: "",
121 | helpUrl: "",
122 | message0: "在 %1 月 %2 日 %3 时 %4 分 %5 秒 %6",
123 | args0: [
124 | {
125 | type: "field_dropdown",
126 | name: "MONTH",
127 | options: [["任意", '"*"']].concat(
128 | Array.from({ length: 12 }, (_, i) => [String(i + 1), String(i + 1)]),
129 | ),
130 | },
131 | {
132 | type: "field_dropdown",
133 | name: "DAY",
134 | options: [["任意", '"*"']].concat(
135 | Array.from({ length: 31 }, (_, i) => [String(i + 1), String(i + 1)]),
136 | ),
137 | },
138 | {
139 | type: "field_dropdown",
140 | name: "HOUR",
141 | options: [["任意", '"*"']].concat(
142 | Array.from({ length: 24 }, (_, i) => [String(i), String(i)]),
143 | ),
144 | },
145 | {
146 | type: "field_dropdown",
147 | name: "MINUTE",
148 | options: [["任意", '"*"']].concat(
149 | Array.from({ length: 60 }, (_, i) => [String(i), String(i)]),
150 | ),
151 | },
152 | {
153 | type: "field_dropdown",
154 | name: "SECOND",
155 | options: Array.from({ length: 60 }, (_, i) => [String(i), String(i)]),
156 | },
157 | {
158 | type: "input_dummy",
159 | name: "NAME",
160 | },
161 | ],
162 | output: "time",
163 | colour: 240,
164 | },
165 | ];
166 |
--------------------------------------------------------------------------------
/packages/app/src/components/ConfigTab.vue:
--------------------------------------------------------------------------------
1 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
72 |
73 |
74 |
82 |
83 |
84 |
90 |
91 |
92 |
100 |
101 |
102 |
109 |
110 |
111 |
112 |
113 |
114 |
120 |
121 |
122 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | 导入设计文件
138 |
139 |
140 |
141 |
142 |
143 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 | 导出设计文件
164 |
165 |
166 |
167 |
168 | 导出 NoneBot 项目
169 |
170 |
171 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/packages/app/src/generators/nonebot_alconna.ts:
--------------------------------------------------------------------------------
1 | import { PythonGenerator, Order } from "blockly/python";
2 | import * as Blockly from "blockly/core";
3 |
4 | import { AlconnaBlock, AlconnaArgGetBlock } from "@/blocks/nonebot_alconna";
5 | import { getAlconnaArg } from "@/blocks/fields/alconna_helper";
6 | import { getGlobalStatement } from "./helper";
7 |
8 | export const forBlock = Object.create(null);
9 |
10 | forBlock["nonebot_on_alconna"] = function (
11 | block: AlconnaBlock,
12 | generator: PythonGenerator,
13 | ) {
14 | const command = block.getFieldValue("COMMAND");
15 | const tomeCheckbox = block.getFieldValue("TOME") === "TRUE";
16 | const globalStatement =
17 | generator.INDENT + getGlobalStatement(block, generator);
18 | const handleStatement =
19 | generator.statementToCode(block, "HANDLE") || generator.PASS;
20 | // generator["definitions_"]["from nonebot.adapters import Bot"] = "from nonebot.adapters import Bot";
21 | // generator["definitions_"]["from nonebot.adapters import Event"] = "from nonebot.adapters import Event";
22 | generator["definitions_"]["from nonebot.matcher import Matcher"] =
23 | "from nonebot.matcher import Matcher";
24 | generator["definitions_"]["from nonebot_plugin_alconna import on_alconna"] =
25 | "from nonebot_plugin_alconna import on_alconna";
26 | let tomeStatement = "";
27 | if (tomeCheckbox) {
28 | generator["definitions_"]["from nonebot.rule import to_me"] =
29 | "from nonebot.rule import to_me";
30 | tomeStatement = ", rule=to_me()";
31 | }
32 | let args: String[] = [];
33 | let argsMatcher = "";
34 | let argsFunction = "";
35 | for (let n = 0; n < block.itemCount_; n++) {
36 | const block_type = block
37 | .getInput("ARG" + String(n))
38 | ?.connection?.targetConnection?.getSourceBlock().type;
39 | const argCode = generator.valueToCode(block, "ARG" + String(n), Order.NONE);
40 | if (block_type === "alconna_const") {
41 | argsMatcher += ` ${argCode}`;
42 | } else if (block_type === "alconna_arg") {
43 | argsMatcher += ` {${argCode}}`;
44 | argsFunction += `, ${argCode}`;
45 | // get name before `: type`
46 | args.push(argCode.split(":")[0]);
47 | }
48 | }
49 | let code = `@on_alconna("${command}${argsMatcher}"${tomeStatement}).handle()\n`;
50 | // code += `async def _(matcher: Matcher, bot: Bot, event: Event, message: Annotated[Message, CommandArg()]):\n`;
51 | code += `async def _(matcher: Matcher${argsFunction}):\n${globalStatement}${handleStatement}\n`;
52 | return code;
53 | };
54 |
55 | forBlock["alconna_const"] = function (
56 | block: Blockly.Block,
57 | _: PythonGenerator,
58 | ) {
59 | const text = block.getFieldValue("TEXT");
60 | return [text, Order.ATOMIC];
61 | };
62 |
63 | forBlock["alconna_arg"] = function (
64 | block: Blockly.Block,
65 | generator: PythonGenerator,
66 | ) {
67 | const name = block.getFieldValue("NAME");
68 | const type = block.getFieldValue("TYPE");
69 | if (name) {
70 | return [`${generator.getVariableName("arg_" + name)}: ${type}`, Order.NONE];
71 | }
72 | return ["", Order.ATOMIC];
73 | };
74 |
75 | forBlock["alconna_arg_get"] = function (
76 | block: AlconnaArgGetBlock,
77 | generator: PythonGenerator,
78 | ) {
79 | // This generator will also update the dropdown list
80 | let name = block.getFieldValue("NAME");
81 | const args = getAlconnaArg(block);
82 | let options = new Array();
83 | // If the block is not initialized, it is reloading from saved
84 | // Should rebuild the dropdown list and set the name to the saved name `block.name_`
85 | if (!block.isInitialized_ && block.name_ !== "") {
86 | name = block.name_;
87 | block.isInitialized_ = true;
88 | // Make sure the selected value is the first one of the dropdown list
89 | // Due to the dynamic dropdowns are not responding to set value calls correctly
90 | // https://github.com/google/blockly/issues/3099
91 | // This will also cause warnings in console:
92 | // `Cannot set the dropdown's value to an unavailable option.`
93 | if (args.indexOf(name) !== -1) {
94 | options.push([name, name]);
95 | }
96 | args.forEach((arg) => {
97 | if (arg !== name) {
98 | options.push([arg, arg]);
99 | }
100 | });
101 | this.removeInput("PARAMS");
102 | this.setWarningText("");
103 | this.appendDummyInput("PARAMS")
104 | ?.appendField("获取参数")
105 | .appendField(new Blockly.FieldDropdown(options), "NAME");
106 | } else {
107 | // If the block is initialized, update the saved name and rebuild the dropdown list
108 | args.forEach((arg) => {
109 | options.push([arg, arg]);
110 | });
111 | block.name_ = name;
112 | }
113 | if (args.length === 0) {
114 | this.removeInput("PARAMS");
115 | this.setWarningText("请放置在“跨平台命令处理”中使用");
116 | this.appendDummyInput("PARAMS")
117 | .appendField("获取参数")
118 | .appendField(new Blockly.FieldDropdown([["-", ""]]), "NAME");
119 | return ["", Order.ATOMIC];
120 | }
121 | if (!args.find((arg) => arg === name)) {
122 | this.removeInput("PARAMS");
123 | this.setWarningText("");
124 | this.appendDummyInput("PARAMS")
125 | ?.appendField("获取参数")
126 | .appendField(new Blockly.FieldDropdown(options), "NAME");
127 | }
128 | if (name) {
129 | return [generator.getVariableName("arg_" + name), Order.NONE];
130 | }
131 | return ["", Order.ATOMIC];
132 | };
133 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/nonebot_alconna.ts:
--------------------------------------------------------------------------------
1 | import * as Blockly from "blockly/core";
2 | import type { BlockDefinition } from "blockly/core/blocks";
3 | import type { BlockSvg } from "blockly/core/block_svg";
4 |
5 | import { createPlusField } from "./fields/field_plus";
6 | import { createMinusField } from "./fields/field_minus";
7 |
8 | export const definitions: BlockDefinition[] = [
9 | {
10 | type: "nonebot_on_alconna",
11 | tooltip: "",
12 | helpUrl: "",
13 | message0:
14 | "跨平台命令解析与处理 %1 命令字符串 %2 仅与我相关 %3 %4 无其他命令参数 %5 %6",
15 | args0: [
16 | {
17 | type: "input_dummy",
18 | name: "NAME",
19 | },
20 | {
21 | type: "field_input",
22 | name: "COMMAND",
23 | text: "hello",
24 | },
25 | {
26 | type: "field_checkbox",
27 | name: "TOME",
28 | checked: "FALSE",
29 | },
30 | {
31 | type: "input_dummy",
32 | name: "PARAMS",
33 | },
34 | {
35 | type: "input_dummy",
36 | name: "EMPTY",
37 | align: "RIGHT",
38 | },
39 | {
40 | type: "input_statement",
41 | name: "HANDLE",
42 | },
43 | ],
44 | colour: 90,
45 | mutator: "alconna_mutator",
46 | },
47 | {
48 | type: "alconna_const",
49 | tooltip: "",
50 | helpUrl: "",
51 | message0: "固定字符串 %1 %2",
52 | args0: [
53 | {
54 | type: "field_input",
55 | name: "TEXT",
56 | text: "help",
57 | },
58 | {
59 | type: "input_dummy",
60 | name: "PARAMS",
61 | },
62 | ],
63 | output: "arg",
64 | colour: 120,
65 | },
66 | {
67 | type: "alconna_arg",
68 | tooltip: "",
69 | helpUrl: "",
70 | message0: "参数名 %1 数据类型 %2 %3",
71 | args0: [
72 | {
73 | type: "field_input",
74 | name: "NAME",
75 | text: "arg1",
76 | },
77 | {
78 | type: "field_dropdown",
79 | name: "TYPE",
80 | options: [
81 | ["字符串", "str"],
82 | ["整数", "int"],
83 | ["浮点数", "float"],
84 | ["布尔值", "bool"],
85 | ],
86 | },
87 | {
88 | type: "input_dummy",
89 | name: "PARAMS",
90 | },
91 | ],
92 | output: "arg",
93 | colour: 120,
94 | },
95 | {
96 | type: "alconna_arg_get",
97 | tooltip: "",
98 | helpUrl: "",
99 | message0: "获取参数 %1 %2",
100 | args0: [
101 | {
102 | type: "field_dropdown",
103 | name: "NAME",
104 | options: [["-", ""]],
105 | },
106 | {
107 | type: "input_dummy",
108 | name: "PARAMS",
109 | },
110 | ],
111 | output: null,
112 | colour: 120,
113 | mutator: "alconna_arg_get_mutator",
114 | },
115 | ];
116 |
117 | /**
118 | * Type of a 'nonebot_on_alconna' block.
119 | *
120 | * @internal
121 | */
122 | export type AlconnaBlock = BlockSvg & AlconnaMixin;
123 | interface AlconnaMixin extends AlconnaMixinType {
124 | itemCount_: number;
125 | topInput_: Blockly.Input | undefined;
126 | }
127 | type AlconnaMixinType = typeof ALCONNA;
128 |
129 | const ALCONNA = {
130 | /**
131 | * Number of item inputs the block has.
132 | * @type {number}
133 | */
134 | itemCount_: 0,
135 |
136 | /**
137 | * Creates XML to represent number of text inputs.
138 | * @returns {!Element} XML storage element.
139 | * @this {Blockly.Block}
140 | */
141 | mutationToDom: function (this: AlconnaBlock): Element {
142 | const container = Blockly.utils.xml.createElement("mutation");
143 | container.setAttribute("items", String(this.itemCount_));
144 | return container;
145 | },
146 | /**
147 | * Parses XML to restore the text inputs.
148 | * @param {!Element} xmlElement XML storage element.
149 | * @this {Blockly.Block}
150 | */
151 | domToMutation: function (this: AlconnaBlock, xmlElement: Element) {
152 | const items = xmlElement.getAttribute("items");
153 | if (!items) throw new TypeError("element did not have items");
154 | this.itemCount_ = parseInt(items, 10);
155 | this.updateShape_();
156 | },
157 |
158 | /**
159 | * Returns the state of this block as a JSON serializable object.
160 | * @returns {{itemCount: number}} The state of this block, ie the item count.
161 | */
162 | saveExtraState: function (this: AlconnaBlock): { itemCount: number } {
163 | return {
164 | itemCount: this.itemCount_,
165 | };
166 | },
167 |
168 | /**
169 | * Applies the given state to this block.
170 | * @param {*} state The state to apply to this block, ie the item count.
171 | */
172 | loadExtraState: function (this: AlconnaBlock, state: any) {
173 | const count = state["itemCount"];
174 | while (this.itemCount_ < count) {
175 | this.addPart_();
176 | }
177 | this.updateShape_();
178 | },
179 |
180 | /**
181 | * Adds inputs to the block until it reaches the target number of inputs.
182 | * @this {Blockly.Block}
183 | * @private
184 | */
185 | updateShape_: function (this: AlconnaBlock) {
186 | this.updateMinus_();
187 | },
188 |
189 | /**
190 | * Callback for the plus image. Adds an input to the end of the block and
191 | * updates the state of the minus.
192 | */
193 | plus: function (this: AlconnaBlock) {
194 | this.addPart_();
195 | this.updateMinus_();
196 | },
197 |
198 | /**
199 | * Callback for the minus image. Removes an input from the end of the block
200 | * and updates the state of the minus.
201 | */
202 | minus: function (this: AlconnaBlock) {
203 | if (this.itemCount_ == 0) {
204 | return;
205 | }
206 | this.removePart_();
207 | this.updateMinus_();
208 | },
209 |
210 | // To properly keep track of indices we have to increment before/after adding
211 | // the inputs, and decrement the opposite.
212 | // Because we want our first input to be ARG0 (not ARG1) we increment after.
213 |
214 | /**
215 | * Adds an input to the end of the block. If the block currently has no
216 | * inputs it updates the top 'EMPTY' input to receive a block.
217 | * @this {Blockly.Block}
218 | * @private
219 | */
220 | addPart_: function (this: AlconnaBlock) {
221 | const connection = (this.getInput("HANDLE") as Blockly.Input).connection
222 | ?.targetConnection;
223 | this.removeInput("HANDLE");
224 | if (this.itemCount_ == 0) {
225 | this.removeInput("EMPTY");
226 | this.topInput_ = this.appendValueInput("ARG" + String(this.itemCount_))
227 | .setAlign(Blockly.inputs.Align.RIGHT)
228 | .setCheck("arg")
229 | .appendField(createPlusField(), "PLUS")
230 | .appendField(`参数 ${this.itemCount_}`);
231 | } else {
232 | this.appendValueInput("ARG" + this.itemCount_)
233 | .setAlign(Blockly.inputs.Align.RIGHT)
234 | .setCheck("arg")
235 | .appendField(`参数 ${this.itemCount_}`);
236 | }
237 | this.itemCount_++;
238 | this.appendStatementInput("HANDLE");
239 | connection?.reconnect(this, "HANDLE");
240 | },
241 |
242 | /**
243 | * Removes an input from the end of the block. If we are removing the last
244 | * input this updates the block to have an 'EMPTY' top input.
245 | * @this {Blockly.Block}
246 | * @private
247 | */
248 | removePart_: function (this: AlconnaBlock) {
249 | this.itemCount_--;
250 | this.removeInput("ARG" + String(this.itemCount_));
251 | if (this.itemCount_ == 0) {
252 | (this.topInput_ as Blockly.Input) = this.appendDummyInput("EMPTY")
253 | .appendField(createPlusField(), "PLUS")
254 | .setAlign(Blockly.inputs.Align.RIGHT)
255 | .appendField("无其他命令参数");
256 | const connection = (this.getInput("HANDLE") as Blockly.Input).connection
257 | ?.targetConnection;
258 | this.removeInput("HANDLE");
259 | this.appendStatementInput("HANDLE");
260 | connection?.reconnect(this, "HANDLE");
261 | }
262 | },
263 |
264 | /**
265 | * Makes it so the minus is visible iff there is an input available to remove.
266 | * @private
267 | */
268 | updateMinus_: function (this: AlconnaBlock) {
269 | const minusField = this.getField("MINUS");
270 | if (!minusField && this.itemCount_ > 0) {
271 | this.topInput_?.insertFieldAt(1, createMinusField(), "MINUS");
272 | } else if (minusField && this.itemCount_ < 1) {
273 | this.topInput_?.removeField("MINUS");
274 | }
275 | },
276 | };
277 |
278 | /**
279 | * Type of a 'nonebot_on_alconna' block.
280 | *
281 | * @internal
282 | */
283 | export type AlconnaArgGetBlock = BlockSvg & AlconnaArgGetMixin;
284 | interface AlconnaArgGetMixin extends AlconnaArgGetMixinType {
285 | name_: string;
286 | isInitialized_: boolean;
287 | }
288 | type AlconnaArgGetMixinType = typeof ALCONNA_ARG_GET;
289 |
290 | const ALCONNA_ARG_GET = {
291 | name_: "",
292 | isInitialized_: false,
293 |
294 | /**
295 | * Returns the state of this block as a JSON serializable object.
296 | */
297 | saveExtraState: function (this: AlconnaArgGetBlock): { name: string } {
298 | return { name: this.name_ };
299 | },
300 |
301 | /**
302 | * Applies the given state to this block.
303 | * @param {*} state The state to apply to this block, ie the item count.
304 | */
305 | loadExtraState: function (this: AlconnaArgGetBlock, state: any) {
306 | const name = state["name"];
307 | this.name_ = name;
308 | },
309 | };
310 |
311 | /**
312 | * Updates the shape of the block to have 0 inputs if no mutation is provided.
313 | * @this {Blockly.Block}
314 | */
315 | const ALCONNA_EXTENSION = function (this: AlconnaBlock) {
316 | this.itemCount_ = 0;
317 | this.updateShape_();
318 | this.getInput("EMPTY")?.insertFieldAt(0, createPlusField(), "PLUS");
319 | };
320 |
321 | if (Blockly.Extensions.isRegistered("alconna_mutator")) {
322 | Blockly.Extensions.unregister("alconna_mutator");
323 | }
324 | Blockly.Extensions.registerMutator(
325 | "alconna_mutator",
326 | ALCONNA,
327 | ALCONNA_EXTENSION,
328 | );
329 |
330 | if (Blockly.Extensions.isRegistered("alconna_arg_get_mutator")) {
331 | Blockly.Extensions.unregister("alconna_arg_get_mutator");
332 | }
333 | Blockly.Extensions.registerMutator("alconna_arg_get_mutator", ALCONNA_ARG_GET);
334 |
--------------------------------------------------------------------------------
/packages/app/src/workspace.ts:
--------------------------------------------------------------------------------
1 | // outputs.ts
2 | import { reactive, ref } from "vue";
3 | import * as Blockly from "blockly";
4 | import { pythonGenerator } from "blockly/python";
5 | import { themeLight, themeDark } from "@/theme";
6 |
7 | import JSZip from "jszip";
8 | import { saveAs } from "file-saver";
9 |
10 | const version = "v1";
11 |
12 | export const workspaceStore = reactive({
13 | workspace: ref(),
14 | demoProject: ref(),
15 | });
16 |
17 | export const optionsStore = reactive({
18 | toolbox: ref(),
19 | theme: ref(),
20 | collapse: false,
21 | comments: false,
22 | disable: false,
23 | maxBlocks: Infinity,
24 | trashcan: true,
25 | horizontalLayout: false,
26 | toolboxPosition: "start",
27 | css: true,
28 | media: "https://blockly-demo.appspot.com/static/media/",
29 | rtl: false,
30 | scrollbars: true,
31 | sounds: false,
32 | oneBasedIndex: true,
33 | grid: {
34 | spacing: 20,
35 | length: 1,
36 | colour: "#888",
37 | snap: true,
38 | },
39 | zoom: {
40 | controls: true,
41 | wheel: false,
42 | startScale: 1,
43 | maxScale: 3,
44 | minScale: 0.3,
45 | scaleSpeed: 1.2,
46 | },
47 | renderer: "geras",
48 | });
49 |
50 | export const outputsStore = reactive({
51 | code: "" as string,
52 | activeTab: ref("tab-0"),
53 | snackbar: false,
54 | snackbarMsg: "" as string,
55 | snackbarTimeout: 2500 as number,
56 | snackbarColor: "green" as string,
57 | });
58 |
59 | export const exportConfig = reactive({
60 | name: "app" as string,
61 | preset: { name: "console", description: "控制台机器人" },
62 | port: 8080 as number,
63 | platform: ["windows", "linux"] as string[],
64 | commandStart: ["/"] as string[],
65 | superusers: [] as string[],
66 | kwargs: {} as Record,
67 | });
68 |
69 | const windowsScripts = {
70 | install: `\
71 | # Step 1: Check if 'uv' is installed
72 | $uvVersion = try {
73 | uv --version
74 | } catch {
75 | $null
76 | }
77 |
78 | if ($uvVersion) {
79 | Write-Host "UV is installed. Version: "
80 | Write-Host $uvVersion
81 | } else {
82 | # Step 2: If 'uv' is not installed, ask user for confirmation to install
83 | Write-Host "UV is not installed on this system."
84 | $confirmation = Read-Host "Do you want to install UV? (Press Enter to confirm or type 'n' to cancel)"
85 |
86 | if ($confirmation -eq '') {
87 | Write-Host "Installing UV..."
88 | Invoke-RestMethod https://astral.sh/uv/install.ps1 | Invoke-Expression
89 | Write-Host "UV has been installed successfully."
90 | } else {
91 | Write-Host "Installation canceled."
92 | exit
93 | }
94 | }
95 |
96 | # Step 3: Create a Python virtual environment
97 | Write-Host "Creating a Python virtual environment with Python 3.12..."
98 | uv venv --python 3.12
99 |
100 | Write-Host "Python virtual environment created successfully."
101 |
102 | # Step 4: Install dependencies
103 | uv pip install -r pyproject.toml`,
104 | run: `\
105 | $uvVersion = try {
106 | uv --version
107 | } catch {
108 | $null
109 | }
110 |
111 | if ($null -eq $uvVersion) {
112 | Write-Host "Please run 'install.ps1' first."
113 | exit
114 | }
115 |
116 | uv run nb run`,
117 | };
118 |
119 | const linuxScripts = {
120 | install: `\
121 | #!/bin/bash
122 |
123 | # Step 1: Check if 'uv' is installed
124 | if command -v uv &> /dev/null
125 | then
126 | echo "UV is installed. Version info:"
127 | uv --version
128 | else
129 | # Step 2: If 'uv' is not installed, ask user for confirmation to install
130 | echo "UV is not installed on this system."
131 | read -p "Do you want to install UV? (Press Enter to confirm or type 'n' to cancel): " confirmation
132 |
133 | if [ "$confirmation" == "" ]; then
134 | echo "Installing UV..."
135 | curl -LsSf https://astral.sh/uv/install.sh | sh
136 | echo "UV has been installed successfully."
137 | else
138 | echo "Installation canceled."
139 | exit 1
140 | fi
141 | fi
142 |
143 | # Step 3: Create a Python virtual environment
144 | echo "Creating a Python virtual environment with Python 3.12..."
145 | uv venv --python 3.12
146 |
147 | echo "Python virtual environment created successfully."`,
148 | run: `\
149 | if ! command -v uv &> /dev/null
150 | then
151 | echo "Please run 'install.sh' first"
152 | exit
153 | fi
154 |
155 | uv run nb run`,
156 | };
157 |
158 | export function setWorkspaceTheme(theme: string) {
159 | let workspace = Blockly.getMainWorkspace();
160 | if (theme === "LightTheme") {
161 | optionsStore.theme = ref(themeLight);
162 | // @ts-ignore
163 | workspace.setTheme(themeLight);
164 | } else if (theme === "DarkTheme") {
165 | optionsStore.theme = ref(themeDark);
166 | // @ts-ignore
167 | workspace.setTheme(themeDark);
168 | }
169 | }
170 |
171 | export function saveJson() {
172 | const workspace = Blockly.getMainWorkspace();
173 | const data = Blockly.serialization.workspaces.save(workspace);
174 | const project = JSON.stringify({
175 | version: version,
176 | data: data,
177 | config: exportConfig,
178 | });
179 | localStorage.setItem("NoneBlockly", project);
180 | outputsStore.snackbarColor = "green";
181 | outputsStore.snackbarMsg = "🤗 工作区已暂存";
182 | outputsStore.snackbar = true;
183 | }
184 |
185 | export function loadJson() {
186 | const workspace = Blockly.getMainWorkspace();
187 | const savedData = localStorage.getItem("NoneBlockly");
188 | if (savedData) {
189 | const project = JSON.parse(savedData);
190 | if (project.version === version) {
191 | Blockly.serialization.workspaces.load(project.data, workspace);
192 | Object.assign(exportConfig, project.config);
193 | outputsStore.snackbarColor = "green";
194 | outputsStore.snackbarMsg = "🥰 已恢复暂存工作区";
195 | outputsStore.snackbar = true;
196 | } else {
197 | initWorkspaceState();
198 | }
199 | } else {
200 | outputsStore.snackbarColor = "warning";
201 | outputsStore.snackbarMsg = "未找到暂存工作区,将导入默认工作区";
202 | outputsStore.snackbar = true;
203 | initWorkspaceState();
204 | }
205 | }
206 |
207 | export function initWorkspaceState() {
208 | const workspace = Blockly.getMainWorkspace();
209 | const demoProject = workspaceStore.demoProject;
210 | Blockly.serialization.workspaces.load(demoProject.data, workspace);
211 | Object.assign(exportConfig, demoProject.config);
212 | outputsStore.snackbarColor = "green";
213 | outputsStore.snackbarMsg = "🥰 已加载工程示例";
214 | outputsStore.snackbar = true;
215 | outputsStore.activeTab = "tab-0";
216 | }
217 |
218 | export function generateCode() {
219 | let workspace = Blockly.getMainWorkspace();
220 | outputsStore.code = pythonGenerator.workspaceToCode(workspace);
221 | }
222 |
223 | export function exportPress() {
224 | outputsStore.activeTab = "tab-3";
225 | }
226 |
227 | function generatePyproject(code: string, preset: string) {
228 | const dependencies = new Set([
229 | "nonebot2[fastapi,httpx,websockets]>=2.3.3",
230 | "nb-cli>=1.4.2",
231 | ]);
232 | const importLines = code.split("\n\n")[0].split("\n");
233 | importLines.forEach((line) => {
234 | if (line.startsWith("from nonebot_plugin_alconna")) {
235 | dependencies.add("nonebot-plugin-alconna>=0.52.3");
236 | } else if (line.startsWith("from nonebot_plugin_apscheduler")) {
237 | dependencies.add("nonebot-plugin-apscheduler>=0.5.0");
238 | } else if (line.startsWith("import nonebot_plugin_localstore")) {
239 | dependencies.add("nonebot-plugin-localstore>=0.7.1");
240 | }
241 | });
242 | let adapters = "";
243 | if (preset === "console") {
244 | adapters = '{ name = "Console", module_name = "nonebot.adapters.console" }';
245 | dependencies.add("nonebot-adapter-console>=0.6.0");
246 | } else if (preset === "onebot") {
247 | adapters =
248 | '{ name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" }';
249 | adapters +=
250 | ', { name = "OneBot V12", module_name = "nonebot.adapters.onebot.v12" }';
251 | dependencies.add("nonebot-adapter-onebot>=2.4.5");
252 | }
253 | return `\
254 | [project]
255 | name = "noneblockly-app"
256 | version = "0.1.0"
257 | description = "NoneBot project generated by NoneBlockly"
258 | authors = [{name = "name", email = "name@example.com"}]
259 | dependencies = [
260 | ${Array.from(dependencies)
261 | .map((dep) => `"${dep}"`)
262 | .join(", \n")}
263 | ]
264 | requires-python = ">=3.9"
265 | license = {text = "MIT"}
266 |
267 | [tool.nonebot]
268 | adapters = [
269 | ${adapters}
270 | ]
271 | plugin_dirs = ["plugins"]`;
272 | }
273 |
274 | function generateInit(code: string) {
275 | const plugins = new Set();
276 | const importLines = code.split("\n\n")[0].split("\n");
277 | importLines.forEach((line) => {
278 | if (line.startsWith("from nonebot_plugin_alconna")) {
279 | plugins.add("nonebot_plugin_alconna");
280 | } else if (line.startsWith("from nonebot_plugin_apscheduler")) {
281 | plugins.add("nonebot_plugin_apscheduler");
282 | } else if (line.startsWith("import nonebot_plugin_localstore")) {
283 | plugins.add("nonebot_plugin_localstore");
284 | }
285 | });
286 | return `\
287 | from nonebot import require
288 | ${Array.from(plugins)
289 | .map((plugin) => `require("${plugin}")`)
290 | .join("\n")}
291 | from .app import * # noqa`;
292 | }
293 |
294 | function generateEnv(config: typeof exportConfig) {
295 | let commandStart = "";
296 | let superusers = "";
297 | if (config.commandStart.length > 0) {
298 | // quote strings
299 | const items = config.commandStart.map((item) => JSON.stringify(item));
300 | commandStart = `COMMAND_START=[${items.join(",")}]\n`;
301 | }
302 | if (config.superusers.length > 0) {
303 | const items = config.superusers.map((item) => JSON.stringify(item));
304 | superusers = `SUPERUSERS=[${items.join(",")}]\n`;
305 | }
306 | return `\
307 | DRIVER=~fastapi+~httpx+~websockets
308 | PORT=${config.port || 8080}
309 | ${commandStart}${superusers}`;
310 | }
311 |
312 | export function exportProject() {
313 | const workspace = Blockly.getMainWorkspace();
314 | const data = Blockly.serialization.workspaces.save(workspace);
315 | const project = JSON.stringify({
316 | version: version,
317 | data: data,
318 | config: exportConfig,
319 | });
320 | const blob = new Blob([project], { type: "application/json" });
321 | const jsonObjectUrl = URL.createObjectURL(blob);
322 | const filename = `${exportConfig.name}.json`;
323 | const anchor = document.createElement("a");
324 | anchor.href = jsonObjectUrl;
325 | anchor.download = filename;
326 | anchor.click();
327 | URL.revokeObjectURL(jsonObjectUrl);
328 | outputsStore.snackbarColor = "green";
329 | outputsStore.snackbarMsg = "🤗 已导出设计文件";
330 | outputsStore.snackbar = true;
331 | }
332 |
333 | export function importProject(event: any) {
334 | const file = event.target.files[0];
335 | const reader = new FileReader();
336 | reader.onload = function (e) {
337 | const project = JSON.parse(e.target?.result as string);
338 | if (project.version === version) {
339 | Blockly.getMainWorkspace().clear();
340 | Blockly.serialization.workspaces.load(
341 | project.data,
342 | Blockly.getMainWorkspace(),
343 | );
344 | Object.assign(exportConfig, project.config);
345 | outputsStore.snackbarColor = "green";
346 | outputsStore.snackbarMsg = "🥰 已导入设计文件";
347 | outputsStore.snackbar = true;
348 | } else {
349 | outputsStore.snackbarColor = "warning";
350 | outputsStore.snackbarMsg = "❌ 无法识别选择的文件";
351 | outputsStore.snackbar = true;
352 | }
353 | };
354 | reader.readAsText(file);
355 | }
356 |
357 | export function exportZip() {
358 | let zip = new JSZip();
359 | const workspace = Blockly.getMainWorkspace();
360 | const code = pythonGenerator.workspaceToCode(workspace);
361 | const config = exportConfig;
362 | console.log(config);
363 | zip.file(`plugins/plugin_${config.name}/__init__.py`, generateInit(code));
364 | zip.file(`plugins/plugin_${config.name}/app.py`, code);
365 | zip.file("pyproject.toml", generatePyproject(code, config.preset.name));
366 | zip.file(".env.prod", generateEnv(config));
367 | config.platform.forEach((platform) => {
368 | if (platform === "windows") {
369 | zip.file("install.ps1", windowsScripts.install);
370 | zip.file("run.ps1", windowsScripts.run);
371 | } else if (platform === "linux") {
372 | zip.file("install.sh", linuxScripts.install);
373 | zip.file("run.sh", linuxScripts.run);
374 | }
375 | });
376 | outputsStore.snackbarColor = "green";
377 | outputsStore.snackbarMsg = "😎 已导出 Python 项目";
378 | outputsStore.snackbar = true;
379 | zip.generateAsync({ type: "blob" }).then(function (content) {
380 | saveAs(content, `${config.name}.zip`);
381 | });
382 | }
383 |
--------------------------------------------------------------------------------
/packages/app/src/toolbox.ts:
--------------------------------------------------------------------------------
1 | export const toolbox = {
2 | kind: "categoryToolbox",
3 | contents: [
4 | {
5 | kind: "CATEGORY",
6 | contents: [
7 | {
8 | kind: "BLOCK",
9 | type: "controls_if",
10 | },
11 | {
12 | kind: "BLOCK",
13 | type: "logic_compare",
14 | },
15 | {
16 | kind: "BLOCK",
17 | type: "logic_operation",
18 | },
19 | {
20 | kind: "BLOCK",
21 | type: "logic_negate",
22 | },
23 | {
24 | kind: "BLOCK",
25 | type: "logic_boolean",
26 | },
27 | {
28 | kind: "BLOCK",
29 | type: "logic_null",
30 | },
31 | {
32 | kind: "BLOCK",
33 | type: "logic_ternary",
34 | },
35 | ],
36 | name: "逻辑",
37 | categorystyle: "logic_category",
38 | },
39 | {
40 | kind: "CATEGORY",
41 | contents: [
42 | {
43 | kind: "BLOCK",
44 | type: "controls_repeat_ext",
45 | },
46 | {
47 | kind: "BLOCK",
48 | type: "controls_repeat",
49 | },
50 | {
51 | kind: "BLOCK",
52 | type: "controls_whileUntil",
53 | },
54 | {
55 | kind: "BLOCK",
56 | type: "controls_for",
57 | },
58 | {
59 | kind: "BLOCK",
60 | type: "controls_forEach",
61 | },
62 | {
63 | kind: "BLOCK",
64 | type: "controls_flow_statements",
65 | },
66 | ],
67 | name: "循环",
68 | categorystyle: "loop_category",
69 | },
70 | {
71 | kind: "CATEGORY",
72 | contents: [
73 | {
74 | kind: "BLOCK",
75 | type: "math_number",
76 | gap: "32",
77 | },
78 | {
79 | kind: "BLOCK",
80 | type: "math_arithmetic",
81 | },
82 | {
83 | kind: "BLOCK",
84 | type: "math_single",
85 | },
86 | {
87 | kind: "BLOCK",
88 | type: "math_trig",
89 | },
90 | {
91 | kind: "BLOCK",
92 | type: "math_constant",
93 | },
94 | {
95 | kind: "BLOCK",
96 | type: "math_number_property",
97 | },
98 | {
99 | kind: "BLOCK",
100 | type: "math_round",
101 | },
102 | {
103 | kind: "BLOCK",
104 | type: "math_on_list",
105 | },
106 | {
107 | kind: "BLOCK",
108 | type: "math_modulo",
109 | },
110 | {
111 | kind: "BLOCK",
112 | type: "math_constrain",
113 | },
114 | {
115 | kind: "BLOCK",
116 | type: "math_random_int",
117 | },
118 | {
119 | kind: "BLOCK",
120 | type: "math_random_float",
121 | },
122 | {
123 | kind: "BLOCK",
124 | type: "math_atan2",
125 | },
126 | ],
127 | name: "数学",
128 | categorystyle: "math_category",
129 | },
130 | {
131 | kind: "CATEGORY",
132 | contents: [
133 | {
134 | kind: "BLOCK",
135 | type: "text",
136 | },
137 | {
138 | kind: "BLOCK",
139 | type: "text_join",
140 | },
141 | {
142 | kind: "BLOCK",
143 | type: "text_append",
144 | inputs: {
145 | TEXT: {
146 | shadow: {
147 | type: "text",
148 | fields: {
149 | TEXT: "",
150 | },
151 | },
152 | },
153 | },
154 | },
155 | {
156 | kind: "BLOCK",
157 | type: "text_length",
158 | inputs: {
159 | VALUE: {
160 | shadow: {
161 | type: "text",
162 | fields: {
163 | TEXT: "abc",
164 | },
165 | },
166 | },
167 | },
168 | },
169 | {
170 | kind: "BLOCK",
171 | type: "text_isEmpty",
172 | inputs: {
173 | VALUE: {
174 | shadow: {
175 | type: "text",
176 | fields: {
177 | TEXT: "",
178 | },
179 | },
180 | },
181 | },
182 | },
183 | {
184 | kind: "BLOCK",
185 | type: "text_indexOf",
186 | inputs: {
187 | VALUE: {
188 | BLOCK: {
189 | type: "variables_get",
190 | },
191 | },
192 | FIND: {
193 | shadow: {
194 | type: "text",
195 | fields: {
196 | TEXT: "abc",
197 | },
198 | },
199 | },
200 | },
201 | },
202 | {
203 | kind: "BLOCK",
204 | type: "text_charAt",
205 | inputs: {
206 | VALUE: {
207 | BLOCK: {
208 | type: "variables_get",
209 | },
210 | },
211 | },
212 | },
213 | {
214 | kind: "BLOCK",
215 | type: "text_getSubstring",
216 | inputs: {
217 | STRING: {
218 | BLOCK: {
219 | type: "variables_get",
220 | },
221 | },
222 | },
223 | },
224 | {
225 | kind: "BLOCK",
226 | type: "text_changeCase",
227 | inputs: {
228 | TEXT: {
229 | shadow: {
230 | type: "text",
231 | fields: {
232 | TEXT: "abc",
233 | },
234 | },
235 | },
236 | },
237 | },
238 | {
239 | kind: "BLOCK",
240 | type: "text_trim",
241 | inputs: {
242 | TEXT: {
243 | shadow: {
244 | type: "text",
245 | fields: {
246 | TEXT: "abc",
247 | },
248 | },
249 | },
250 | },
251 | },
252 | {
253 | kind: "BLOCK",
254 | type: "text_count",
255 | inputs: {
256 | SUB: {
257 | shadow: {
258 | type: "text",
259 | },
260 | },
261 | TEXT: {
262 | shadow: {
263 | type: "text",
264 | },
265 | },
266 | },
267 | },
268 | {
269 | kind: "BLOCK",
270 | type: "text_replace",
271 | inputs: {
272 | FROM: {
273 | shadow: {
274 | type: "text",
275 | },
276 | },
277 | TO: {
278 | shadow: {
279 | type: "text",
280 | },
281 | },
282 | TEXT: {
283 | shadow: {
284 | type: "text",
285 | },
286 | },
287 | },
288 | },
289 | {
290 | kind: "BLOCK",
291 | type: "text_reverse",
292 | inputs: {
293 | TEXT: {
294 | shadow: {
295 | type: "text",
296 | },
297 | },
298 | },
299 | },
300 | ],
301 | name: "文本",
302 | categorystyle: "text_category",
303 | },
304 | {
305 | kind: "CATEGORY",
306 | contents: [
307 | {
308 | kind: "BLOCK",
309 | type: "lists_create_with",
310 | },
311 | {
312 | kind: "BLOCK",
313 | type: "lists_repeat",
314 | },
315 | {
316 | kind: "BLOCK",
317 | type: "lists_length",
318 | },
319 | {
320 | kind: "BLOCK",
321 | type: "lists_isEmpty",
322 | },
323 | {
324 | kind: "BLOCK",
325 | type: "lists_indexOf",
326 | },
327 | {
328 | kind: "BLOCK",
329 | type: "lists_getIndex",
330 | },
331 | {
332 | kind: "BLOCK",
333 | type: "lists_setIndex",
334 | },
335 | {
336 | kind: "BLOCK",
337 | type: "lists_getSublist",
338 | },
339 | {
340 | kind: "BLOCK",
341 | type: "lists_split",
342 | },
343 | {
344 | kind: "BLOCK",
345 | type: "lists_sort",
346 | },
347 | {
348 | kind: "BLOCK",
349 | type: "lists_reverse",
350 | },
351 | ],
352 | name: "列表",
353 | categorystyle: "list_category",
354 | },
355 | {
356 | kind: "CATEGORY",
357 | contents: [
358 | {
359 | kind: "BLOCK",
360 | type: "dicts_create_with",
361 | },
362 | {
363 | kind: "BLOCK",
364 | type: "dicts_get",
365 | },
366 | {
367 | kind: "BLOCK",
368 | type: "dicts_get_multi",
369 | },
370 | {
371 | kind: "BLOCK",
372 | type: "dicts_set",
373 | },
374 | ],
375 | name: "字典",
376 | colour: "0",
377 | },
378 | {
379 | kind: "SEP",
380 | },
381 | {
382 | kind: "CATEGORY",
383 | contents: [
384 | {
385 | kind: "LABEL",
386 | text: "“消息处理”块会处理所有收到的消息",
387 | },
388 | {
389 | kind: "BLOCK",
390 | type: "nonebot_on_message",
391 | },
392 | {
393 | kind: "LABEL",
394 | text: "“命令处理”块只处理以指定前缀和命令起始的消息",
395 | },
396 | {
397 | kind: "BLOCK",
398 | type: "nonebot_on_command",
399 | },
400 | {
401 | kind: "LABEL",
402 | text: "以下参数或方法仅能在“消息处理”或“命令处理”语句中使用",
403 | },
404 | {
405 | kind: "BLOCK",
406 | type: "nonebot_param_text",
407 | },
408 | {
409 | kind: "BLOCK",
410 | type: "nonebot_send",
411 | },
412 | ],
413 | name: "通用消息处理",
414 | colour: "0",
415 | },
416 | {
417 | kind: "CATEGORY",
418 | contents: [
419 | {
420 | kind: "LABEL",
421 | text: "“跨平台命令解析与处理”块提供更加灵活的跨平台命令解析支持",
422 | },
423 | {
424 | kind: "BLOCK",
425 | type: "nonebot_on_alconna",
426 | },
427 | {
428 | kind: "LABEL",
429 | text: "以下命令参数只能填充到“跨平台命令解析与处理”中对应位置",
430 | },
431 | {
432 | kind: "BLOCK",
433 | type: "alconna_const",
434 | },
435 | {
436 | kind: "BLOCK",
437 | type: "alconna_arg",
438 | },
439 | {
440 | kind: "LABEL",
441 | text: "以下参数或方法仅能在“跨平台命令解析与处理”语句中使用",
442 | },
443 | {
444 | kind: "BLOCK",
445 | type: "alconna_arg_get",
446 | },
447 | {
448 | kind: "BLOCK",
449 | type: "nonebot_send",
450 | },
451 | ],
452 | name: "跨平台命令处理",
453 | colour: "90",
454 | },
455 | {
456 | kind: "CATEGORY",
457 | contents: [
458 | {
459 | kind: "BLOCK",
460 | type: "store_save_json",
461 | },
462 | {
463 | kind: "BLOCK",
464 | type: "store_load_json",
465 | },
466 | {
467 | kind: "BLOCK",
468 | type: "store_save_text",
469 | },
470 | {
471 | kind: "BLOCK",
472 | type: "store_load_text",
473 | },
474 | ],
475 | name: "文件存储",
476 | colour: "150",
477 | },
478 | {
479 | kind: "CATEGORY",
480 | contents: [
481 | {
482 | kind: "BLOCK",
483 | type: "scheduler_add",
484 | },
485 | {
486 | kind: "BLOCK",
487 | type: "scheduler_remove",
488 | },
489 | {
490 | kind: "LABEL",
491 | text: "请将以下时间配置块填入定时任务创建语句中使用",
492 | },
493 | {
494 | kind: "BLOCK",
495 | type: "scheduler_time_interval",
496 | },
497 | {
498 | kind: "BLOCK",
499 | type: "scheduler_time_cron_daily",
500 | },
501 | {
502 | kind: "BLOCK",
503 | type: "scheduler_time_cron",
504 | },
505 | ],
506 | name: "定时任务",
507 | colour: "210",
508 | },
509 | {
510 | kind: "CATEGORY",
511 | contents: [
512 | {
513 | kind: "BLOCK",
514 | type: "request_get",
515 | },
516 | {
517 | kind: "BLOCK",
518 | type: "request_post",
519 | },
520 | ],
521 | name: "网络请求",
522 | colour: "270",
523 | },
524 | {
525 | kind: "SEP",
526 | },
527 | {
528 | kind: "CATEGORY",
529 | name: "变量",
530 | categorystyle: "variable_category",
531 | custom: "VARIABLE",
532 | },
533 | // {
534 | // kind: "CATEGORY",
535 | // name: "函数",
536 | // categorystyle: "procedure_category",
537 | // custom: "PROCEDURE",
538 | // },
539 | ],
540 | };
541 |
--------------------------------------------------------------------------------
/packages/app/src/blocks/python_dict.ts:
--------------------------------------------------------------------------------
1 | import * as Blockly from "blockly/core";
2 | import type { BlockDefinition } from "blockly/core/blocks";
3 | import type { BlockSvg } from "blockly/core/block_svg";
4 |
5 | import { createPlusField } from "./fields/field_plus";
6 | import { createMinusField } from "./fields/field_minus";
7 |
8 | export const pythonDict: BlockDefinition[] = [
9 | {
10 | type: "dicts_create_with",
11 | message0: "创建字典 %1 空字典 %2",
12 | args0: [
13 | {
14 | type: "input_dummy",
15 | },
16 | {
17 | type: "input_dummy",
18 | name: "EMPTY",
19 | align: "RIGHT",
20 | },
21 | ],
22 | output: "dict",
23 | tooltip: "",
24 | helpUrl: "",
25 | mutator: "dict_create_with_mutator",
26 | colour: 0,
27 | // style: "dict_blocks",
28 | },
29 | {
30 | type: "dicts_get",
31 | message0: "从字典 %1 中取键 %2 的值",
32 | args0: [
33 | {
34 | type: "input_value",
35 | name: "DICT",
36 | check: "dict",
37 | },
38 | {
39 | type: "field_input",
40 | name: "KEY",
41 | text: "key",
42 | },
43 | ],
44 | output: null,
45 | inputsInline: true,
46 | tooltip: "",
47 | helpUrl: "",
48 | colour: 0,
49 | // style: "dict_blocks",
50 | },
51 | {
52 | type: "dicts_get_multi",
53 | message0: "从字典 %1 连续取值 %2 %3",
54 | args0: [
55 | {
56 | type: "input_value",
57 | name: "DICT",
58 | check: "dict",
59 | },
60 | {
61 | type: "input_dummy",
62 | name: "TOOLS",
63 | },
64 | {
65 | type: "input_dummy",
66 | name: "KEYS",
67 | },
68 | ],
69 | output: null,
70 | inputsInline: true,
71 | tooltip: "",
72 | helpUrl: "",
73 | mutator: "dict_get_multi_mutator",
74 | colour: 0,
75 | // style: "dict_blocks",
76 | },
77 | {
78 | type: "dicts_set",
79 | message0: "设置字典 %1 键 %2 的值为 %3",
80 | args0: [
81 | {
82 | type: "input_value",
83 | name: "DICT",
84 | check: "dict",
85 | },
86 | {
87 | type: "field_input",
88 | name: "KEY",
89 | text: "key",
90 | },
91 | {
92 | type: "input_value",
93 | name: "VALUE",
94 | },
95 | ],
96 | inputsInline: true,
97 | tooltip: "",
98 | helpUrl: "",
99 | previousStatement: null,
100 | nextStatement: null,
101 | colour: 0,
102 | // style: "dict_blocks",
103 | },
104 | ];
105 |
106 | /**
107 | * Type of a 'dicts_create_with' block.
108 | *
109 | * @internal
110 | */
111 | export type DictCreateWithBlock = BlockSvg & DictCreateWithMixin;
112 | interface DictCreateWithMixin extends DictCreateWithMixinType {
113 | itemCount_: number;
114 | topInput_: Blockly.Input | undefined;
115 | }
116 | type DictCreateWithMixinType = typeof DICTS_CREATE_WITH;
117 |
118 | const DICTS_CREATE_WITH = {
119 | /**
120 | * Number of item inputs the block has.
121 | * @type {number}
122 | */
123 | itemCount_: 0,
124 |
125 | /**
126 | * Create XML to represent list inputs.
127 | * Backwards compatible serialization implementation.
128 | */
129 | mutationToDom: function (this: DictCreateWithBlock): Element {
130 | const container = Blockly.utils.xml.createElement("mutation");
131 | container.setAttribute("items", String(this.itemCount_));
132 | return container;
133 | },
134 |
135 | /**
136 | * Parse XML to restore the list inputs.
137 | * Backwards compatible serialization implementation.
138 | *
139 | * @param container XML storage element.
140 | */
141 | domToMutation: function (this: DictCreateWithBlock, xmlElement: Element) {
142 | const items = xmlElement.getAttribute("items");
143 | if (!items) throw new TypeError("element did not have items");
144 | this.itemCount_ = parseInt(items, 10);
145 | this.updateShape_();
146 | },
147 |
148 | /**
149 | * Returns the state of this block as a JSON serializable object.
150 | *
151 | * @returns The state of this block, ie the item count.
152 | */
153 | saveExtraState: function (this: DictCreateWithBlock): { itemCount: number } {
154 | return {
155 | itemCount: this.itemCount_,
156 | };
157 | },
158 |
159 | /**
160 | * Applies the given state to this block.
161 | *
162 | * @param state The state to apply to this block, ie the item count.
163 | */
164 | loadExtraState: function (this: DictCreateWithBlock, state: any) {
165 | const count = state["itemCount"];
166 | while (this.itemCount_ < count) {
167 | this.addPart_();
168 | }
169 | this.updateShape_();
170 | },
171 |
172 | /**
173 | * Modify this block to have the correct number of inputs.
174 | */
175 | updateShape_: function (this: DictCreateWithBlock) {
176 | this.updateMinus_();
177 | },
178 |
179 | /**
180 | * Callback for the plus image. Adds an input to the end of the block and
181 | * updates the state of the minus.
182 | */
183 | plus: function (this: DictCreateWithBlock) {
184 | this.addPart_();
185 | this.updateMinus_();
186 | },
187 |
188 | /**
189 | * Callback for the minus image. Removes an input from the end of the block
190 | * and updates the state of the minus.
191 | */
192 | minus: function (this: DictCreateWithBlock) {
193 | if (this.itemCount_ == 0) {
194 | return;
195 | }
196 | this.removePart_();
197 | this.updateMinus_();
198 | },
199 |
200 | // To properly keep track of indices we have to increment before/after adding
201 | // the inputs, and decrement the opposite.
202 | // Because we want our first input to be ARG0 (not ARG1) we increment after.
203 |
204 | /**
205 | * Adds an input to the end of the block. If the block currently has no
206 | * inputs it updates the top 'EMPTY' input to receive a block.
207 | * @this {Blockly.Block}
208 | * @private
209 | */
210 | addPart_: function (this: DictCreateWithBlock) {
211 | if (this.itemCount_ == 0) {
212 | this.removeInput("EMPTY");
213 | this.topInput_ = this.appendValueInput("KEY" + String(this.itemCount_))
214 | .setAlign(Blockly.inputs.Align.RIGHT)
215 | .appendField(createPlusField(), "PLUS")
216 | .appendField(`键 ${this.itemCount_}`);
217 | this.appendValueInput("VALUE" + String(this.itemCount_))
218 | .setAlign(Blockly.inputs.Align.RIGHT)
219 | .appendField(`值 ${this.itemCount_}`);
220 | } else {
221 | this.appendValueInput("KEY" + String(this.itemCount_))
222 | .setAlign(Blockly.inputs.Align.RIGHT)
223 | .appendField(`键 ${this.itemCount_}`);
224 | this.appendValueInput("VALUE" + String(this.itemCount_))
225 | .setAlign(Blockly.inputs.Align.RIGHT)
226 | .appendField(`值 ${this.itemCount_}`);
227 | }
228 | this.itemCount_++;
229 | },
230 |
231 | /**
232 | * Removes an input from the end of the block. If we are removing the last
233 | * input this updates the block to have an 'EMPTY' top input.
234 | * @this {Blockly.Block}
235 | * @private
236 | */
237 | removePart_: function (this: DictCreateWithBlock) {
238 | this.itemCount_--;
239 | this.removeInput("KEY" + String(this.itemCount_));
240 | this.removeInput("VALUE" + String(this.itemCount_));
241 | if (this.itemCount_ == 0) {
242 | (this.topInput_ as Blockly.Input) = this.appendDummyInput("EMPTY")
243 | .appendField(createPlusField(), "PLUS")
244 | .setAlign(Blockly.inputs.Align.RIGHT)
245 | .appendField("空字典");
246 | }
247 | },
248 |
249 | /**
250 | * Makes it so the minus is visible iff there is an input available to remove.
251 | * @private
252 | */
253 | updateMinus_: function (this: DictCreateWithBlock) {
254 | const minusField = this.getField("MINUS");
255 | if (!minusField && this.itemCount_ > 0) {
256 | this.topInput_?.insertFieldAt(1, createMinusField(), "MINUS");
257 | } else if (minusField && this.itemCount_ < 1) {
258 | this.topInput_?.removeField("MINUS");
259 | }
260 | },
261 | };
262 |
263 | const DICTS_CREATE_WITH_EXTENSION = function (this: DictCreateWithBlock) {
264 | this.itemCount_ = 0;
265 | this.updateShape_();
266 | this.getInput("EMPTY")?.insertFieldAt(0, createPlusField(), "PLUS");
267 | };
268 |
269 | /**
270 | * Type of a 'dicts_get_multi' block.
271 | *
272 | * @internal
273 | */
274 | export type DictGetMultiBlock = BlockSvg & DictGetMultiMixin;
275 | interface DictGetMultiMixin extends DictGetMultiMixinType {
276 | itemCount_: number;
277 | }
278 | type DictGetMultiMixinType = typeof DICTS_GET_MULTI;
279 |
280 | const DICTS_GET_MULTI = {
281 | /**
282 | * Number of item inputs the block has.
283 | * @type {number}
284 | */
285 | itemCount_: 1,
286 |
287 | /**
288 | * Create XML to represent list inputs.
289 | * Backwards compatible serialization implementation.
290 | */
291 | mutationToDom: function (this: DictGetMultiBlock): Element {
292 | const container = Blockly.utils.xml.createElement("mutation");
293 | container.setAttribute("items", String(this.itemCount_));
294 | return container;
295 | },
296 |
297 | /**
298 | * Parse XML to restore the list inputs.
299 | * Backwards compatible serialization implementation.
300 | *
301 | * @param container XML storage element.
302 | */
303 | domToMutation: function (this: DictGetMultiBlock, xmlElement: Element) {
304 | const items = xmlElement.getAttribute("items");
305 | if (!items) throw new TypeError("element did not have items");
306 | this.itemCount_ = parseInt(items, 10);
307 | this.updateShape_();
308 | },
309 |
310 | /**
311 | * Returns the state of this block as a JSON serializable object.
312 | *
313 | * @returns The state of this block, ie the item count.
314 | */
315 | saveExtraState: function (this: DictGetMultiBlock): { itemCount: number } {
316 | return {
317 | itemCount: this.itemCount_,
318 | };
319 | },
320 |
321 | /**
322 | * Applies the given state to this block.
323 | *
324 | * @param state The state to apply to this block, ie the item count.
325 | */
326 | loadExtraState: function (this: DictGetMultiBlock, state: any) {
327 | const count = state["itemCount"];
328 | while (this.itemCount_ < count) {
329 | this.addPart_();
330 | }
331 | this.updateShape_();
332 | },
333 |
334 | /**
335 | * Modify this block to have the correct number of inputs.
336 | */
337 | updateShape_: function (this: DictGetMultiBlock) {
338 | this.updateMinus_();
339 | },
340 |
341 | /**
342 | * Callback for the plus image. Adds an input to the end of the block and
343 | * updates the state of the minus.
344 | */
345 | plus: function (this: DictGetMultiBlock) {
346 | this.addPart_();
347 | this.updateMinus_();
348 | },
349 |
350 | /**
351 | * Callback for the minus image. Removes an input from the end of the block
352 | * and updates the state of the minus.
353 | */
354 | minus: function (this: DictGetMultiBlock) {
355 | if (this.itemCount_ == 1) {
356 | return;
357 | }
358 | this.removePart_();
359 | this.updateMinus_();
360 | },
361 |
362 | // To properly keep track of indices we have to increment before/after adding
363 | // the inputs, and decrement the opposite.
364 | // Because we want our first input to be ARG0 (not ARG1) we increment after.
365 |
366 | /**
367 | * Adds an input to the end of the block. If the block currently has no
368 | * inputs it updates the top 'EMPTY' input to receive a block.
369 | * @this {Blockly.Block}
370 | * @private
371 | */
372 | addPart_: function (this: DictGetMultiBlock) {
373 | this.getInput("KEYS")?.appendField(
374 | new Blockly.FieldTextInput("key" + String(this.itemCount_)),
375 | "KEY" + String(this.itemCount_),
376 | );
377 | this.itemCount_++;
378 | },
379 |
380 | /**
381 | * Removes an input from the end of the block. If we are removing the last
382 | * input this updates the block to have an 'EMPTY' top input.
383 | * @this {Blockly.Block}
384 | * @private
385 | */
386 | removePart_: function (this: DictGetMultiBlock) {
387 | this.itemCount_--;
388 | this.getInput("KEYS")?.removeField("KEY" + String(this.itemCount_));
389 | },
390 |
391 | /**
392 | * Makes it so the minus is visible iff there is an input available to remove.
393 | * @private
394 | */
395 | updateMinus_: function (this: DictGetMultiBlock) {
396 | const minusField = this.getField("MINUS");
397 | if (!minusField && this.itemCount_ > 1) {
398 | this.getInput("TOOLS")?.insertFieldAt(1, createMinusField(), "MINUS");
399 | } else if (minusField && this.itemCount_ < 2) {
400 | this.getInput("TOOLS")?.removeField("MINUS");
401 | }
402 | },
403 | };
404 |
405 | const DICTS_GET_MULTI_EXTENSION = function (this: DictGetMultiBlock) {
406 | this.itemCount_ = 1;
407 | this.updateShape_();
408 | this.getInput("KEYS")?.appendField(
409 | new Blockly.FieldTextInput("key0"),
410 | "KEY0",
411 | );
412 | this.getInput("TOOLS")?.insertFieldAt(0, createPlusField(), "PLUS");
413 | };
414 |
415 | if (Blockly.Extensions.isRegistered("dict_create_with_mutator")) {
416 | Blockly.Extensions.unregister("dict_create_with_mutator");
417 | }
418 | Blockly.Extensions.registerMutator(
419 | "dict_create_with_mutator",
420 | DICTS_CREATE_WITH,
421 | DICTS_CREATE_WITH_EXTENSION,
422 | );
423 |
424 | if (Blockly.Extensions.isRegistered("dict_get_multi_mutator")) {
425 | Blockly.Extensions.unregister("dict_get_multi_mutator");
426 | }
427 | Blockly.Extensions.registerMutator(
428 | "dict_get_multi_mutator",
429 | DICTS_GET_MULTI,
430 | DICTS_GET_MULTI_EXTENSION,
431 | );
432 |
--------------------------------------------------------------------------------
/packages/app/src/default.ts:
--------------------------------------------------------------------------------
1 | export const demoProject = {
2 | version: "v1",
3 | data: {
4 | workspaceComments: [
5 | {
6 | height: 79.33334350585938,
7 | width: 401.33331298828125,
8 | id: ").5C,33iYe,Es5QP^CZS",
9 | x: 490,
10 | y: 50,
11 | text: '消息处理对整条纯文本消息进行响应\n发送内容为 "ping" 会得到回复 "pong"',
12 | },
13 | {
14 | height: 143.99996948242188,
15 | width: 480.00006103515625,
16 | id: "^qq4*u=7LW8i@`p}MbNT",
17 | x: 970,
18 | y: 350,
19 | text: '命令处理只处理以命令起始符和命令字符串起始的消息\n命令起始符可在配置选项卡中设置,默认为 "/"\n选择了空命令起始符时可直接匹配以命令字符串起始的消息\n本例中可相应消息如 "/save 1" "/save 2"\n每次调用会返回上一次的参数,首次为 "None",第二次为 "1"',
20 | },
21 | {
22 | height: 92,
23 | width: 483.33331298828125,
24 | id: "[NvN:*-?JE3}m5R}jIm7",
25 | x: 970,
26 | y: 510,
27 | text: "文件存储支持可以将文本或字典存入本地文件\n或从本地文件读出到对应的变量中",
28 | },
29 | {
30 | height: 104.66668701171875,
31 | width: 402,
32 | id: "W;2zsU/+VlY54WQsR)5V",
33 | x: 490,
34 | y: 570,
35 | text: "跨平台命令解析预处理由 alconna 插件提供支持\n可以自由配置固定字符串或多种数据类型的命令参数列表\n并在处理流程中取得对应的命令参数使用",
36 | },
37 | {
38 | height: 82.666748046875,
39 | width: 401.99993896484375,
40 | id: "^dl/_H.6Ei@c8]zhc)MI",
41 | x: 490,
42 | y: 150,
43 | text: "网络请求支持可以构建GET或POST网络请求\n需要正确配置返回内容的数据类型以支持后续处理",
44 | },
45 | {
46 | height: 58.00006103515625,
47 | width: 389.99981689453125,
48 | id: "6ILVtb0f/4J2QpMw?W?u",
49 | x: 970,
50 | y: 810,
51 | text: "定时任务支持可以根据设定时间重复指定的处理操作",
52 | },
53 | ],
54 | blocks: {
55 | languageVersion: 0,
56 | blocks: [
57 | {
58 | type: "nonebot_on_command",
59 | id: "!WdCO9=B,k2IY0/Su4|I",
60 | x: 970,
61 | y: 50,
62 | fields: { COMMAND: "save", TOME: true },
63 | inputs: {
64 | HANDLE: {
65 | block: {
66 | type: "variables_set",
67 | id: "9_AQ_@)mWU$=2GkYbT-d",
68 | fields: { VAR: { id: "VSvr3AeHEP),5NB5v$+;" } },
69 | inputs: {
70 | VALUE: {
71 | block: {
72 | type: "store_load_json",
73 | id: "3~H`IY?A(%(Bb+8/wL3c",
74 | fields: { FILE: "save.json" },
75 | },
76 | },
77 | },
78 | next: {
79 | block: {
80 | type: "variables_set",
81 | id: "gc.{0S@R!yzBky.dRf/T",
82 | fields: { VAR: { id: "dS|cq^n0{Ep#B]f*RtAH" } },
83 | inputs: {
84 | VALUE: {
85 | block: {
86 | type: "dicts_get",
87 | id: "R5``m]FzB+yBPW1W2`]$",
88 | fields: { KEY: "last" },
89 | inputs: {
90 | DICT: {
91 | block: {
92 | type: "variables_get",
93 | id: "[Ws*B$JWgLY]BeT`^_=]",
94 | fields: { VAR: { id: "VSvr3AeHEP),5NB5v$+;" } },
95 | },
96 | },
97 | },
98 | },
99 | },
100 | },
101 | next: {
102 | block: {
103 | type: "nonebot_send",
104 | id: "2VEO#b:zwo|#8!@y%P}q",
105 | fields: { FINISH: false },
106 | inputs: {
107 | MESSAGE: {
108 | block: {
109 | type: "text_join",
110 | id: "~Va]uDcID[2P5@n3z{pe",
111 | extraState: { itemCount: 2 },
112 | inputs: {
113 | ADD0: {
114 | block: {
115 | type: "text",
116 | id: "3Aw,]xK/YxeE+S`=:}vc",
117 | fields: { TEXT: "last: " },
118 | },
119 | },
120 | ADD1: {
121 | block: {
122 | type: "variables_get",
123 | id: "hF5`vj~{OC2uF+^}Q8yk",
124 | fields: {
125 | VAR: { id: "dS|cq^n0{Ep#B]f*RtAH" },
126 | },
127 | },
128 | },
129 | },
130 | },
131 | },
132 | },
133 | next: {
134 | block: {
135 | type: "dicts_set",
136 | id: "B3Wx%a7x=Pp95taY%.*K",
137 | fields: { KEY: "last" },
138 | inputs: {
139 | DICT: {
140 | block: {
141 | type: "variables_get",
142 | id: "7iF8)`YtA.s.-@v_dYNo",
143 | fields: {
144 | VAR: { id: "VSvr3AeHEP),5NB5v$+;" },
145 | },
146 | },
147 | },
148 | VALUE: {
149 | block: {
150 | type: "nonebot_param_text",
151 | id: "{eYrM|C4so41hXY4~MtD",
152 | },
153 | },
154 | },
155 | next: {
156 | block: {
157 | type: "store_save_json",
158 | id: "sOck!o^D;xHPwNdX0v;s",
159 | fields: { FILE: "save.json" },
160 | inputs: {
161 | DICT: {
162 | block: {
163 | type: "variables_get",
164 | id: "Z8a4Wo4ae#)plFG}S:6B",
165 | fields: {
166 | VAR: { id: "VSvr3AeHEP),5NB5v$+;" },
167 | },
168 | },
169 | },
170 | },
171 | },
172 | },
173 | },
174 | },
175 | },
176 | },
177 | },
178 | },
179 | },
180 | },
181 | },
182 | },
183 | {
184 | type: "nonebot_on_message",
185 | id: "rJ[.=4k)POkDVT?Wz%zD",
186 | x: 70,
187 | y: 50,
188 | fields: { TOME: false },
189 | inputs: {
190 | HANDLE: {
191 | block: {
192 | type: "controls_if",
193 | id: "SBBb-4DL]Ofg,o5,lix9",
194 | extraState: { elseIfCount: 2 },
195 | inputs: {
196 | IF0: {
197 | block: {
198 | type: "logic_compare",
199 | id: "3KiI-ePNeWs/tZ.xUQd*",
200 | fields: { OP: "EQ" },
201 | inputs: {
202 | A: {
203 | block: {
204 | type: "nonebot_param_text",
205 | id: "}:vuF-QZzN6NL{}`jW87",
206 | },
207 | },
208 | B: {
209 | block: {
210 | type: "text",
211 | id: ":R-~fjy%Xs,HExpeH$jd",
212 | fields: { TEXT: "ping" },
213 | },
214 | },
215 | },
216 | },
217 | },
218 | DO0: {
219 | block: {
220 | type: "nonebot_send",
221 | id: "zyjiCMiE87Wm_gt~i6dj",
222 | fields: { FINISH: true },
223 | inputs: {
224 | MESSAGE: {
225 | block: {
226 | type: "text",
227 | id: "NpK-srq00|eB:+v#I5t?",
228 | fields: { TEXT: "pong" },
229 | },
230 | },
231 | },
232 | },
233 | },
234 | IF1: {
235 | block: {
236 | type: "logic_compare",
237 | id: "!Q{^KS]tk}knY$0loSbK",
238 | fields: { OP: "EQ" },
239 | inputs: {
240 | A: {
241 | block: {
242 | type: "nonebot_param_text",
243 | id: "R3^-rcLn~gkf#eJO//|S",
244 | },
245 | },
246 | B: {
247 | block: {
248 | type: "text",
249 | id: "L44H6xsB#jzeNUodNS3*",
250 | fields: { TEXT: "plugin" },
251 | },
252 | },
253 | },
254 | },
255 | },
256 | DO1: {
257 | block: {
258 | type: "variables_set",
259 | id: "}8cI$-ju]d/4jbudI(%?",
260 | fields: { VAR: { id: "i7%fQRO5s:HNPn!Egg/!" } },
261 | inputs: {
262 | VALUE: {
263 | block: {
264 | type: "request_get",
265 | id: "i%$0*PV*Q}aD.4uiq,Xk",
266 | fields: { TYPE: "list", TIMEOUT: 60 },
267 | inputs: {
268 | URL: {
269 | block: {
270 | type: "text",
271 | id: "ju`#!lmdA,,j~GxD)LdV",
272 | fields: {
273 | TEXT: "https://registry.nonebot.dev/plugins.json",
274 | },
275 | },
276 | },
277 | },
278 | },
279 | },
280 | },
281 | next: {
282 | block: {
283 | type: "nonebot_send",
284 | id: "I]HVejYi1XMiH7@L#%fa",
285 | fields: { FINISH: true },
286 | inputs: {
287 | MESSAGE: {
288 | block: {
289 | type: "text_join",
290 | id: "TBv3^GAlbgL-)1DGdyI`",
291 | extraState: { itemCount: 2 },
292 | inputs: {
293 | ADD0: {
294 | block: {
295 | type: "text",
296 | id: "Nr4jVV3k@]@q._B7$O+_",
297 | fields: { TEXT: "当前插件数量为:" },
298 | },
299 | },
300 | ADD1: {
301 | block: {
302 | type: "lists_length",
303 | id: "e8GR1^[w[S5c,=vs`nU(",
304 | inputs: {
305 | VALUE: {
306 | block: {
307 | type: "variables_get",
308 | id: "@^l~%MD,Wm9RLJ|N%Ys4",
309 | fields: {
310 | VAR: {
311 | id: "i7%fQRO5s:HNPn!Egg/!",
312 | },
313 | },
314 | },
315 | },
316 | },
317 | },
318 | },
319 | },
320 | },
321 | },
322 | },
323 | },
324 | },
325 | },
326 | },
327 | IF2: {
328 | block: {
329 | type: "logic_compare",
330 | id: "o|dYI,`Cy)u6gPCrP]!r",
331 | fields: { OP: "EQ" },
332 | inputs: {
333 | A: {
334 | block: {
335 | type: "nonebot_param_text",
336 | id: "2_xq)Q*KP{j0d0ZG1y`Z",
337 | },
338 | },
339 | B: {
340 | block: {
341 | type: "text",
342 | id: "S:8I/+[}C@_cjY*@_dhE",
343 | fields: { TEXT: "random" },
344 | },
345 | },
346 | },
347 | },
348 | },
349 | DO2: {
350 | block: {
351 | type: "nonebot_send",
352 | id: "[QQQ:Wu;b)hi|C_=p[T(",
353 | fields: { FINISH: false },
354 | inputs: {
355 | MESSAGE: {
356 | block: {
357 | type: "text_join",
358 | id: "NXK(kC#{y^-Wvr2a0_+;",
359 | extraState: { itemCount: 1 },
360 | inputs: {
361 | ADD0: {
362 | block: {
363 | type: "variables_get",
364 | id: "-f7]+j)Y],;g0.nxr[Kr",
365 | fields: {
366 | VAR: { id: "dm}-^1M|xx~4#6g~~7e#" },
367 | },
368 | },
369 | },
370 | },
371 | },
372 | },
373 | },
374 | },
375 | },
376 | },
377 | },
378 | },
379 | },
380 | },
381 | {
382 | type: "nonebot_on_alconna",
383 | id: "|Fu4X1(2DR;,y_a_tXuZ",
384 | x: 70,
385 | y: 610,
386 | extraState: { itemCount: 3 },
387 | fields: { COMMAND: "command", TOME: true },
388 | inputs: {
389 | ARG0: {
390 | block: {
391 | type: "alconna_const",
392 | id: "F]W)`U.)2h|Wy@y[#X1A",
393 | fields: { TEXT: "test" },
394 | },
395 | },
396 | ARG1: {
397 | block: {
398 | type: "alconna_arg",
399 | id: "86},)_bY]o`5w*GGsg+o",
400 | fields: { NAME: "name", TYPE: "str" },
401 | },
402 | },
403 | ARG2: {
404 | block: {
405 | type: "alconna_arg",
406 | id: "}qB@y=xly_6U~?~cTvl7",
407 | fields: { NAME: "number", TYPE: "int" },
408 | },
409 | },
410 | HANDLE: {
411 | block: {
412 | type: "nonebot_send",
413 | id: "IG0GrnVygzO2N+qv~Wav",
414 | fields: { FINISH: false },
415 | inputs: {
416 | MESSAGE: {
417 | block: {
418 | type: "text_join",
419 | id: "BYK7GDru]?*p60E0ED!}",
420 | extraState: { itemCount: 2 },
421 | inputs: {
422 | ADD0: {
423 | block: {
424 | type: "text",
425 | id: "Lz;^)x7Ry)aC`;jz(n_[",
426 | fields: { TEXT: "参数name为:" },
427 | },
428 | },
429 | ADD1: {
430 | block: {
431 | type: "alconna_arg_get",
432 | id: "kjv.p]ZKq?$Ay!@|(GZz",
433 | extraState: { name: "name" },
434 | fields: { NAME: "name" },
435 | },
436 | },
437 | },
438 | },
439 | },
440 | },
441 | next: {
442 | block: {
443 | type: "nonebot_send",
444 | id: "Df,m4`}7lCDAVw=}TxLl",
445 | fields: { FINISH: false },
446 | inputs: {
447 | MESSAGE: {
448 | block: {
449 | type: "text_join",
450 | id: ",ujwQq~#eb/c(q9#mKnW",
451 | extraState: { itemCount: 2 },
452 | inputs: {
453 | ADD0: {
454 | block: {
455 | type: "text",
456 | id: "TJ?2:7Q!R$=_eUyD;M8m",
457 | fields: { TEXT: "参数number为:" },
458 | },
459 | },
460 | ADD1: {
461 | block: {
462 | type: "alconna_arg_get",
463 | id: "[u:Is_2hXiJap%JWvgl2",
464 | extraState: { name: "number" },
465 | fields: { NAME: "number" },
466 | },
467 | },
468 | },
469 | },
470 | },
471 | },
472 | },
473 | },
474 | },
475 | },
476 | },
477 | },
478 | {
479 | type: "scheduler_add",
480 | id: "6djmI(1,`~eAj}g=@PXr",
481 | x: 970,
482 | y: 650,
483 | inputs: {
484 | TIME: {
485 | block: {
486 | type: "scheduler_time_interval",
487 | id: "XCCX[EaW9yKIF;Ck|waX",
488 | fields: { NUMBER: 10, UNIT: "seconds" },
489 | },
490 | },
491 | ID: {
492 | block: {
493 | type: "text",
494 | id: "@JgyG!7m]+3d#@1GPRaA",
495 | fields: { TEXT: "refresh_number" },
496 | },
497 | },
498 | HANDLE: {
499 | block: {
500 | type: "variables_set",
501 | id: "iRc7.8GkpN=ZE){MYOG9",
502 | fields: { VAR: { id: "dm}-^1M|xx~4#6g~~7e#" } },
503 | inputs: {
504 | VALUE: {
505 | block: {
506 | type: "math_random_float",
507 | id: "$oMB|U7+QU_qfCwi,J$j",
508 | },
509 | },
510 | },
511 | },
512 | },
513 | },
514 | },
515 | ],
516 | },
517 | variables: [
518 | { name: "data_dict", id: "VSvr3AeHEP),5NB5v$+;" },
519 | { name: "last_param", id: "dS|cq^n0{Ep#B]f*RtAH" },
520 | { name: "plugin_data", id: "i7%fQRO5s:HNPn!Egg/!" },
521 | { name: "plugin_data2", id: "`a{F^Z`6/mb_37*,mPgL" },
522 | { name: "random_number", id: "dm}-^1M|xx~4#6g~~7e#" },
523 | ],
524 | },
525 | config: {
526 | name: "app_demo",
527 | preset: { name: "console", description: "控制台机器人" },
528 | port: 8080,
529 | platform: ["windows", "linux"],
530 | commandStart: ["/", ""],
531 | superusers: ["User"],
532 | kwargs: {},
533 | },
534 | };
535 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: "9.0"
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 | .:
9 | devDependencies:
10 | prettier:
11 | specifier: ^3.3.2
12 | version: 3.3.2
13 |
14 | packages/app:
15 | dependencies:
16 | blockly:
17 | specifier: ^11.1.1
18 | version: 11.1.1
19 | file-saver:
20 | specifier: ^2.0.5
21 | version: 2.0.5
22 | highlight.js:
23 | specifier: ^11.9.0
24 | version: 11.9.0
25 | jszip:
26 | specifier: ^3.10.1
27 | version: 3.10.1
28 | sass:
29 | specifier: ^1.77.6
30 | version: 1.77.6
31 | vue:
32 | specifier: ^3.4.29
33 | version: 3.4.29(typescript@5.5.2)
34 | vuetify:
35 | specifier: ^3.6.10
36 | version: 3.6.10(typescript@5.5.2)(vite-plugin-vuetify@2.0.3)(vue@3.4.29(typescript@5.5.2))
37 | devDependencies:
38 | "@blockly/theme-dark":
39 | specifier: ^7.0.1
40 | version: 7.0.1(blockly@11.1.1)
41 | "@highlightjs/vue-plugin":
42 | specifier: ^2.1.0
43 | version: 2.1.0(highlight.js@11.9.0)(vue@3.4.29(typescript@5.5.2))
44 | "@mdi/js":
45 | specifier: ^7.4.47
46 | version: 7.4.47
47 | "@types/file-saver":
48 | specifier: ^2.0.7
49 | version: 2.0.7
50 | "@vitejs/plugin-vue":
51 | specifier: ^5.0.5
52 | version: 5.0.5(vite@5.3.1(sass@1.77.6))(vue@3.4.29(typescript@5.5.2))
53 | typescript:
54 | specifier: ^5.5.2
55 | version: 5.5.2
56 | vite:
57 | specifier: ^5.3.1
58 | version: 5.3.1(sass@1.77.6)
59 | vite-plugin-static-copy:
60 | specifier: ^1.0.5
61 | version: 1.0.5(vite@5.3.1(sass@1.77.6))
62 | vite-plugin-vuetify:
63 | specifier: ^2.0.3
64 | version: 2.0.3(vite@5.3.1(sass@1.77.6))(vue@3.4.29(typescript@5.5.2))(vuetify@3.6.10)
65 | vue-tsc:
66 | specifier: ^2.0.21
67 | version: 2.0.21(typescript@5.5.2)
68 |
69 | packages:
70 | "@babel/helper-string-parser@7.24.7":
71 | resolution:
72 | {
73 | integrity: sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==,
74 | }
75 | engines: { node: ">=6.9.0" }
76 |
77 | "@babel/helper-validator-identifier@7.24.7":
78 | resolution:
79 | {
80 | integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==,
81 | }
82 | engines: { node: ">=6.9.0" }
83 |
84 | "@babel/parser@7.24.7":
85 | resolution:
86 | {
87 | integrity: sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==,
88 | }
89 | engines: { node: ">=6.0.0" }
90 | hasBin: true
91 |
92 | "@babel/types@7.24.7":
93 | resolution:
94 | {
95 | integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==,
96 | }
97 | engines: { node: ">=6.9.0" }
98 |
99 | "@blockly/theme-dark@7.0.1":
100 | resolution:
101 | {
102 | integrity: sha512-yJZmdV/8ZZQ/NvL1QncTwuxDay/XwyCPr8qYHpfqYii2zOBCtSTxpK5KaF/RHHVwBti0j2c8qEp/AdJ1J3nuSg==,
103 | }
104 | engines: { node: ">=8.17.0" }
105 | peerDependencies:
106 | blockly: ^11.0.0
107 |
108 | "@esbuild/aix-ppc64@0.21.5":
109 | resolution:
110 | {
111 | integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==,
112 | }
113 | engines: { node: ">=12" }
114 | cpu: [ppc64]
115 | os: [aix]
116 |
117 | "@esbuild/android-arm64@0.21.5":
118 | resolution:
119 | {
120 | integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==,
121 | }
122 | engines: { node: ">=12" }
123 | cpu: [arm64]
124 | os: [android]
125 |
126 | "@esbuild/android-arm@0.21.5":
127 | resolution:
128 | {
129 | integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==,
130 | }
131 | engines: { node: ">=12" }
132 | cpu: [arm]
133 | os: [android]
134 |
135 | "@esbuild/android-x64@0.21.5":
136 | resolution:
137 | {
138 | integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==,
139 | }
140 | engines: { node: ">=12" }
141 | cpu: [x64]
142 | os: [android]
143 |
144 | "@esbuild/darwin-arm64@0.21.5":
145 | resolution:
146 | {
147 | integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==,
148 | }
149 | engines: { node: ">=12" }
150 | cpu: [arm64]
151 | os: [darwin]
152 |
153 | "@esbuild/darwin-x64@0.21.5":
154 | resolution:
155 | {
156 | integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==,
157 | }
158 | engines: { node: ">=12" }
159 | cpu: [x64]
160 | os: [darwin]
161 |
162 | "@esbuild/freebsd-arm64@0.21.5":
163 | resolution:
164 | {
165 | integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==,
166 | }
167 | engines: { node: ">=12" }
168 | cpu: [arm64]
169 | os: [freebsd]
170 |
171 | "@esbuild/freebsd-x64@0.21.5":
172 | resolution:
173 | {
174 | integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==,
175 | }
176 | engines: { node: ">=12" }
177 | cpu: [x64]
178 | os: [freebsd]
179 |
180 | "@esbuild/linux-arm64@0.21.5":
181 | resolution:
182 | {
183 | integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==,
184 | }
185 | engines: { node: ">=12" }
186 | cpu: [arm64]
187 | os: [linux]
188 |
189 | "@esbuild/linux-arm@0.21.5":
190 | resolution:
191 | {
192 | integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==,
193 | }
194 | engines: { node: ">=12" }
195 | cpu: [arm]
196 | os: [linux]
197 |
198 | "@esbuild/linux-ia32@0.21.5":
199 | resolution:
200 | {
201 | integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==,
202 | }
203 | engines: { node: ">=12" }
204 | cpu: [ia32]
205 | os: [linux]
206 |
207 | "@esbuild/linux-loong64@0.21.5":
208 | resolution:
209 | {
210 | integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==,
211 | }
212 | engines: { node: ">=12" }
213 | cpu: [loong64]
214 | os: [linux]
215 |
216 | "@esbuild/linux-mips64el@0.21.5":
217 | resolution:
218 | {
219 | integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==,
220 | }
221 | engines: { node: ">=12" }
222 | cpu: [mips64el]
223 | os: [linux]
224 |
225 | "@esbuild/linux-ppc64@0.21.5":
226 | resolution:
227 | {
228 | integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==,
229 | }
230 | engines: { node: ">=12" }
231 | cpu: [ppc64]
232 | os: [linux]
233 |
234 | "@esbuild/linux-riscv64@0.21.5":
235 | resolution:
236 | {
237 | integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==,
238 | }
239 | engines: { node: ">=12" }
240 | cpu: [riscv64]
241 | os: [linux]
242 |
243 | "@esbuild/linux-s390x@0.21.5":
244 | resolution:
245 | {
246 | integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==,
247 | }
248 | engines: { node: ">=12" }
249 | cpu: [s390x]
250 | os: [linux]
251 |
252 | "@esbuild/linux-x64@0.21.5":
253 | resolution:
254 | {
255 | integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==,
256 | }
257 | engines: { node: ">=12" }
258 | cpu: [x64]
259 | os: [linux]
260 |
261 | "@esbuild/netbsd-x64@0.21.5":
262 | resolution:
263 | {
264 | integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==,
265 | }
266 | engines: { node: ">=12" }
267 | cpu: [x64]
268 | os: [netbsd]
269 |
270 | "@esbuild/openbsd-x64@0.21.5":
271 | resolution:
272 | {
273 | integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==,
274 | }
275 | engines: { node: ">=12" }
276 | cpu: [x64]
277 | os: [openbsd]
278 |
279 | "@esbuild/sunos-x64@0.21.5":
280 | resolution:
281 | {
282 | integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==,
283 | }
284 | engines: { node: ">=12" }
285 | cpu: [x64]
286 | os: [sunos]
287 |
288 | "@esbuild/win32-arm64@0.21.5":
289 | resolution:
290 | {
291 | integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==,
292 | }
293 | engines: { node: ">=12" }
294 | cpu: [arm64]
295 | os: [win32]
296 |
297 | "@esbuild/win32-ia32@0.21.5":
298 | resolution:
299 | {
300 | integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==,
301 | }
302 | engines: { node: ">=12" }
303 | cpu: [ia32]
304 | os: [win32]
305 |
306 | "@esbuild/win32-x64@0.21.5":
307 | resolution:
308 | {
309 | integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==,
310 | }
311 | engines: { node: ">=12" }
312 | cpu: [x64]
313 | os: [win32]
314 |
315 | "@highlightjs/vue-plugin@2.1.0":
316 | resolution:
317 | {
318 | integrity: sha512-E+bmk4ncca+hBEYRV2a+1aIzIV0VSY/e5ArjpuSN9IO7wBJrzUE2u4ESCwrbQD7sAy+jWQjkV5qCCWgc+pu7CQ==,
319 | }
320 | peerDependencies:
321 | highlight.js: ^11.0.1
322 | vue: ^3
323 |
324 | "@jridgewell/sourcemap-codec@1.4.15":
325 | resolution:
326 | {
327 | integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==,
328 | }
329 |
330 | "@mdi/js@7.4.47":
331 | resolution:
332 | {
333 | integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==,
334 | }
335 |
336 | "@nodelib/fs.scandir@2.1.5":
337 | resolution:
338 | {
339 | integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==,
340 | }
341 | engines: { node: ">= 8" }
342 |
343 | "@nodelib/fs.stat@2.0.5":
344 | resolution:
345 | {
346 | integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==,
347 | }
348 | engines: { node: ">= 8" }
349 |
350 | "@nodelib/fs.walk@1.2.8":
351 | resolution:
352 | {
353 | integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==,
354 | }
355 | engines: { node: ">= 8" }
356 |
357 | "@rollup/rollup-android-arm-eabi@4.18.0":
358 | resolution:
359 | {
360 | integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==,
361 | }
362 | cpu: [arm]
363 | os: [android]
364 |
365 | "@rollup/rollup-android-arm64@4.18.0":
366 | resolution:
367 | {
368 | integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==,
369 | }
370 | cpu: [arm64]
371 | os: [android]
372 |
373 | "@rollup/rollup-darwin-arm64@4.18.0":
374 | resolution:
375 | {
376 | integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==,
377 | }
378 | cpu: [arm64]
379 | os: [darwin]
380 |
381 | "@rollup/rollup-darwin-x64@4.18.0":
382 | resolution:
383 | {
384 | integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==,
385 | }
386 | cpu: [x64]
387 | os: [darwin]
388 |
389 | "@rollup/rollup-linux-arm-gnueabihf@4.18.0":
390 | resolution:
391 | {
392 | integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==,
393 | }
394 | cpu: [arm]
395 | os: [linux]
396 |
397 | "@rollup/rollup-linux-arm-musleabihf@4.18.0":
398 | resolution:
399 | {
400 | integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==,
401 | }
402 | cpu: [arm]
403 | os: [linux]
404 |
405 | "@rollup/rollup-linux-arm64-gnu@4.18.0":
406 | resolution:
407 | {
408 | integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==,
409 | }
410 | cpu: [arm64]
411 | os: [linux]
412 |
413 | "@rollup/rollup-linux-arm64-musl@4.18.0":
414 | resolution:
415 | {
416 | integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==,
417 | }
418 | cpu: [arm64]
419 | os: [linux]
420 |
421 | "@rollup/rollup-linux-powerpc64le-gnu@4.18.0":
422 | resolution:
423 | {
424 | integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==,
425 | }
426 | cpu: [ppc64]
427 | os: [linux]
428 |
429 | "@rollup/rollup-linux-riscv64-gnu@4.18.0":
430 | resolution:
431 | {
432 | integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==,
433 | }
434 | cpu: [riscv64]
435 | os: [linux]
436 |
437 | "@rollup/rollup-linux-s390x-gnu@4.18.0":
438 | resolution:
439 | {
440 | integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==,
441 | }
442 | cpu: [s390x]
443 | os: [linux]
444 |
445 | "@rollup/rollup-linux-x64-gnu@4.18.0":
446 | resolution:
447 | {
448 | integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==,
449 | }
450 | cpu: [x64]
451 | os: [linux]
452 |
453 | "@rollup/rollup-linux-x64-musl@4.18.0":
454 | resolution:
455 | {
456 | integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==,
457 | }
458 | cpu: [x64]
459 | os: [linux]
460 |
461 | "@rollup/rollup-win32-arm64-msvc@4.18.0":
462 | resolution:
463 | {
464 | integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==,
465 | }
466 | cpu: [arm64]
467 | os: [win32]
468 |
469 | "@rollup/rollup-win32-ia32-msvc@4.18.0":
470 | resolution:
471 | {
472 | integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==,
473 | }
474 | cpu: [ia32]
475 | os: [win32]
476 |
477 | "@rollup/rollup-win32-x64-msvc@4.18.0":
478 | resolution:
479 | {
480 | integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==,
481 | }
482 | cpu: [x64]
483 | os: [win32]
484 |
485 | "@types/estree@1.0.5":
486 | resolution:
487 | {
488 | integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==,
489 | }
490 |
491 | "@types/file-saver@2.0.7":
492 | resolution:
493 | {
494 | integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==,
495 | }
496 |
497 | "@vitejs/plugin-vue@5.0.5":
498 | resolution:
499 | {
500 | integrity: sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==,
501 | }
502 | engines: { node: ^18.0.0 || >=20.0.0 }
503 | peerDependencies:
504 | vite: ^5.0.0
505 | vue: ^3.2.25
506 |
507 | "@volar/language-core@2.3.0":
508 | resolution:
509 | {
510 | integrity: sha512-pvhL24WUh3VDnv7Yw5N1sjhPtdx7q9g+Wl3tggmnkMcyK8GcCNElF2zHiKznryn0DiUGk+eez/p2qQhz+puuHw==,
511 | }
512 |
513 | "@volar/source-map@2.3.0":
514 | resolution:
515 | {
516 | integrity: sha512-G/228aZjAOGhDjhlyZ++nDbKrS9uk+5DMaEstjvzglaAw7nqtDyhnQAsYzUg6BMP9BtwZ59RIw5HGePrutn00Q==,
517 | }
518 |
519 | "@volar/typescript@2.3.0":
520 | resolution:
521 | {
522 | integrity: sha512-PtUwMM87WsKVeLJN33GSTUjBexlKfKgouWlOUIv7pjrOnTwhXHZNSmpc312xgXdTjQPpToK6KXSIcKu9sBQ5LQ==,
523 | }
524 |
525 | "@vue/compiler-core@3.4.29":
526 | resolution:
527 | {
528 | integrity: sha512-TFKiRkKKsRCKvg/jTSSKK7mYLJEQdUiUfykbG49rubC9SfDyvT2JrzTReopWlz2MxqeLyxh9UZhvxEIBgAhtrg==,
529 | }
530 |
531 | "@vue/compiler-dom@3.4.29":
532 | resolution:
533 | {
534 | integrity: sha512-A6+iZ2fKIEGnfPJejdB7b1FlJzgiD+Y/sxxKwJWg1EbJu6ZPgzaPQQ51ESGNv0CP6jm6Z7/pO6Ia8Ze6IKrX7w==,
535 | }
536 |
537 | "@vue/compiler-sfc@3.4.29":
538 | resolution:
539 | {
540 | integrity: sha512-zygDcEtn8ZimDlrEQyLUovoWgKQic6aEQqRXce2WXBvSeHbEbcAsXyCk9oG33ZkyWH4sl9D3tkYc1idoOkdqZQ==,
541 | }
542 |
543 | "@vue/compiler-ssr@3.4.29":
544 | resolution:
545 | {
546 | integrity: sha512-rFbwCmxJ16tDp3N8XCx5xSQzjhidYjXllvEcqX/lopkoznlNPz3jyy0WGJCyhAaVQK677WWFt3YO/WUEkMMUFQ==,
547 | }
548 |
549 | "@vue/language-core@2.0.21":
550 | resolution:
551 | {
552 | integrity: sha512-vjs6KwnCK++kIXT+eI63BGpJHfHNVJcUCr3RnvJsccT3vbJnZV5IhHR2puEkoOkIbDdp0Gqi1wEnv3hEd3WsxQ==,
553 | }
554 | peerDependencies:
555 | typescript: "*"
556 | peerDependenciesMeta:
557 | typescript:
558 | optional: true
559 |
560 | "@vue/reactivity@3.4.29":
561 | resolution:
562 | {
563 | integrity: sha512-w8+KV+mb1a8ornnGQitnMdLfE0kXmteaxLdccm2XwdFxXst4q/Z7SEboCV5SqJNpZbKFeaRBBJBhW24aJyGINg==,
564 | }
565 |
566 | "@vue/runtime-core@3.4.29":
567 | resolution:
568 | {
569 | integrity: sha512-s8fmX3YVR/Rk5ig0ic0NuzTNjK2M7iLuVSZyMmCzN/+Mjuqqif1JasCtEtmtoJWF32pAtUjyuT2ljNKNLeOmnQ==,
570 | }
571 |
572 | "@vue/runtime-dom@3.4.29":
573 | resolution:
574 | {
575 | integrity: sha512-gI10atCrtOLf/2MPPMM+dpz3NGulo9ZZR9d1dWo4fYvm+xkfvRrw1ZmJ7mkWtiJVXSsdmPbcK1p5dZzOCKDN0g==,
576 | }
577 |
578 | "@vue/server-renderer@3.4.29":
579 | resolution:
580 | {
581 | integrity: sha512-HMLCmPI2j/k8PVkSBysrA2RxcxC5DgBiCdj7n7H2QtR8bQQPqKAe8qoaxLcInzouBmzwJ+J0x20ygN/B5mYBng==,
582 | }
583 | peerDependencies:
584 | vue: 3.4.29
585 |
586 | "@vue/shared@3.4.29":
587 | resolution:
588 | {
589 | integrity: sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==,
590 | }
591 |
592 | "@vuetify/loader-shared@2.0.3":
593 | resolution:
594 | {
595 | integrity: sha512-Ss3GC7eJYkp2SF6xVzsT7FAruEmdihmn4OCk2+UocREerlXKWgOKKzTN5PN3ZVN5q05jHHrsNhTuWbhN61Bpdg==,
596 | }
597 | peerDependencies:
598 | vue: ^3.0.0
599 | vuetify: ^3.0.0
600 |
601 | agent-base@7.1.1:
602 | resolution:
603 | {
604 | integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==,
605 | }
606 | engines: { node: ">= 14" }
607 |
608 | anymatch@3.1.3:
609 | resolution:
610 | {
611 | integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==,
612 | }
613 | engines: { node: ">= 8" }
614 |
615 | asynckit@0.4.0:
616 | resolution:
617 | {
618 | integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==,
619 | }
620 |
621 | balanced-match@1.0.2:
622 | resolution:
623 | {
624 | integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==,
625 | }
626 |
627 | binary-extensions@2.3.0:
628 | resolution:
629 | {
630 | integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==,
631 | }
632 | engines: { node: ">=8" }
633 |
634 | blockly@11.1.1:
635 | resolution:
636 | {
637 | integrity: sha512-PmInYM9zH1HcYMffqnfmeu2O3g0intsowy08S0KDu3q8/95TfGo1tcDYpeWNQDkPOEzN1yy3oocsRO4NPDHtKA==,
638 | }
639 | engines: { node: ">=18" }
640 |
641 | brace-expansion@2.0.1:
642 | resolution:
643 | {
644 | integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==,
645 | }
646 |
647 | braces@3.0.3:
648 | resolution:
649 | {
650 | integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==,
651 | }
652 | engines: { node: ">=8" }
653 |
654 | chokidar@3.6.0:
655 | resolution:
656 | {
657 | integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==,
658 | }
659 | engines: { node: ">= 8.10.0" }
660 |
661 | combined-stream@1.0.8:
662 | resolution:
663 | {
664 | integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==,
665 | }
666 | engines: { node: ">= 0.8" }
667 |
668 | computeds@0.0.1:
669 | resolution:
670 | {
671 | integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==,
672 | }
673 |
674 | core-util-is@1.0.3:
675 | resolution:
676 | {
677 | integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==,
678 | }
679 |
680 | cssstyle@3.0.0:
681 | resolution:
682 | {
683 | integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==,
684 | }
685 | engines: { node: ">=14" }
686 |
687 | csstype@3.1.3:
688 | resolution:
689 | {
690 | integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==,
691 | }
692 |
693 | data-urls@5.0.0:
694 | resolution:
695 | {
696 | integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==,
697 | }
698 | engines: { node: ">=18" }
699 |
700 | de-indent@1.0.2:
701 | resolution:
702 | {
703 | integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==,
704 | }
705 |
706 | debug@4.3.5:
707 | resolution:
708 | {
709 | integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==,
710 | }
711 | engines: { node: ">=6.0" }
712 | peerDependencies:
713 | supports-color: "*"
714 | peerDependenciesMeta:
715 | supports-color:
716 | optional: true
717 |
718 | decimal.js@10.4.3:
719 | resolution:
720 | {
721 | integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==,
722 | }
723 |
724 | delayed-stream@1.0.0:
725 | resolution:
726 | {
727 | integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==,
728 | }
729 | engines: { node: ">=0.4.0" }
730 |
731 | entities@4.5.0:
732 | resolution:
733 | {
734 | integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==,
735 | }
736 | engines: { node: ">=0.12" }
737 |
738 | esbuild@0.21.5:
739 | resolution:
740 | {
741 | integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==,
742 | }
743 | engines: { node: ">=12" }
744 | hasBin: true
745 |
746 | estree-walker@2.0.2:
747 | resolution:
748 | {
749 | integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==,
750 | }
751 |
752 | fast-glob@3.3.2:
753 | resolution:
754 | {
755 | integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==,
756 | }
757 | engines: { node: ">=8.6.0" }
758 |
759 | fastq@1.17.1:
760 | resolution:
761 | {
762 | integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==,
763 | }
764 |
765 | file-saver@2.0.5:
766 | resolution:
767 | {
768 | integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==,
769 | }
770 |
771 | fill-range@7.1.1:
772 | resolution:
773 | {
774 | integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==,
775 | }
776 | engines: { node: ">=8" }
777 |
778 | form-data@4.0.0:
779 | resolution:
780 | {
781 | integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==,
782 | }
783 | engines: { node: ">= 6" }
784 |
785 | fs-extra@11.2.0:
786 | resolution:
787 | {
788 | integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==,
789 | }
790 | engines: { node: ">=14.14" }
791 |
792 | fsevents@2.3.3:
793 | resolution:
794 | {
795 | integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==,
796 | }
797 | engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 }
798 | os: [darwin]
799 |
800 | glob-parent@5.1.2:
801 | resolution:
802 | {
803 | integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==,
804 | }
805 | engines: { node: ">= 6" }
806 |
807 | graceful-fs@4.2.11:
808 | resolution:
809 | {
810 | integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==,
811 | }
812 |
813 | he@1.2.0:
814 | resolution:
815 | {
816 | integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==,
817 | }
818 | hasBin: true
819 |
820 | highlight.js@11.9.0:
821 | resolution:
822 | {
823 | integrity: sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==,
824 | }
825 | engines: { node: ">=12.0.0" }
826 |
827 | html-encoding-sniffer@4.0.0:
828 | resolution:
829 | {
830 | integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==,
831 | }
832 | engines: { node: ">=18" }
833 |
834 | http-proxy-agent@7.0.2:
835 | resolution:
836 | {
837 | integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==,
838 | }
839 | engines: { node: ">= 14" }
840 |
841 | https-proxy-agent@7.0.4:
842 | resolution:
843 | {
844 | integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==,
845 | }
846 | engines: { node: ">= 14" }
847 |
848 | iconv-lite@0.6.3:
849 | resolution:
850 | {
851 | integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==,
852 | }
853 | engines: { node: ">=0.10.0" }
854 |
855 | immediate@3.0.6:
856 | resolution:
857 | {
858 | integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==,
859 | }
860 |
861 | immutable@4.3.6:
862 | resolution:
863 | {
864 | integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==,
865 | }
866 |
867 | inherits@2.0.4:
868 | resolution:
869 | {
870 | integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==,
871 | }
872 |
873 | is-binary-path@2.1.0:
874 | resolution:
875 | {
876 | integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==,
877 | }
878 | engines: { node: ">=8" }
879 |
880 | is-extglob@2.1.1:
881 | resolution:
882 | {
883 | integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==,
884 | }
885 | engines: { node: ">=0.10.0" }
886 |
887 | is-glob@4.0.3:
888 | resolution:
889 | {
890 | integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==,
891 | }
892 | engines: { node: ">=0.10.0" }
893 |
894 | is-number@7.0.0:
895 | resolution:
896 | {
897 | integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==,
898 | }
899 | engines: { node: ">=0.12.0" }
900 |
901 | is-potential-custom-element-name@1.0.1:
902 | resolution:
903 | {
904 | integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==,
905 | }
906 |
907 | isarray@1.0.0:
908 | resolution:
909 | {
910 | integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==,
911 | }
912 |
913 | jsdom@23.0.0:
914 | resolution:
915 | {
916 | integrity: sha512-cbL/UCtohJguhFC7c2/hgW6BeZCNvP7URQGnx9tSJRYKCdnfbfWOrtuLTMfiB2VxKsx5wPHVsh/J0aBy9lIIhQ==,
917 | }
918 | engines: { node: ">=18" }
919 | peerDependencies:
920 | canvas: ^3.0.0
921 | peerDependenciesMeta:
922 | canvas:
923 | optional: true
924 |
925 | jsonfile@6.1.0:
926 | resolution:
927 | {
928 | integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==,
929 | }
930 |
931 | jszip@3.10.1:
932 | resolution:
933 | {
934 | integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==,
935 | }
936 |
937 | lie@3.3.0:
938 | resolution:
939 | {
940 | integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==,
941 | }
942 |
943 | magic-string@0.30.10:
944 | resolution:
945 | {
946 | integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==,
947 | }
948 |
949 | merge2@1.4.1:
950 | resolution:
951 | {
952 | integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==,
953 | }
954 | engines: { node: ">= 8" }
955 |
956 | micromatch@4.0.7:
957 | resolution:
958 | {
959 | integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==,
960 | }
961 | engines: { node: ">=8.6" }
962 |
963 | mime-db@1.52.0:
964 | resolution:
965 | {
966 | integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==,
967 | }
968 | engines: { node: ">= 0.6" }
969 |
970 | mime-types@2.1.35:
971 | resolution:
972 | {
973 | integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==,
974 | }
975 | engines: { node: ">= 0.6" }
976 |
977 | minimatch@9.0.4:
978 | resolution:
979 | {
980 | integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==,
981 | }
982 | engines: { node: ">=16 || 14 >=14.17" }
983 |
984 | ms@2.1.2:
985 | resolution:
986 | {
987 | integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==,
988 | }
989 |
990 | muggle-string@0.4.1:
991 | resolution:
992 | {
993 | integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==,
994 | }
995 |
996 | nanoid@3.3.7:
997 | resolution:
998 | {
999 | integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==,
1000 | }
1001 | engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 }
1002 | hasBin: true
1003 |
1004 | normalize-path@3.0.0:
1005 | resolution:
1006 | {
1007 | integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==,
1008 | }
1009 | engines: { node: ">=0.10.0" }
1010 |
1011 | nwsapi@2.2.10:
1012 | resolution:
1013 | {
1014 | integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==,
1015 | }
1016 |
1017 | pako@1.0.11:
1018 | resolution:
1019 | {
1020 | integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==,
1021 | }
1022 |
1023 | parse5@7.1.2:
1024 | resolution:
1025 | {
1026 | integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==,
1027 | }
1028 |
1029 | path-browserify@1.0.1:
1030 | resolution:
1031 | {
1032 | integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==,
1033 | }
1034 |
1035 | picocolors@1.0.1:
1036 | resolution:
1037 | {
1038 | integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==,
1039 | }
1040 |
1041 | picomatch@2.3.1:
1042 | resolution:
1043 | {
1044 | integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==,
1045 | }
1046 | engines: { node: ">=8.6" }
1047 |
1048 | postcss@8.4.38:
1049 | resolution:
1050 | {
1051 | integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==,
1052 | }
1053 | engines: { node: ^10 || ^12 || >=14 }
1054 |
1055 | prettier@3.3.2:
1056 | resolution:
1057 | {
1058 | integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==,
1059 | }
1060 | engines: { node: ">=14" }
1061 | hasBin: true
1062 |
1063 | process-nextick-args@2.0.1:
1064 | resolution:
1065 | {
1066 | integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==,
1067 | }
1068 |
1069 | psl@1.9.0:
1070 | resolution:
1071 | {
1072 | integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==,
1073 | }
1074 |
1075 | punycode@2.3.1:
1076 | resolution:
1077 | {
1078 | integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==,
1079 | }
1080 | engines: { node: ">=6" }
1081 |
1082 | querystringify@2.2.0:
1083 | resolution:
1084 | {
1085 | integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==,
1086 | }
1087 |
1088 | queue-microtask@1.2.3:
1089 | resolution:
1090 | {
1091 | integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==,
1092 | }
1093 |
1094 | readable-stream@2.3.8:
1095 | resolution:
1096 | {
1097 | integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==,
1098 | }
1099 |
1100 | readdirp@3.6.0:
1101 | resolution:
1102 | {
1103 | integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==,
1104 | }
1105 | engines: { node: ">=8.10.0" }
1106 |
1107 | requires-port@1.0.0:
1108 | resolution:
1109 | {
1110 | integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==,
1111 | }
1112 |
1113 | reusify@1.0.4:
1114 | resolution:
1115 | {
1116 | integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==,
1117 | }
1118 | engines: { iojs: ">=1.0.0", node: ">=0.10.0" }
1119 |
1120 | rollup@4.18.0:
1121 | resolution:
1122 | {
1123 | integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==,
1124 | }
1125 | engines: { node: ">=18.0.0", npm: ">=8.0.0" }
1126 | hasBin: true
1127 |
1128 | rrweb-cssom@0.6.0:
1129 | resolution:
1130 | {
1131 | integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==,
1132 | }
1133 |
1134 | run-parallel@1.2.0:
1135 | resolution:
1136 | {
1137 | integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==,
1138 | }
1139 |
1140 | safe-buffer@5.1.2:
1141 | resolution:
1142 | {
1143 | integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==,
1144 | }
1145 |
1146 | safer-buffer@2.1.2:
1147 | resolution:
1148 | {
1149 | integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==,
1150 | }
1151 |
1152 | sass@1.77.6:
1153 | resolution:
1154 | {
1155 | integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==,
1156 | }
1157 | engines: { node: ">=14.0.0" }
1158 | hasBin: true
1159 |
1160 | saxes@6.0.0:
1161 | resolution:
1162 | {
1163 | integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==,
1164 | }
1165 | engines: { node: ">=v12.22.7" }
1166 |
1167 | semver@7.6.2:
1168 | resolution:
1169 | {
1170 | integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==,
1171 | }
1172 | engines: { node: ">=10" }
1173 | hasBin: true
1174 |
1175 | setimmediate@1.0.5:
1176 | resolution:
1177 | {
1178 | integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==,
1179 | }
1180 |
1181 | source-map-js@1.2.0:
1182 | resolution:
1183 | {
1184 | integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==,
1185 | }
1186 | engines: { node: ">=0.10.0" }
1187 |
1188 | string_decoder@1.1.1:
1189 | resolution:
1190 | {
1191 | integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==,
1192 | }
1193 |
1194 | symbol-tree@3.2.4:
1195 | resolution:
1196 | {
1197 | integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==,
1198 | }
1199 |
1200 | to-fast-properties@2.0.0:
1201 | resolution:
1202 | {
1203 | integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==,
1204 | }
1205 | engines: { node: ">=4" }
1206 |
1207 | to-regex-range@5.0.1:
1208 | resolution:
1209 | {
1210 | integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==,
1211 | }
1212 | engines: { node: ">=8.0" }
1213 |
1214 | tough-cookie@4.1.4:
1215 | resolution:
1216 | {
1217 | integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==,
1218 | }
1219 | engines: { node: ">=6" }
1220 |
1221 | tr46@5.0.0:
1222 | resolution:
1223 | {
1224 | integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==,
1225 | }
1226 | engines: { node: ">=18" }
1227 |
1228 | typescript@5.5.2:
1229 | resolution:
1230 | {
1231 | integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==,
1232 | }
1233 | engines: { node: ">=14.17" }
1234 | hasBin: true
1235 |
1236 | universalify@0.2.0:
1237 | resolution:
1238 | {
1239 | integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==,
1240 | }
1241 | engines: { node: ">= 4.0.0" }
1242 |
1243 | universalify@2.0.1:
1244 | resolution:
1245 | {
1246 | integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==,
1247 | }
1248 | engines: { node: ">= 10.0.0" }
1249 |
1250 | upath@2.0.1:
1251 | resolution:
1252 | {
1253 | integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==,
1254 | }
1255 | engines: { node: ">=4" }
1256 |
1257 | url-parse@1.5.10:
1258 | resolution:
1259 | {
1260 | integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==,
1261 | }
1262 |
1263 | util-deprecate@1.0.2:
1264 | resolution:
1265 | {
1266 | integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==,
1267 | }
1268 |
1269 | vite-plugin-static-copy@1.0.5:
1270 | resolution:
1271 | {
1272 | integrity: sha512-02k0Rox+buYdEOfeilKZSgs1gXfPf9RjVztZEIYZgVIxjsVZi6AXssjzdi+qW6zYt00d3bq+tpP2voVXN2fKLw==,
1273 | }
1274 | engines: { node: ^18.0.0 || >=20.0.0 }
1275 | peerDependencies:
1276 | vite: ^5.0.0
1277 |
1278 | vite-plugin-vuetify@2.0.3:
1279 | resolution:
1280 | {
1281 | integrity: sha512-HbYajgGgb/noaVKNRhnnXIiQZrNXfNIeanUGAwXgOxL6h/KULS40Uf51Kyz8hNmdegF+DwjgXXI/8J1PNS83xw==,
1282 | }
1283 | engines: { node: ^18.0.0 || >=20.0.0 }
1284 | peerDependencies:
1285 | vite: ">=5"
1286 | vue: ^3.0.0
1287 | vuetify: ^3.0.0
1288 |
1289 | vite@5.3.1:
1290 | resolution:
1291 | {
1292 | integrity: sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==,
1293 | }
1294 | engines: { node: ^18.0.0 || >=20.0.0 }
1295 | hasBin: true
1296 | peerDependencies:
1297 | "@types/node": ^18.0.0 || >=20.0.0
1298 | less: "*"
1299 | lightningcss: ^1.21.0
1300 | sass: "*"
1301 | stylus: "*"
1302 | sugarss: "*"
1303 | terser: ^5.4.0
1304 | peerDependenciesMeta:
1305 | "@types/node":
1306 | optional: true
1307 | less:
1308 | optional: true
1309 | lightningcss:
1310 | optional: true
1311 | sass:
1312 | optional: true
1313 | stylus:
1314 | optional: true
1315 | sugarss:
1316 | optional: true
1317 | terser:
1318 | optional: true
1319 |
1320 | vscode-uri@3.0.8:
1321 | resolution:
1322 | {
1323 | integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==,
1324 | }
1325 |
1326 | vue-template-compiler@2.7.16:
1327 | resolution:
1328 | {
1329 | integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==,
1330 | }
1331 |
1332 | vue-tsc@2.0.21:
1333 | resolution:
1334 | {
1335 | integrity: sha512-E6x1p1HaHES6Doy8pqtm7kQern79zRtIewkf9fiv7Y43Zo4AFDS5hKi+iHi2RwEhqRmuiwliB1LCEFEGwvxQnw==,
1336 | }
1337 | hasBin: true
1338 | peerDependencies:
1339 | typescript: "*"
1340 |
1341 | vue@3.4.29:
1342 | resolution:
1343 | {
1344 | integrity: sha512-8QUYfRcYzNlYuzKPfge1UWC6nF9ym0lx7mpGVPJYNhddxEf3DD0+kU07NTL0sXuiT2HuJuKr/iEO8WvXvT0RSQ==,
1345 | }
1346 | peerDependencies:
1347 | typescript: "*"
1348 | peerDependenciesMeta:
1349 | typescript:
1350 | optional: true
1351 |
1352 | vuetify@3.6.10:
1353 | resolution:
1354 | {
1355 | integrity: sha512-Myd9+EFq4Gmu61yKPNVS0QdGQkcZ9cHom27wuvRw7jgDxM+X4MT9BwQRk/Dt1q3G3JlK8oh+ZYyq5Ps/Z73cMg==,
1356 | }
1357 | engines: { node: ^12.20 || >=14.13 }
1358 | peerDependencies:
1359 | typescript: ">=4.7"
1360 | vite-plugin-vuetify: ">=1.0.0"
1361 | vue: ^3.3.0
1362 | vue-i18n: ^9.0.0
1363 | webpack-plugin-vuetify: ">=2.0.0"
1364 | peerDependenciesMeta:
1365 | typescript:
1366 | optional: true
1367 | vite-plugin-vuetify:
1368 | optional: true
1369 | vue-i18n:
1370 | optional: true
1371 | webpack-plugin-vuetify:
1372 | optional: true
1373 |
1374 | w3c-xmlserializer@5.0.0:
1375 | resolution:
1376 | {
1377 | integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==,
1378 | }
1379 | engines: { node: ">=18" }
1380 |
1381 | webidl-conversions@7.0.0:
1382 | resolution:
1383 | {
1384 | integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==,
1385 | }
1386 | engines: { node: ">=12" }
1387 |
1388 | whatwg-encoding@3.1.1:
1389 | resolution:
1390 | {
1391 | integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==,
1392 | }
1393 | engines: { node: ">=18" }
1394 |
1395 | whatwg-mimetype@4.0.0:
1396 | resolution:
1397 | {
1398 | integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==,
1399 | }
1400 | engines: { node: ">=18" }
1401 |
1402 | whatwg-url@14.0.0:
1403 | resolution:
1404 | {
1405 | integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==,
1406 | }
1407 | engines: { node: ">=18" }
1408 |
1409 | ws@8.17.1:
1410 | resolution:
1411 | {
1412 | integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==,
1413 | }
1414 | engines: { node: ">=10.0.0" }
1415 | peerDependencies:
1416 | bufferutil: ^4.0.1
1417 | utf-8-validate: ">=5.0.2"
1418 | peerDependenciesMeta:
1419 | bufferutil:
1420 | optional: true
1421 | utf-8-validate:
1422 | optional: true
1423 |
1424 | xml-name-validator@5.0.0:
1425 | resolution:
1426 | {
1427 | integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==,
1428 | }
1429 | engines: { node: ">=18" }
1430 |
1431 | xmlchars@2.2.0:
1432 | resolution:
1433 | {
1434 | integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==,
1435 | }
1436 |
1437 | snapshots:
1438 | "@babel/helper-string-parser@7.24.7": {}
1439 |
1440 | "@babel/helper-validator-identifier@7.24.7": {}
1441 |
1442 | "@babel/parser@7.24.7":
1443 | dependencies:
1444 | "@babel/types": 7.24.7
1445 |
1446 | "@babel/types@7.24.7":
1447 | dependencies:
1448 | "@babel/helper-string-parser": 7.24.7
1449 | "@babel/helper-validator-identifier": 7.24.7
1450 | to-fast-properties: 2.0.0
1451 |
1452 | "@blockly/theme-dark@7.0.1(blockly@11.1.1)":
1453 | dependencies:
1454 | blockly: 11.1.1
1455 |
1456 | "@esbuild/aix-ppc64@0.21.5":
1457 | optional: true
1458 |
1459 | "@esbuild/android-arm64@0.21.5":
1460 | optional: true
1461 |
1462 | "@esbuild/android-arm@0.21.5":
1463 | optional: true
1464 |
1465 | "@esbuild/android-x64@0.21.5":
1466 | optional: true
1467 |
1468 | "@esbuild/darwin-arm64@0.21.5":
1469 | optional: true
1470 |
1471 | "@esbuild/darwin-x64@0.21.5":
1472 | optional: true
1473 |
1474 | "@esbuild/freebsd-arm64@0.21.5":
1475 | optional: true
1476 |
1477 | "@esbuild/freebsd-x64@0.21.5":
1478 | optional: true
1479 |
1480 | "@esbuild/linux-arm64@0.21.5":
1481 | optional: true
1482 |
1483 | "@esbuild/linux-arm@0.21.5":
1484 | optional: true
1485 |
1486 | "@esbuild/linux-ia32@0.21.5":
1487 | optional: true
1488 |
1489 | "@esbuild/linux-loong64@0.21.5":
1490 | optional: true
1491 |
1492 | "@esbuild/linux-mips64el@0.21.5":
1493 | optional: true
1494 |
1495 | "@esbuild/linux-ppc64@0.21.5":
1496 | optional: true
1497 |
1498 | "@esbuild/linux-riscv64@0.21.5":
1499 | optional: true
1500 |
1501 | "@esbuild/linux-s390x@0.21.5":
1502 | optional: true
1503 |
1504 | "@esbuild/linux-x64@0.21.5":
1505 | optional: true
1506 |
1507 | "@esbuild/netbsd-x64@0.21.5":
1508 | optional: true
1509 |
1510 | "@esbuild/openbsd-x64@0.21.5":
1511 | optional: true
1512 |
1513 | "@esbuild/sunos-x64@0.21.5":
1514 | optional: true
1515 |
1516 | "@esbuild/win32-arm64@0.21.5":
1517 | optional: true
1518 |
1519 | "@esbuild/win32-ia32@0.21.5":
1520 | optional: true
1521 |
1522 | "@esbuild/win32-x64@0.21.5":
1523 | optional: true
1524 |
1525 | "@highlightjs/vue-plugin@2.1.0(highlight.js@11.9.0)(vue@3.4.29(typescript@5.5.2))":
1526 | dependencies:
1527 | highlight.js: 11.9.0
1528 | vue: 3.4.29(typescript@5.5.2)
1529 |
1530 | "@jridgewell/sourcemap-codec@1.4.15": {}
1531 |
1532 | "@mdi/js@7.4.47": {}
1533 |
1534 | "@nodelib/fs.scandir@2.1.5":
1535 | dependencies:
1536 | "@nodelib/fs.stat": 2.0.5
1537 | run-parallel: 1.2.0
1538 |
1539 | "@nodelib/fs.stat@2.0.5": {}
1540 |
1541 | "@nodelib/fs.walk@1.2.8":
1542 | dependencies:
1543 | "@nodelib/fs.scandir": 2.1.5
1544 | fastq: 1.17.1
1545 |
1546 | "@rollup/rollup-android-arm-eabi@4.18.0":
1547 | optional: true
1548 |
1549 | "@rollup/rollup-android-arm64@4.18.0":
1550 | optional: true
1551 |
1552 | "@rollup/rollup-darwin-arm64@4.18.0":
1553 | optional: true
1554 |
1555 | "@rollup/rollup-darwin-x64@4.18.0":
1556 | optional: true
1557 |
1558 | "@rollup/rollup-linux-arm-gnueabihf@4.18.0":
1559 | optional: true
1560 |
1561 | "@rollup/rollup-linux-arm-musleabihf@4.18.0":
1562 | optional: true
1563 |
1564 | "@rollup/rollup-linux-arm64-gnu@4.18.0":
1565 | optional: true
1566 |
1567 | "@rollup/rollup-linux-arm64-musl@4.18.0":
1568 | optional: true
1569 |
1570 | "@rollup/rollup-linux-powerpc64le-gnu@4.18.0":
1571 | optional: true
1572 |
1573 | "@rollup/rollup-linux-riscv64-gnu@4.18.0":
1574 | optional: true
1575 |
1576 | "@rollup/rollup-linux-s390x-gnu@4.18.0":
1577 | optional: true
1578 |
1579 | "@rollup/rollup-linux-x64-gnu@4.18.0":
1580 | optional: true
1581 |
1582 | "@rollup/rollup-linux-x64-musl@4.18.0":
1583 | optional: true
1584 |
1585 | "@rollup/rollup-win32-arm64-msvc@4.18.0":
1586 | optional: true
1587 |
1588 | "@rollup/rollup-win32-ia32-msvc@4.18.0":
1589 | optional: true
1590 |
1591 | "@rollup/rollup-win32-x64-msvc@4.18.0":
1592 | optional: true
1593 |
1594 | "@types/estree@1.0.5": {}
1595 |
1596 | "@types/file-saver@2.0.7": {}
1597 |
1598 | "@vitejs/plugin-vue@5.0.5(vite@5.3.1(sass@1.77.6))(vue@3.4.29(typescript@5.5.2))":
1599 | dependencies:
1600 | vite: 5.3.1(sass@1.77.6)
1601 | vue: 3.4.29(typescript@5.5.2)
1602 |
1603 | "@volar/language-core@2.3.0":
1604 | dependencies:
1605 | "@volar/source-map": 2.3.0
1606 |
1607 | "@volar/source-map@2.3.0":
1608 | dependencies:
1609 | muggle-string: 0.4.1
1610 |
1611 | "@volar/typescript@2.3.0":
1612 | dependencies:
1613 | "@volar/language-core": 2.3.0
1614 | path-browserify: 1.0.1
1615 | vscode-uri: 3.0.8
1616 |
1617 | "@vue/compiler-core@3.4.29":
1618 | dependencies:
1619 | "@babel/parser": 7.24.7
1620 | "@vue/shared": 3.4.29
1621 | entities: 4.5.0
1622 | estree-walker: 2.0.2
1623 | source-map-js: 1.2.0
1624 |
1625 | "@vue/compiler-dom@3.4.29":
1626 | dependencies:
1627 | "@vue/compiler-core": 3.4.29
1628 | "@vue/shared": 3.4.29
1629 |
1630 | "@vue/compiler-sfc@3.4.29":
1631 | dependencies:
1632 | "@babel/parser": 7.24.7
1633 | "@vue/compiler-core": 3.4.29
1634 | "@vue/compiler-dom": 3.4.29
1635 | "@vue/compiler-ssr": 3.4.29
1636 | "@vue/shared": 3.4.29
1637 | estree-walker: 2.0.2
1638 | magic-string: 0.30.10
1639 | postcss: 8.4.38
1640 | source-map-js: 1.2.0
1641 |
1642 | "@vue/compiler-ssr@3.4.29":
1643 | dependencies:
1644 | "@vue/compiler-dom": 3.4.29
1645 | "@vue/shared": 3.4.29
1646 |
1647 | "@vue/language-core@2.0.21(typescript@5.5.2)":
1648 | dependencies:
1649 | "@volar/language-core": 2.3.0
1650 | "@vue/compiler-dom": 3.4.29
1651 | "@vue/shared": 3.4.29
1652 | computeds: 0.0.1
1653 | minimatch: 9.0.4
1654 | path-browserify: 1.0.1
1655 | vue-template-compiler: 2.7.16
1656 | optionalDependencies:
1657 | typescript: 5.5.2
1658 |
1659 | "@vue/reactivity@3.4.29":
1660 | dependencies:
1661 | "@vue/shared": 3.4.29
1662 |
1663 | "@vue/runtime-core@3.4.29":
1664 | dependencies:
1665 | "@vue/reactivity": 3.4.29
1666 | "@vue/shared": 3.4.29
1667 |
1668 | "@vue/runtime-dom@3.4.29":
1669 | dependencies:
1670 | "@vue/reactivity": 3.4.29
1671 | "@vue/runtime-core": 3.4.29
1672 | "@vue/shared": 3.4.29
1673 | csstype: 3.1.3
1674 |
1675 | "@vue/server-renderer@3.4.29(vue@3.4.29(typescript@5.5.2))":
1676 | dependencies:
1677 | "@vue/compiler-ssr": 3.4.29
1678 | "@vue/shared": 3.4.29
1679 | vue: 3.4.29(typescript@5.5.2)
1680 |
1681 | "@vue/shared@3.4.29": {}
1682 |
1683 | "@vuetify/loader-shared@2.0.3(vue@3.4.29(typescript@5.5.2))(vuetify@3.6.10(typescript@5.5.2)(vite-plugin-vuetify@2.0.3)(vue@3.4.29(typescript@5.5.2)))":
1684 | dependencies:
1685 | upath: 2.0.1
1686 | vue: 3.4.29(typescript@5.5.2)
1687 | vuetify: 3.6.10(typescript@5.5.2)(vite-plugin-vuetify@2.0.3)(vue@3.4.29(typescript@5.5.2))
1688 |
1689 | agent-base@7.1.1:
1690 | dependencies:
1691 | debug: 4.3.5
1692 | transitivePeerDependencies:
1693 | - supports-color
1694 |
1695 | anymatch@3.1.3:
1696 | dependencies:
1697 | normalize-path: 3.0.0
1698 | picomatch: 2.3.1
1699 |
1700 | asynckit@0.4.0: {}
1701 |
1702 | balanced-match@1.0.2: {}
1703 |
1704 | binary-extensions@2.3.0: {}
1705 |
1706 | blockly@11.1.1:
1707 | dependencies:
1708 | jsdom: 23.0.0
1709 | transitivePeerDependencies:
1710 | - bufferutil
1711 | - canvas
1712 | - supports-color
1713 | - utf-8-validate
1714 |
1715 | brace-expansion@2.0.1:
1716 | dependencies:
1717 | balanced-match: 1.0.2
1718 |
1719 | braces@3.0.3:
1720 | dependencies:
1721 | fill-range: 7.1.1
1722 |
1723 | chokidar@3.6.0:
1724 | dependencies:
1725 | anymatch: 3.1.3
1726 | braces: 3.0.3
1727 | glob-parent: 5.1.2
1728 | is-binary-path: 2.1.0
1729 | is-glob: 4.0.3
1730 | normalize-path: 3.0.0
1731 | readdirp: 3.6.0
1732 | optionalDependencies:
1733 | fsevents: 2.3.3
1734 |
1735 | combined-stream@1.0.8:
1736 | dependencies:
1737 | delayed-stream: 1.0.0
1738 |
1739 | computeds@0.0.1: {}
1740 |
1741 | core-util-is@1.0.3: {}
1742 |
1743 | cssstyle@3.0.0:
1744 | dependencies:
1745 | rrweb-cssom: 0.6.0
1746 |
1747 | csstype@3.1.3: {}
1748 |
1749 | data-urls@5.0.0:
1750 | dependencies:
1751 | whatwg-mimetype: 4.0.0
1752 | whatwg-url: 14.0.0
1753 |
1754 | de-indent@1.0.2: {}
1755 |
1756 | debug@4.3.5:
1757 | dependencies:
1758 | ms: 2.1.2
1759 |
1760 | decimal.js@10.4.3: {}
1761 |
1762 | delayed-stream@1.0.0: {}
1763 |
1764 | entities@4.5.0: {}
1765 |
1766 | esbuild@0.21.5:
1767 | optionalDependencies:
1768 | "@esbuild/aix-ppc64": 0.21.5
1769 | "@esbuild/android-arm": 0.21.5
1770 | "@esbuild/android-arm64": 0.21.5
1771 | "@esbuild/android-x64": 0.21.5
1772 | "@esbuild/darwin-arm64": 0.21.5
1773 | "@esbuild/darwin-x64": 0.21.5
1774 | "@esbuild/freebsd-arm64": 0.21.5
1775 | "@esbuild/freebsd-x64": 0.21.5
1776 | "@esbuild/linux-arm": 0.21.5
1777 | "@esbuild/linux-arm64": 0.21.5
1778 | "@esbuild/linux-ia32": 0.21.5
1779 | "@esbuild/linux-loong64": 0.21.5
1780 | "@esbuild/linux-mips64el": 0.21.5
1781 | "@esbuild/linux-ppc64": 0.21.5
1782 | "@esbuild/linux-riscv64": 0.21.5
1783 | "@esbuild/linux-s390x": 0.21.5
1784 | "@esbuild/linux-x64": 0.21.5
1785 | "@esbuild/netbsd-x64": 0.21.5
1786 | "@esbuild/openbsd-x64": 0.21.5
1787 | "@esbuild/sunos-x64": 0.21.5
1788 | "@esbuild/win32-arm64": 0.21.5
1789 | "@esbuild/win32-ia32": 0.21.5
1790 | "@esbuild/win32-x64": 0.21.5
1791 |
1792 | estree-walker@2.0.2: {}
1793 |
1794 | fast-glob@3.3.2:
1795 | dependencies:
1796 | "@nodelib/fs.stat": 2.0.5
1797 | "@nodelib/fs.walk": 1.2.8
1798 | glob-parent: 5.1.2
1799 | merge2: 1.4.1
1800 | micromatch: 4.0.7
1801 |
1802 | fastq@1.17.1:
1803 | dependencies:
1804 | reusify: 1.0.4
1805 |
1806 | file-saver@2.0.5: {}
1807 |
1808 | fill-range@7.1.1:
1809 | dependencies:
1810 | to-regex-range: 5.0.1
1811 |
1812 | form-data@4.0.0:
1813 | dependencies:
1814 | asynckit: 0.4.0
1815 | combined-stream: 1.0.8
1816 | mime-types: 2.1.35
1817 |
1818 | fs-extra@11.2.0:
1819 | dependencies:
1820 | graceful-fs: 4.2.11
1821 | jsonfile: 6.1.0
1822 | universalify: 2.0.1
1823 |
1824 | fsevents@2.3.3:
1825 | optional: true
1826 |
1827 | glob-parent@5.1.2:
1828 | dependencies:
1829 | is-glob: 4.0.3
1830 |
1831 | graceful-fs@4.2.11: {}
1832 |
1833 | he@1.2.0: {}
1834 |
1835 | highlight.js@11.9.0: {}
1836 |
1837 | html-encoding-sniffer@4.0.0:
1838 | dependencies:
1839 | whatwg-encoding: 3.1.1
1840 |
1841 | http-proxy-agent@7.0.2:
1842 | dependencies:
1843 | agent-base: 7.1.1
1844 | debug: 4.3.5
1845 | transitivePeerDependencies:
1846 | - supports-color
1847 |
1848 | https-proxy-agent@7.0.4:
1849 | dependencies:
1850 | agent-base: 7.1.1
1851 | debug: 4.3.5
1852 | transitivePeerDependencies:
1853 | - supports-color
1854 |
1855 | iconv-lite@0.6.3:
1856 | dependencies:
1857 | safer-buffer: 2.1.2
1858 |
1859 | immediate@3.0.6: {}
1860 |
1861 | immutable@4.3.6: {}
1862 |
1863 | inherits@2.0.4: {}
1864 |
1865 | is-binary-path@2.1.0:
1866 | dependencies:
1867 | binary-extensions: 2.3.0
1868 |
1869 | is-extglob@2.1.1: {}
1870 |
1871 | is-glob@4.0.3:
1872 | dependencies:
1873 | is-extglob: 2.1.1
1874 |
1875 | is-number@7.0.0: {}
1876 |
1877 | is-potential-custom-element-name@1.0.1: {}
1878 |
1879 | isarray@1.0.0: {}
1880 |
1881 | jsdom@23.0.0:
1882 | dependencies:
1883 | cssstyle: 3.0.0
1884 | data-urls: 5.0.0
1885 | decimal.js: 10.4.3
1886 | form-data: 4.0.0
1887 | html-encoding-sniffer: 4.0.0
1888 | http-proxy-agent: 7.0.2
1889 | https-proxy-agent: 7.0.4
1890 | is-potential-custom-element-name: 1.0.1
1891 | nwsapi: 2.2.10
1892 | parse5: 7.1.2
1893 | rrweb-cssom: 0.6.0
1894 | saxes: 6.0.0
1895 | symbol-tree: 3.2.4
1896 | tough-cookie: 4.1.4
1897 | w3c-xmlserializer: 5.0.0
1898 | webidl-conversions: 7.0.0
1899 | whatwg-encoding: 3.1.1
1900 | whatwg-mimetype: 4.0.0
1901 | whatwg-url: 14.0.0
1902 | ws: 8.17.1
1903 | xml-name-validator: 5.0.0
1904 | transitivePeerDependencies:
1905 | - bufferutil
1906 | - supports-color
1907 | - utf-8-validate
1908 |
1909 | jsonfile@6.1.0:
1910 | dependencies:
1911 | universalify: 2.0.1
1912 | optionalDependencies:
1913 | graceful-fs: 4.2.11
1914 |
1915 | jszip@3.10.1:
1916 | dependencies:
1917 | lie: 3.3.0
1918 | pako: 1.0.11
1919 | readable-stream: 2.3.8
1920 | setimmediate: 1.0.5
1921 |
1922 | lie@3.3.0:
1923 | dependencies:
1924 | immediate: 3.0.6
1925 |
1926 | magic-string@0.30.10:
1927 | dependencies:
1928 | "@jridgewell/sourcemap-codec": 1.4.15
1929 |
1930 | merge2@1.4.1: {}
1931 |
1932 | micromatch@4.0.7:
1933 | dependencies:
1934 | braces: 3.0.3
1935 | picomatch: 2.3.1
1936 |
1937 | mime-db@1.52.0: {}
1938 |
1939 | mime-types@2.1.35:
1940 | dependencies:
1941 | mime-db: 1.52.0
1942 |
1943 | minimatch@9.0.4:
1944 | dependencies:
1945 | brace-expansion: 2.0.1
1946 |
1947 | ms@2.1.2: {}
1948 |
1949 | muggle-string@0.4.1: {}
1950 |
1951 | nanoid@3.3.7: {}
1952 |
1953 | normalize-path@3.0.0: {}
1954 |
1955 | nwsapi@2.2.10: {}
1956 |
1957 | pako@1.0.11: {}
1958 |
1959 | parse5@7.1.2:
1960 | dependencies:
1961 | entities: 4.5.0
1962 |
1963 | path-browserify@1.0.1: {}
1964 |
1965 | picocolors@1.0.1: {}
1966 |
1967 | picomatch@2.3.1: {}
1968 |
1969 | postcss@8.4.38:
1970 | dependencies:
1971 | nanoid: 3.3.7
1972 | picocolors: 1.0.1
1973 | source-map-js: 1.2.0
1974 |
1975 | prettier@3.3.2: {}
1976 |
1977 | process-nextick-args@2.0.1: {}
1978 |
1979 | psl@1.9.0: {}
1980 |
1981 | punycode@2.3.1: {}
1982 |
1983 | querystringify@2.2.0: {}
1984 |
1985 | queue-microtask@1.2.3: {}
1986 |
1987 | readable-stream@2.3.8:
1988 | dependencies:
1989 | core-util-is: 1.0.3
1990 | inherits: 2.0.4
1991 | isarray: 1.0.0
1992 | process-nextick-args: 2.0.1
1993 | safe-buffer: 5.1.2
1994 | string_decoder: 1.1.1
1995 | util-deprecate: 1.0.2
1996 |
1997 | readdirp@3.6.0:
1998 | dependencies:
1999 | picomatch: 2.3.1
2000 |
2001 | requires-port@1.0.0: {}
2002 |
2003 | reusify@1.0.4: {}
2004 |
2005 | rollup@4.18.0:
2006 | dependencies:
2007 | "@types/estree": 1.0.5
2008 | optionalDependencies:
2009 | "@rollup/rollup-android-arm-eabi": 4.18.0
2010 | "@rollup/rollup-android-arm64": 4.18.0
2011 | "@rollup/rollup-darwin-arm64": 4.18.0
2012 | "@rollup/rollup-darwin-x64": 4.18.0
2013 | "@rollup/rollup-linux-arm-gnueabihf": 4.18.0
2014 | "@rollup/rollup-linux-arm-musleabihf": 4.18.0
2015 | "@rollup/rollup-linux-arm64-gnu": 4.18.0
2016 | "@rollup/rollup-linux-arm64-musl": 4.18.0
2017 | "@rollup/rollup-linux-powerpc64le-gnu": 4.18.0
2018 | "@rollup/rollup-linux-riscv64-gnu": 4.18.0
2019 | "@rollup/rollup-linux-s390x-gnu": 4.18.0
2020 | "@rollup/rollup-linux-x64-gnu": 4.18.0
2021 | "@rollup/rollup-linux-x64-musl": 4.18.0
2022 | "@rollup/rollup-win32-arm64-msvc": 4.18.0
2023 | "@rollup/rollup-win32-ia32-msvc": 4.18.0
2024 | "@rollup/rollup-win32-x64-msvc": 4.18.0
2025 | fsevents: 2.3.3
2026 |
2027 | rrweb-cssom@0.6.0: {}
2028 |
2029 | run-parallel@1.2.0:
2030 | dependencies:
2031 | queue-microtask: 1.2.3
2032 |
2033 | safe-buffer@5.1.2: {}
2034 |
2035 | safer-buffer@2.1.2: {}
2036 |
2037 | sass@1.77.6:
2038 | dependencies:
2039 | chokidar: 3.6.0
2040 | immutable: 4.3.6
2041 | source-map-js: 1.2.0
2042 |
2043 | saxes@6.0.0:
2044 | dependencies:
2045 | xmlchars: 2.2.0
2046 |
2047 | semver@7.6.2: {}
2048 |
2049 | setimmediate@1.0.5: {}
2050 |
2051 | source-map-js@1.2.0: {}
2052 |
2053 | string_decoder@1.1.1:
2054 | dependencies:
2055 | safe-buffer: 5.1.2
2056 |
2057 | symbol-tree@3.2.4: {}
2058 |
2059 | to-fast-properties@2.0.0: {}
2060 |
2061 | to-regex-range@5.0.1:
2062 | dependencies:
2063 | is-number: 7.0.0
2064 |
2065 | tough-cookie@4.1.4:
2066 | dependencies:
2067 | psl: 1.9.0
2068 | punycode: 2.3.1
2069 | universalify: 0.2.0
2070 | url-parse: 1.5.10
2071 |
2072 | tr46@5.0.0:
2073 | dependencies:
2074 | punycode: 2.3.1
2075 |
2076 | typescript@5.5.2: {}
2077 |
2078 | universalify@0.2.0: {}
2079 |
2080 | universalify@2.0.1: {}
2081 |
2082 | upath@2.0.1: {}
2083 |
2084 | url-parse@1.5.10:
2085 | dependencies:
2086 | querystringify: 2.2.0
2087 | requires-port: 1.0.0
2088 |
2089 | util-deprecate@1.0.2: {}
2090 |
2091 | vite-plugin-static-copy@1.0.5(vite@5.3.1(sass@1.77.6)):
2092 | dependencies:
2093 | chokidar: 3.6.0
2094 | fast-glob: 3.3.2
2095 | fs-extra: 11.2.0
2096 | picocolors: 1.0.1
2097 | vite: 5.3.1(sass@1.77.6)
2098 |
2099 | vite-plugin-vuetify@2.0.3(vite@5.3.1(sass@1.77.6))(vue@3.4.29(typescript@5.5.2))(vuetify@3.6.10):
2100 | dependencies:
2101 | "@vuetify/loader-shared": 2.0.3(vue@3.4.29(typescript@5.5.2))(vuetify@3.6.10(typescript@5.5.2)(vite-plugin-vuetify@2.0.3)(vue@3.4.29(typescript@5.5.2)))
2102 | debug: 4.3.5
2103 | upath: 2.0.1
2104 | vite: 5.3.1(sass@1.77.6)
2105 | vue: 3.4.29(typescript@5.5.2)
2106 | vuetify: 3.6.10(typescript@5.5.2)(vite-plugin-vuetify@2.0.3)(vue@3.4.29(typescript@5.5.2))
2107 | transitivePeerDependencies:
2108 | - supports-color
2109 |
2110 | vite@5.3.1(sass@1.77.6):
2111 | dependencies:
2112 | esbuild: 0.21.5
2113 | postcss: 8.4.38
2114 | rollup: 4.18.0
2115 | optionalDependencies:
2116 | fsevents: 2.3.3
2117 | sass: 1.77.6
2118 |
2119 | vscode-uri@3.0.8: {}
2120 |
2121 | vue-template-compiler@2.7.16:
2122 | dependencies:
2123 | de-indent: 1.0.2
2124 | he: 1.2.0
2125 |
2126 | vue-tsc@2.0.21(typescript@5.5.2):
2127 | dependencies:
2128 | "@volar/typescript": 2.3.0
2129 | "@vue/language-core": 2.0.21(typescript@5.5.2)
2130 | semver: 7.6.2
2131 | typescript: 5.5.2
2132 |
2133 | vue@3.4.29(typescript@5.5.2):
2134 | dependencies:
2135 | "@vue/compiler-dom": 3.4.29
2136 | "@vue/compiler-sfc": 3.4.29
2137 | "@vue/runtime-dom": 3.4.29
2138 | "@vue/server-renderer": 3.4.29(vue@3.4.29(typescript@5.5.2))
2139 | "@vue/shared": 3.4.29
2140 | optionalDependencies:
2141 | typescript: 5.5.2
2142 |
2143 | vuetify@3.6.10(typescript@5.5.2)(vite-plugin-vuetify@2.0.3)(vue@3.4.29(typescript@5.5.2)):
2144 | dependencies:
2145 | vue: 3.4.29(typescript@5.5.2)
2146 | optionalDependencies:
2147 | typescript: 5.5.2
2148 | vite-plugin-vuetify: 2.0.3(vite@5.3.1(sass@1.77.6))(vue@3.4.29(typescript@5.5.2))(vuetify@3.6.10)
2149 |
2150 | w3c-xmlserializer@5.0.0:
2151 | dependencies:
2152 | xml-name-validator: 5.0.0
2153 |
2154 | webidl-conversions@7.0.0: {}
2155 |
2156 | whatwg-encoding@3.1.1:
2157 | dependencies:
2158 | iconv-lite: 0.6.3
2159 |
2160 | whatwg-mimetype@4.0.0: {}
2161 |
2162 | whatwg-url@14.0.0:
2163 | dependencies:
2164 | tr46: 5.0.0
2165 | webidl-conversions: 7.0.0
2166 |
2167 | ws@8.17.1: {}
2168 |
2169 | xml-name-validator@5.0.0: {}
2170 |
2171 | xmlchars@2.2.0: {}
2172 |
--------------------------------------------------------------------------------