From 461f1bb07c8e1f74723325b76c4edbd842051172 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Thu, 6 Feb 2025 12:35:43 +0800 Subject: [PATCH 001/114] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=8F=92?= =?UTF-8?q?=E4=BB=B6handler=E4=BC=98=E5=85=88=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/star/register/star_handler.py | 54 ++++++++++++------- astrbot/core/star/star_handler.py | 62 ++++++++++++++++------ astrbot/core/star/star_manager.py | 1 - tests/test_plugin_manager.py | 1 - 4 files changed, 82 insertions(+), 36 deletions(-) diff --git a/astrbot/core/star/register/star_handler.py b/astrbot/core/star/register/star_handler.py index 6b8c6b878..5d7489d35 100644 --- a/astrbot/core/star/register/star_handler.py +++ b/astrbot/core/star/register/star_handler.py @@ -17,7 +17,12 @@ def get_handler_full_name(awaitable: Awaitable) -> str: '''获取 Handler 的全名''' return f"{awaitable.__module__}_{awaitable.__name__}" -def get_handler_or_create(handler: Awaitable, event_type: EventType, dont_add = False, **kwargs) -> StarHandlerMetadata: +def get_handler_or_create( + handler: Awaitable, + event_type: EventType, + dont_add = False, + **kwargs +) -> StarHandlerMetadata: '''获取 Handler 或者创建一个新的 Handler''' handler_full_name = get_handler_full_name(handler) md = star_handlers_registry.get_handler_by_full_name(handler_full_name) @@ -30,18 +35,27 @@ def get_handler_or_create(handler: Awaitable, event_type: EventType, dont_add = handler_name=handler.__name__, handler_module_path=handler.__module__, handler=handler, - event_filters=[], + event_filters=[] ) + + # 插件handler的附加额外信息 + if 'desc' in kwargs: + md.desc = kwargs['desc'] + del kwargs['desc'] + md.extras_configs = kwargs + if handler.__doc__: md.desc = handler.__doc__.strip() if not dont_add: star_handlers_registry.append(md) return md -def register_command(command_name: str = None, *args): +def register_command(command_name: str = None, *args, **kwargs): '''注册一个 Command. ''' + # print("command: ", command_name, args, kwargs) + new_command = None add_to_event_filters = False if isinstance(command_name, RegisteringCommandable): @@ -54,7 +68,7 @@ def register_command(command_name: str = None, *args): add_to_event_filters = True def decorator(awaitable): - handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent) + handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent, **kwargs) new_command.init_handler_md(handler_md) if add_to_event_filters: # 裸指令 @@ -64,10 +78,12 @@ def register_command(command_name: str = None, *args): return decorator -def register_command_group(command_group_name: str = None, *args): +def register_command_group(command_group_name: str = None, *args, **kwargs): '''注册一个 CommandGroup ''' + # print("commandgroup: ", command_group_name,args, kwargs) + new_group = None add_to_event_filters = False if isinstance(command_group_name, RegisteringCommandable): @@ -82,7 +98,7 @@ def register_command_group(command_group_name: str = None, *args): def decorator(obj): if add_to_event_filters: # 根指令组 - handler_md = get_handler_or_create(obj, EventType.AdapterMessageEvent) + handler_md = get_handler_or_create(obj, EventType.AdapterMessageEvent, **kwargs) handler_md.event_filters.append(new_group) return RegisteringCommandable(new_group) @@ -97,16 +113,16 @@ class RegisteringCommandable(): def __init__(self, parent_group: CommandGroupFilter): self.parent_group = parent_group -def register_event_message_type(event_message_type: EventMessageType): +def register_event_message_type(event_message_type: EventMessageType, **kwargs): '''注册一个 EventMessageType''' def decorator(awaitable): - handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent) + handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent, kwargs) handler_md.event_filters.append(EventMessageTypeFilter(event_message_type)) return awaitable return decorator -def register_platform_adapter_type(platform_adapter_type: PlatformAdapterType): +def register_platform_adapter_type(platform_adapter_type: PlatformAdapterType, **kwargs): '''注册一个 PlatformAdapterType''' def decorator(awaitable): handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent) @@ -115,10 +131,10 @@ def register_platform_adapter_type(platform_adapter_type: PlatformAdapterType): return decorator -def register_regex(regex: str): +def register_regex(regex: str, **kwargs): '''注册一个 Regex''' def decorator(awaitable): - handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent) + handler_md = get_handler_or_create(awaitable, EventType.AdapterMessageEvent, **kwargs) handler_md.event_filters.append(RegexFilter(regex)) return awaitable @@ -138,7 +154,7 @@ def register_permission_type(permission_type: PermissionType, raise_error: bool return decorator -def register_on_llm_request(): +def register_on_llm_request(**kwargs): '''当有 LLM 请求时的事件 Examples: @@ -153,12 +169,12 @@ def register_on_llm_request(): 请务必接收两个参数:event, request ''' def decorator(awaitable): - _ = get_handler_or_create(awaitable, EventType.OnLLMRequestEvent) + _ = get_handler_or_create(awaitable, EventType.OnLLMRequestEvent, **kwargs) return awaitable return decorator -def register_on_llm_response(): +def register_on_llm_response(**kwargs): '''当有 LLM 请求后的事件 Examples: @@ -173,7 +189,7 @@ def register_on_llm_response(): 请务必接收两个参数:event, request ''' def decorator(awaitable): - _ = get_handler_or_create(awaitable, EventType.OnLLMResponseEvent) + _ = get_handler_or_create(awaitable, EventType.OnLLMResponseEvent, **kwargs) return awaitable return decorator @@ -219,18 +235,18 @@ def register_llm_tool(name: str = None): return decorator -def register_on_decorating_result(): +def register_on_decorating_result(**kwargs): '''在发送消息前的事件''' def decorator(awaitable): - _ = get_handler_or_create(awaitable, EventType.OnDecoratingResultEvent) + _ = get_handler_or_create(awaitable, EventType.OnDecoratingResultEvent, **kwargs) return awaitable return decorator -def register_after_message_sent(): +def register_after_message_sent(**kwargs): '''在消息发送后的事件''' def decorator(awaitable): - _ = get_handler_or_create(awaitable, EventType.OnAfterMessageSentEvent) + _ = get_handler_or_create(awaitable, EventType.OnAfterMessageSentEvent, **kwargs) return awaitable return decorator \ No newline at end of file diff --git a/astrbot/core/star/star_handler.py b/astrbot/core/star/star_handler.py index 378038327..389935a37 100644 --- a/astrbot/core/star/star_handler.py +++ b/astrbot/core/star/star_handler.py @@ -1,34 +1,41 @@ from __future__ import annotations import enum -from dataclasses import dataclass +import heapq +from dataclasses import dataclass, field from typing import Awaitable, List, Dict, TypeVar, Generic from .filter import HandlerFilter from .star import star_map T = TypeVar('T', bound='StarHandlerMetadata') -class StarHandlerRegistry(Generic[T], List[T]): +class StarHandlerRegistry(Generic[T]): '''用于存储所有的 Star Handler''' star_handlers_map: Dict[str, StarHandlerMetadata] = {} '''用于快速查找。key 是 handler_full_name''' + _handlers = [] def append(self, handler: StarHandlerMetadata): '''添加一个 Handler''' - super().append(handler) + if 'priority' not in handler.extras_configs: + handler.extras_configs['priority'] = 0 + + heapq.heappush(self._handlers, (-handler.extras_configs['priority'], handler)) self.star_handlers_map[handler.handler_full_name] = handler - def get_handlers_by_event_type(self, event_type: EventType, only_activated = True) -> List[StarHandlerMetadata]: + def _print_handlers(self): + '''打印所有的 Handler''' + for _, handler in self._handlers: + print(handler.handler_full_name) + + def get_handlers_by_event_type(self, event_type: EventType, only_activated=True) -> List[StarHandlerMetadata]: '''通过事件类型获取 Handler''' - if only_activated: - return [ - handler - for handler in self - if handler.event_type == event_type and - star_map[handler.handler_module_path] and - star_map[handler.handler_module_path].activated - ] - else: - return [handler for handler in self if handler.event_type == event_type] + handlers = [ + handler + for _, handler in self._handlers + if handler.event_type == event_type and + (not only_activated or (star_map[handler.handler_module_path] and star_map[handler.handler_module_path].activated)) + ] + return handlers def get_handler_by_full_name(self, full_name: str) -> StarHandlerMetadata: '''通过 Handler 的全名获取 Handler''' @@ -36,7 +43,25 @@ class StarHandlerRegistry(Generic[T], List[T]): def get_handlers_by_module_name(self, module_name: str) -> List[StarHandlerMetadata]: '''通过模块名获取 Handler''' - return [handler for handler in self if handler.handler_module_path == module_name] + return [handler for _, handler in self._handlers if handler.handler_module_path == module_name] + + def clear(self): + '''清空所有的 Handler''' + self.star_handlers_map.clear() + self._handlers.clear() + + def remove(self, handler: StarHandlerMetadata): + '''删除一个 Handler''' + self._handlers.remove(handler) + del self.star_handlers_map[handler.handler_full_name] + + def __iter__(self): + '''使 StarHandlerRegistry 支持迭代''' + return (handler for _, handler in self._handlers) + + def __len__(self): + '''返回 Handler 的数量''' + return len(self._handlers) star_handlers_registry = StarHandlerRegistry() @@ -76,3 +101,10 @@ class StarHandlerMetadata(): desc: str = "" '''Handler 的描述信息''' + + extras_configs: dict = field(default_factory=dict) + '''插件注册的一些其他的信息, 如 priority 等''' + + def __lt__(self, other: StarHandlerMetadata): + '''定义小于运算符以支持优先队列''' + return self.extras_configs.get('priority', 0) < other.extras_configs.get('priority', 0) \ No newline at end of file diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index a7a694fce..e06908605 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -146,7 +146,6 @@ class PluginManager: smd.star_cls.__del__() star_handlers_registry.clear() - star_handlers_registry.star_handlers_map.clear() star_map.clear() star_registry.clear() for key in list(sys.modules.keys()): diff --git a/tests/test_plugin_manager.py b/tests/test_plugin_manager.py index 1d336a57a..86a99c6c1 100644 --- a/tests/test_plugin_manager.py +++ b/tests/test_plugin_manager.py @@ -30,7 +30,6 @@ async def test_plugin_manager_reload(plugin_manager_pm: PluginManager): success, err_message = await plugin_manager_pm.reload() assert success is True assert err_message is None - assert len(star_handlers_registry) > 0 # package @pytest.mark.asyncio async def test_plugin_crud(plugin_manager_pm: PluginManager): From 3800e96d143af79ef8e4e67dc81d5db84ba7cb8e Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Thu, 6 Feb 2025 15:10:24 +0800 Subject: [PATCH 002/114] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dmetadata?= =?UTF-8?q?=E4=B8=8D=E7=94=9F=E6=95=88=E7=9A=84=E9=97=AE=E9=A2=98=20feat:?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81=E6=9F=A5=E7=9C=8B=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/provider/sources/gemini_source.py | 2 +- .../core/provider/sources/openai_source.py | 4 +- astrbot/core/star/filter/regex.py | 1 + astrbot/core/star/register/star_handler.py | 4 +- astrbot/core/star/star.py | 3 + astrbot/core/star/star_handler.py | 6 +- astrbot/core/star/star_manager.py | 29 +++++--- astrbot/dashboard/routes/plugin.py | 65 ++++++++++++++++- dashboard/package.json | 2 +- .../full/vertical-sidebar/VerticalSidebar.vue | 2 +- dashboard/src/views/ExtensionPage.vue | 72 ++++++++++++++++--- 11 files changed, 163 insertions(+), 27 deletions(-) diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index 63cf7f9a2..b91facf74 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -196,7 +196,7 @@ class ProviderGoogleGenAI(Provider): if retry_cnt == 0: llm_response = LLMResponse("err", "err: 请尝试 /reset 重置会话") elif "Function calling is not enabled" in str(e): - logger.info(f"{self.get_model()} 不支持函数调用工具调用,已经自动去除") + logger.info(f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。") if 'tools' in payloads: del payloads['tools'] llm_response = await self._query(payloads, None) diff --git a/astrbot/core/provider/sources/openai_source.py b/astrbot/core/provider/sources/openai_source.py index 4b4b678ec..c4c7c809b 100644 --- a/astrbot/core/provider/sources/openai_source.py +++ b/astrbot/core/provider/sources/openai_source.py @@ -165,7 +165,7 @@ class ProviderOpenAIOfficial(Provider): or 'Function call is not supported' in str(e) \ or 'Function calling is not enabled' in str(e) \ or 'Tool calling is not supported' in str(e): # siliconcloud - logger.info(f"{self.get_model()} 不支持函数调用工具调用,已经自动去除") + logger.info(f"{self.get_model()} 不支持函数工具调用,已自动去除,不影响使用。") if 'tools' in payloads: del payloads['tools'] llm_response = await self._query(payloads, None) @@ -173,7 +173,7 @@ class ProviderOpenAIOfficial(Provider): logger.error(f"发生了错误。Provider 配置如下: {self.provider_config}") if 'tool' in str(e).lower() and 'support' in str(e).lower(): - logger.error(f"疑似该模型不支持函数调用工具调用。请输入 /tool off_all") + logger.error("疑似该模型不支持函数调用工具调用。请输入 /tool off_all") if 'Connection error.' in str(e): proxy = os.environ.get("http_proxy", None) diff --git a/astrbot/core/star/filter/regex.py b/astrbot/core/star/filter/regex.py index c5f919ee3..816b14109 100644 --- a/astrbot/core/star/filter/regex.py +++ b/astrbot/core/star/filter/regex.py @@ -8,6 +8,7 @@ from astrbot.core.config import AstrBotConfig class RegexFilter(HandlerFilter): '''正则表达式过滤器''' def __init__(self, regex: str): + self.regex_str = regex self.regex = re.compile(regex) def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool: diff --git a/astrbot/core/star/register/star_handler.py b/astrbot/core/star/register/star_handler.py index 5d7489d35..2a5e40a63 100644 --- a/astrbot/core/star/register/star_handler.py +++ b/astrbot/core/star/register/star_handler.py @@ -39,13 +39,13 @@ def get_handler_or_create( ) # 插件handler的附加额外信息 + if handler.__doc__: + md.desc = handler.__doc__.strip() if 'desc' in kwargs: md.desc = kwargs['desc'] del kwargs['desc'] md.extras_configs = kwargs - if handler.__doc__: - md.desc = handler.__doc__.strip() if not dont_add: star_handlers_registry.append(md) return md diff --git a/astrbot/core/star/star.py b/astrbot/core/star/star.py index c2abb6e34..862d8d036 100644 --- a/astrbot/core/star/star.py +++ b/astrbot/core/star/star.py @@ -39,6 +39,9 @@ class StarMetadata: config: AstrBotConfig = None '''插件配置''' + + star_handler_full_names: List[str] = None + '''注册的 Handler 的全名列表''' def __str__(self) -> str: return f"StarMetadata({self.name}, {self.desc}, {self.version}, {self.repo})" \ No newline at end of file diff --git a/astrbot/core/star/star_handler.py b/astrbot/core/star/star_handler.py index 389935a37..2d96ac8c3 100644 --- a/astrbot/core/star/star_handler.py +++ b/astrbot/core/star/star_handler.py @@ -52,7 +52,11 @@ class StarHandlerRegistry(Generic[T]): def remove(self, handler: StarHandlerMetadata): '''删除一个 Handler''' - self._handlers.remove(handler) + # self._handlers.remove(handler) + for i, h in enumerate(self._handlers): + if h[1] == handler: + self._handlers.pop(i) + break del self.star_handlers_map[handler.handler_full_name] def __iter__(self): diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index e06908605..72e74f366 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -9,7 +9,6 @@ import logging from types import ModuleType from typing import List from astrbot.core.config.astrbot_config import AstrBotConfig -from astrbot.core.config.default import DEFAULT_VALUE_MAP from astrbot.core import logger, sp, pip_installer from .context import Context from . import StarMetadata @@ -127,7 +126,7 @@ class PluginManager: if isinstance(metadata, dict): if 'name' not in metadata or 'desc' not in metadata or 'version' not in metadata or 'author' not in metadata: - raise Exception("插件元数据信息不完整。") + raise Exception("插件元数据信息不完整。name, desc, version, author 是必须的字段。") metadata = StarMetadata( name=metadata['name'], author=metadata['author'], @@ -203,6 +202,18 @@ class PluginManager: # 通过装饰器的方式注册插件 metadata = star_map[path] + try: + # yaml 文件的元数据优先 + metadata_yaml = self._load_plugin_metadata(plugin_path=plugin_dir_path) + if metadata_yaml: + metadata.name = metadata_yaml.name + metadata.author = metadata_yaml.author + metadata.desc = metadata_yaml.desc + metadata.version = metadata_yaml.version + metadata.repo = metadata_yaml.repo + except Exception: + pass + if plugin_config: metadata.config = plugin_config try: @@ -219,7 +230,6 @@ class PluginManager: # 绑定 handler related_handlers = star_handlers_registry.get_handlers_by_module_name(metadata.module_path) for handler in related_handlers: - logger.debug(f"bind handler {handler.handler_name} to {metadata.name}") handler.handler = functools.partial(handler.handler, metadata.star_cls) # 绑定 llm_tool handler for func_tool in llm_tools.func_list: @@ -243,8 +253,7 @@ class PluginManager: obj = getattr(module, classes[0])(context=self.context) # 实例化插件类 metadata = None - plugin_path = os.path.join(self.plugin_store_path, root_dir_name) if not reserved else os.path.join(self.reserved_plugin_path, root_dir_name) - metadata = self._load_plugin_metadata(plugin_path=plugin_path, plugin_obj=obj) + metadata = self._load_plugin_metadata(plugin_path=plugin_dir_path, plugin_obj=obj) metadata.star_cls = obj metadata.config = plugin_config metadata.module = module @@ -260,10 +269,12 @@ class PluginManager: if metadata.module_path in inactivated_plugins: metadata.activated = False - # 检查并且植入自定义的权限过滤器(alter_cmd) + full_names = [] for handler in star_handlers_registry.get_handlers_by_module_name(metadata.module_path): + full_names.append(handler.handler_full_name) + + # 检查并且植入自定义的权限过滤器(alter_cmd) if metadata.name in alter_cmd and handler.handler_name in alter_cmd[metadata.name]: - # 注入权限过滤器 cmd_type = alter_cmd[metadata.name][handler.handler_name].get("permission", "member") found_permission_filter = False for filter_ in handler.event_filters: @@ -279,6 +290,8 @@ class PluginManager: logger.debug(f"插入权限过滤器 {cmd_type} 到 {metadata.name} 的 {handler.handler_name} 方法。") + metadata.star_handler_full_names = full_names + # 执行 initialize() 方法 if hasattr(metadata.star_cls, "initialize"): await metadata.star_cls.initialize() @@ -290,7 +303,7 @@ class PluginManager: # 清除 pip.main 导致的多余的 logging handlers for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) - + if not fail_rec: return True, None else: diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index 56600224f..63cb6ce16 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -6,6 +6,12 @@ from astrbot.core import logger from quart import request from astrbot.core.star.star_manager import PluginManager from astrbot.core.core_lifecycle import AstrBotCoreLifecycle +from astrbot.core.star.star_handler import star_handlers_registry +from astrbot.core.star.filter.command import CommandFilter +from astrbot.core.star.filter.command_group import CommandGroupFilter +from astrbot.core.star.filter.permission import PermissionTypeFilter +from astrbot.core.star.filter.regex import RegexFilter +from astrbot.core.star.star_handler import EventType class PluginRoute(Route): def __init__(self, context: RouteContext, core_lifecycle: AstrBotCoreLifecycle, plugin_manager: PluginManager) -> None: @@ -23,6 +29,15 @@ class PluginRoute(Route): self.core_lifecycle = core_lifecycle self.plugin_manager = plugin_manager self.register_routes() + + self.translated_event_type = { + EventType.AdapterMessageEvent: "平台消息下发时", + EventType.OnLLMRequestEvent: "LLM 请求时", + EventType.OnLLMResponseEvent: "LLM 响应后", + EventType.OnDecoratingResultEvent: "回复消息前", + EventType.OnCallingFuncToolEvent: "函数工具", + EventType.OnAfterMessageSentEvent: "发送消息后" + } async def get_online_plugins(self): custom = request.args.get("custom_registry") @@ -59,10 +74,58 @@ class PluginRoute(Route): "desc": plugin.desc, "version": plugin.version, "reserved": plugin.reserved, - "activated": plugin.activated + "activated": plugin.activated, + "handlers": await self.get_plugin_handlers_info(plugin.star_handler_full_names) } _plugin_resp.append(_t) return Response().ok(_plugin_resp).__dict__ + + async def get_plugin_handlers_info(self, handler_full_names: list[str]): + '''解析插件行为''' + handlers = [] + + for handler_full_name in handler_full_names: + info = {} + handler = star_handlers_registry.star_handlers_map.get(handler_full_name, None) + if handler is None: + continue + info["event_type"] = handler.event_type.name + info["event_type_h"] = self.translated_event_type.get(handler.event_type, handler.event_type.name) + info["handler_full_name"] = handler.handler_full_name + info["desc"] = handler.desc + info["handler_name"] = handler.handler_name + + if handler.event_type == EventType.AdapterMessageEvent: + # 处理平台适配器消息事件 + has_admin = False + for filter in handler.event_filters: # 正常handler就只有 1~2 个 filter,因此这里时间复杂度不会太高 + if isinstance(filter, CommandFilter): + info["type"] = "指令" + info["cmd"] = filter.command_name + elif isinstance(filter, CommandGroupFilter): + info["type"] = "指令组" + info["cmd"] = filter.group_name + info["sub_command"] = filter.print_cmd_tree(filter.sub_command_filters) + elif isinstance(filter, RegexFilter): + info["type"] = "正则匹配" + info["cmd"] = filter.regex_str + elif isinstance(filter, PermissionTypeFilter): + has_admin = True + info["has_admin"] = has_admin + if "cmd" not in info: + info["cmd"] = "未知" + if "type" not in info: + info["type"] = "事件监听器" + else: + info["cmd"] = "自动触发" + info["type"] = "无" + + if not info["desc"]: + info["desc"] = "无描述" + + handlers.append(info) + + return handlers async def install_plugin(self): post_data = await request.json diff --git a/dashboard/package.json b/dashboard/package.json index 9b59cfa92..5d76b72d5 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -32,7 +32,7 @@ "vue-router": "4.2.4", "vue3-apexcharts": "1.4.4", "vue3-print-nb": "0.1.4", - "vuetify": "3.3.14", + "vuetify": "3.7.11", "yup": "1.2.0" }, "devDependencies": { diff --git a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue index 58afcbea2..ae9697571 100644 --- a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue +++ b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue @@ -9,7 +9,7 @@ const sidebarMenu = shallowRef(sidebarItems);