diff --git a/astrbot/cli/__init__.py b/astrbot/cli/__init__.py index 8f71965c0..75e91284a 100644 --- a/astrbot/cli/__init__.py +++ b/astrbot/cli/__init__.py @@ -1 +1 @@ -__version__ = "4.24.0" +__version__ = "4.24.1" diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index d000ebaff..b661d337e 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -5,7 +5,7 @@ import os from astrbot.core.computer.booters.cua_defaults import CUA_DEFAULT_CONFIG from astrbot.core.utils.astrbot_path import get_astrbot_data_path -VERSION = "4.24.0" +VERSION = "4.24.1" DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db") PERSONAL_WECHAT_CONFIG_METADATA = { "weixin_oc_base_url": { diff --git a/changelogs/v4.24.0.md b/changelogs/v4.24.1.md similarity index 100% rename from changelogs/v4.24.0.md rename to changelogs/v4.24.1.md diff --git a/dashboard/src/main.ts b/dashboard/src/main.ts index e1c736aa2..61cf487ab 100644 --- a/dashboard/src/main.ts +++ b/dashboard/src/main.ts @@ -10,12 +10,25 @@ import VueApexCharts from 'vue3-apexcharts'; import print from 'vue3-print-nb'; import { loader } from '@guolao/vue-monaco-editor' -import * as monaco from 'monaco-editor'; +import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution'; +import 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution'; +import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'; +import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'; +import 'monaco-editor/esm/vs/basic-languages/powershell/powershell.contribution'; +import 'monaco-editor/esm/vs/basic-languages/python/python.contribution'; +import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'; +import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'; +import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'; +import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution'; +import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'; +import 'monaco-editor/esm/vs/language/css/monaco.contribution'; +import 'monaco-editor/esm/vs/language/html/monaco.contribution'; +import 'monaco-editor/esm/vs/language/json/monaco.contribution'; import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; -import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'; import axios from 'axios'; import { waitForRouterReadyInBackground } from './utils/routerReadiness.mjs'; @@ -30,9 +43,6 @@ import { waitForRouterReadyInBackground } from './utils/routerReadiness.mjs'; if (label === 'html' || label === 'handlebars' || label === 'razor') { return new htmlWorker(); } - if (label === 'typescript' || label === 'javascript') { - return new tsWorker(); - } return new editorWorker(); }, }; diff --git a/dashboard/src/utils/shiki.js b/dashboard/src/utils/shiki.js index cb9eac828..c15516908 100644 --- a/dashboard/src/utils/shiki.js +++ b/dashboard/src/utils/shiki.js @@ -1,4 +1,7 @@ -import { getSingletonHighlighter } from "shiki"; +import { + createHighlighter, + normalizeLimitedShikiLanguage, +} from "./shikiLimitedBundle"; export const SHIKI_THEMES = { light: "github-light", @@ -8,8 +11,7 @@ export const SHIKI_THEMES = { let highlighterPromise; function normalizeLanguage(language) { - const normalized = (language || "text").trim().split(/\s+/, 1)[0].toLowerCase(); - return normalized || "text"; + return normalizeLimitedShikiLanguage(language); } export function escapeHtml(value = "") { @@ -23,28 +25,16 @@ export function escapeHtml(value = "") { export async function getShikiHighlighter() { if (!highlighterPromise) { - highlighterPromise = getSingletonHighlighter({ + highlighterPromise = createHighlighter({ themes: Object.values(SHIKI_THEMES), - langs: ["text"], }); } return highlighterPromise; } -export async function ensureShikiLanguages(languages = []) { +export async function ensureShikiLanguages() { const highlighter = await getShikiHighlighter(); - const languagesToLoad = [...new Set(languages.map(normalizeLanguage))].filter( - (language) => language !== "text", - ); - - await Promise.all( - languagesToLoad.map((language) => - highlighter.loadLanguage(language).catch((err) => { - console.warn(`Failed to load Shiki language "${language}".`, err); - }), - ), - ); return highlighter; } diff --git a/dashboard/src/utils/shikiLimitedBundle.js b/dashboard/src/utils/shikiLimitedBundle.js new file mode 100644 index 000000000..b0ece5387 --- /dev/null +++ b/dashboard/src/utils/shikiLimitedBundle.js @@ -0,0 +1,196 @@ +import { createHighlighterCore } from "shiki/core"; +import { createJavaScriptRegexEngine } from "shiki/engine/javascript"; +import bash from "shiki/langs/bash.mjs"; +import css from "shiki/langs/css.mjs"; +import diff from "shiki/langs/diff.mjs"; +import dockerfile from "shiki/langs/dockerfile.mjs"; +import html from "shiki/langs/html.mjs"; +import ini from "shiki/langs/ini.mjs"; +import java from "shiki/langs/java.mjs"; +import javascript from "shiki/langs/javascript.mjs"; +import json from "shiki/langs/json.mjs"; +import jsx from "shiki/langs/jsx.mjs"; +import markdown from "shiki/langs/markdown.mjs"; +import powershell from "shiki/langs/powershell.mjs"; +import python from "shiki/langs/python.mjs"; +import sql from "shiki/langs/sql.mjs"; +import tsx from "shiki/langs/tsx.mjs"; +import typescript from "shiki/langs/typescript.mjs"; +import vue from "shiki/langs/vue.mjs"; +import xml from "shiki/langs/xml.mjs"; +import yaml from "shiki/langs/yaml.mjs"; +import githubDark from "shiki/themes/github-dark.mjs"; +import githubLight from "shiki/themes/github-light.mjs"; +import vitesseDark from "shiki/themes/vitesse-dark.mjs"; +import vitesseLight from "shiki/themes/vitesse-light.mjs"; + +export const LIMITED_SHIKI_LANGUAGES = [ + ...bash, + ...css, + ...diff, + ...dockerfile, + ...html, + ...ini, + ...java, + ...javascript, + ...json, + ...jsx, + ...markdown, + ...powershell, + ...python, + ...sql, + ...tsx, + ...typescript, + ...vue, + ...xml, + ...yaml, +]; + +const THEME_BY_NAME = { + "github-dark": githubDark, + "github-light": githubLight, + "vitesse-dark": vitesseDark, + "vitesse-light": vitesseLight, +}; + +const BUILT_IN_LANGUAGES = ["text", "plaintext", "plain"]; + +export const LIMITED_SHIKI_LANGUAGE_ALIASES = { + bat: "powershell", + cjs: "javascript", + console: "bash", + cts: "typescript", + docker: "dockerfile", + htm: "html", + js: "javascript", + md: "markdown", + mjs: "javascript", + mts: "typescript", + plain: "text", + plaintext: "text", + ps1: "powershell", + pwsh: "powershell", + py: "python", + shell: "bash", + shellscript: "bash", + sh: "bash", + svg: "xml", + text: "text", + ts: "typescript", + txt: "text", + xhtml: "html", + yml: "yaml", + zsh: "bash", +}; + +export const LIMITED_SHIKI_SUPPORTED_LANGUAGES = new Set([ + ...BUILT_IN_LANGUAGES, + ...LIMITED_SHIKI_LANGUAGES.flatMap((language) => [ + language.name, + ...(language.aliases || []), + ]), +]); + +function getThemeName(theme) { + return typeof theme === "string" ? theme : theme?.name; +} + +function resolveTheme(theme) { + if (!theme) return null; + if (typeof theme !== "string") return theme; + return THEME_BY_NAME[theme] || null; +} + +function uniqueThemes(themes) { + const seen = new Set(); + const result = []; + + for (const theme of themes) { + const resolved = resolveTheme(theme); + const name = getThemeName(resolved); + if (!resolved || !name || seen.has(name)) continue; + seen.add(name); + result.push(resolved); + } + + return result; +} + +export function normalizeLimitedShikiLanguage(language) { + const normalized = String(language || "text") + .trim() + .split(/\s+/, 1)[0] + .toLowerCase(); + + if (!normalized) return "text"; + if (normalized in LIMITED_SHIKI_LANGUAGE_ALIASES) { + return LIMITED_SHIKI_LANGUAGE_ALIASES[normalized]; + } + + return LIMITED_SHIKI_SUPPORTED_LANGUAGES.has(normalized) ? normalized : "text"; +} + +function normalizeCodeOptions(options) { + if (!options || typeof options !== "object") return options; + return { + ...options, + lang: normalizeLimitedShikiLanguage(options.lang), + }; +} + +function wrapLimitedHighlighter(highlighter) { + const codeToHtml = highlighter.codeToHtml.bind(highlighter); + const codeToTokens = highlighter.codeToTokens.bind(highlighter); + const codeToHast = highlighter.codeToHast.bind(highlighter); + const getLanguage = highlighter.getLanguage.bind(highlighter); + const getLoadedLanguages = highlighter.getLoadedLanguages.bind(highlighter); + const loadThemeSync = highlighter.loadThemeSync?.bind(highlighter); + const loadTheme = highlighter.loadTheme?.bind(highlighter); + + return { + ...highlighter, + codeToHast(code, options) { + return codeToHast(code, normalizeCodeOptions(options)); + }, + codeToHtml(code, options) { + return codeToHtml(code, normalizeCodeOptions(options)); + }, + codeToTokens(code, options) { + return codeToTokens(code, normalizeCodeOptions(options)); + }, + getLanguage(language) { + return getLanguage(normalizeLimitedShikiLanguage(language)); + }, + getLoadedLanguages() { + return [...new Set([...getLoadedLanguages(), ...BUILT_IN_LANGUAGES])]; + }, + loadLanguage() { + return Promise.resolve(); + }, + loadLanguageSync() {}, + async loadTheme(...themes) { + const resolved = uniqueThemes(themes.flat()); + if (resolved.length && loadTheme) await loadTheme(...resolved); + }, + loadThemeSync(...themes) { + const resolved = uniqueThemes(themes.flat()); + if (resolved.length && loadThemeSync) loadThemeSync(...resolved); + }, + }; +} + +export async function createHighlighter(options = {}) { + const themes = uniqueThemes([ + ...(Array.isArray(options.themes) ? options.themes : []), + ...Object.values(THEME_BY_NAME), + ]); + + const highlighter = await createHighlighterCore({ + ...options, + engine: options.engine || createJavaScriptRegexEngine(), + langs: LIMITED_SHIKI_LANGUAGES, + themes, + }); + + return wrapLimitedHighlighter(highlighter); +} diff --git a/dashboard/src/utils/streamMonacoDisabled.js b/dashboard/src/utils/streamMonacoDisabled.js new file mode 100644 index 000000000..23430cbfe --- /dev/null +++ b/dashboard/src/utils/streamMonacoDisabled.js @@ -0,0 +1 @@ +throw new Error("stream-monaco is disabled in the dashboard bundle."); diff --git a/dashboard/vite.config.ts b/dashboard/vite.config.ts index 05eaff1d3..fef80e436 100644 --- a/dashboard/vite.config.ts +++ b/dashboard/vite.config.ts @@ -74,10 +74,24 @@ export default defineConfig(({ command }) => ({ webfontDl() ], resolve: { - alias: { - mermaid: 'mermaid/dist/mermaid.js', - '@': fileURLToPath(new URL('./src', import.meta.url)) - } + alias: [ + { + find: /^shiki$/, + replacement: fileURLToPath(new URL('./src/utils/shikiLimitedBundle.js', import.meta.url)) + }, + { + find: /^stream-monaco$/, + replacement: fileURLToPath(new URL('./src/utils/streamMonacoDisabled.js', import.meta.url)) + }, + { + find: 'mermaid', + replacement: 'mermaid/dist/mermaid.js' + }, + { + find: '@', + replacement: fileURLToPath(new URL('./src', import.meta.url)) + } + ] }, css: { preprocessorOptions: { diff --git a/pyproject.toml b/pyproject.toml index 05c6622d2..3aca67a16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "AstrBot" -version = "4.24.0" +version = "4.24.1" description = "Easy-to-use multi-platform LLM chatbot and development framework" readme = "README.md" license = { text = "AGPL-3.0-or-later" }