├── 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 | 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 | 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 | 51 | -------------------------------------------------------------------------------- /packages/app/src/components/ContentCard.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 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 | 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 | --------------------------------------------------------------------------------