diff --git a/plugins/tiga/.gitignore b/plugins/tiga/.gitignore new file mode 100644 index 00000000..afac2461 --- /dev/null +++ b/plugins/tiga/.gitignore @@ -0,0 +1,34 @@ +# Dependencies +node_modules/ + +# Build output +dist/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* + +# Environment +.env +.env.local +.env.*.local + +# Vite +*.local + +# TypeScript +*.tsbuildinfo + +# Auto-generated by unplugin-auto-import / unplugin-vue-components +# 注:auto-imports.d.ts 与 components.d.ts 需纳入版本控制, +# 以保证 CI 与协作者的类型检查(vue-tsc)正常运行 diff --git a/plugins/tiga/.mimicode/skills/note-workflow/SKILL.md b/plugins/tiga/.mimicode/skills/note-workflow/SKILL.md new file mode 100644 index 00000000..e5096ee5 --- /dev/null +++ b/plugins/tiga/.mimicode/skills/note-workflow/SKILL.md @@ -0,0 +1,138 @@ +--- +name: note-workflow +description: 按规则优化、合并、发布笔记的统一工作流。适用于 Area/ 和 Resource/ 下的笔记处理,包含格式优化、多源合并、发布文章三种模式。 +--- + +# 笔记处理工作流 + +> 该技能封装了 my-note 项目中反复执行的笔记处理工作流:按规则优化、按规则合并、按规则创建发布笔记。 + +## 适用场景 + +用户说以下任一表述时触发此技能: + +- "按规则优化Area/XXX/第Y章笔记" +- "按规则合并Resource/JavaSE/Java第Y章..." +- "按规则创建发布笔记Output/GoF设计模式/..." +- "提炼笔记"、"提取发布笔记" +- 任何包含"按规则"且涉及笔记处理的请求 + +## 前置条件 + +1. 确认当前工作目录是笔记项目(存在 `.claude/rules/` 目录) +2. 识别目标路径,确定适用的规则文件 + +## 统一工作流 + +### Step 1: 识别模式 + +根据用户指令判断处理模式: + +| 模式 | 信号词 | 输入 | 输出 | +|------|--------|------|------| +| **优化** | "优化"、"格式优化" | 单个来源笔记 | 优化后的笔记(同路径覆盖或新建) | +| **合并** | "合并"、"整合" | 多个来源笔记 | 合并后的笔记(Resource/ 下) | +| **发布** | "创建发布"、"提炼"、"提取发布" | Area/ 下的笔记 | Output/ 下的发布文章 | + +### Step 2: 加载规则 + +根据目标路径查找适用的规则文件: + +``` +规则查找顺序: +1. .claude/rules/Area/{子路径}-rules.md — 领域专属规则 +2. .claude/rules/Output/{子路径}-rules.md — 发布规范(发布模式) +3. .claude/rules/common.md — 通用格式规则 +``` + +**已知规则文件映射**: +| 目标路径 | 规则文件 | +|----------|----------| +| `Area/JDBC/**` | `Area/jdbc-rules.md` | +| `Area/Java/JavaSE/**` | `Area/javase-rules.md` | +| `Area/Java/设计模式/**` | `Area/design-pattern-rules.md` | +| `Output/**`(设计模式) | `Output/design-pattern-article-rules.md` | +| 所有路径 | `common.md` | + +**必须完整阅读规则文件**,不能跳过或只读部分。 + +### Step 3: 读取来源笔记 + +**优化模式**: +- 读取目标笔记的完整内容 +- 如有多个来源目录(如 JDBC 的 `2024老杜` 和 `老版老杜`),按规则文件中的来源分配表读取 + +**合并模式**: +- 按规则文件中的"整合章节清单"找到所有来源 +- 逐个读取来源笔记的完整内容 +- 标记每个来源的独有内容 + +**发布模式**: +- 读取 Area/ 下的源笔记 +- 读取发布规范文件(如 `design-pattern-article-rules.md`) + +### Step 4: 按规则处理 + +**优化模式执行清单**: +- [ ] 代码块、引用块、列表前面无空行 +- [ ] 表格前面允许空行 +- [ ] 标题无序号 +- [ ] 代码块前有文字描述 +- [ ] ⚠️ 和 💡 标记正确使用 +- [ ] 面试问答使用引用块格式(问句在上,`> 答案` 在下) +- [ ] 内容准确真实,不编造 +- [ ] 添加"进阶内容"和"要点速记"章节 + +**合并模式执行清单**: +- [ ] 去重:多个来源都讲的内容,保留最清晰版本 +- [ ] 互补:每个来源独有的内容全部收录 +- [ ] 升级:补充面试问法、实战场景、踩坑经验 +- [ ] 检查规则文件中的"注释提取清单"(如有) +- [ ] 合并后自检:对照来源目录逐项确认无遗漏 +- [ ] 应用所有优化模式的格式规则 + +**发布模式执行清单**: +- [ ] 按发布规范的章节结构编写(前言→概念→实现→总结→相似模式区分→练习→扩展) +- [ ] 必须有 mermaid UML 图 +- [ ] 实现代码使用 GoF 原始类名 +- [ ] 业务场景代码有实际含义的命名 +- [ ] 不使用第二人称 +- [ ] 总结包含:本质、什么时候用、什么时候不用、简单记忆口诀 + +### Step 5: 写入输出 + +**优化模式**: +- 将优化后的笔记写入目标路径 +- 询问用户是否归档原笔记(除非用户明确说"不要归档") + +**合并模式**: +- 将合并后的笔记写入 `Resource/` 下对应路径 +- 将原笔记归档到 `Archive/` 下对应路径 + +**发布模式**: +- 将发布文章写入 `Output/` 下对应路径 +- 不添加笔记属性(frontmatter) + +### Step 6: 自检 + +处理完成后,执行最终自检: + +1. **格式自检**:抽查 3-5 处代码块/引用块/列表,确认前面无空行 +2. **内容自检**:确认无编造内容,不确定的标注提醒用户 +3. **完整性自检**(合并模式):对照来源目录逐项确认 +4. **输出确认**:告知用户处理完成,列出变更摘要 + +## 常见问题处理 + +### 笔记中有图片 +- 保留图片引用,使用相对路径 `assets/章节名/图片文件名` +- 如果图片无法解析,用文字描述图片内容 + +### 来源笔记质量差 +- 不要照搬质量差的内容 +- 按规则文件中的"取舍规则"决定保留/删除/补充 + +### 用户对结果不满意 +- 用户可能说"内容很浅,没有深度"→ 补充"为什么"、实战场景、踩坑经验 +- 用户可能说"不要精简"→ 保留代码示例和表格,只优化格式 +- 用户可能说"面试题格式不对"→ 确认使用引用块格式 diff --git a/plugins/tiga/AGENTS.md b/plugins/tiga/AGENTS.md new file mode 100644 index 00000000..a349ac94 --- /dev/null +++ b/plugins/tiga/AGENTS.md @@ -0,0 +1,49 @@ +# tiga + +ZTools 插件:定时提醒提肛运动。Vue 3 + Vite + TypeScript + Element Plus。 + +## 命令 + +- `npm run dev` — Vite 开发服务器,地址 `localhost:5173` +- `npm run build` — `vue-tsc && vite build`(先类型检查再打包) + +无 lint、format 或 test 命令。 + +## 两个执行上下文 + +这是最重要的架构事实: + +1. **Vue 渲染层**(`src/`)— 标准 Vue 3 应用,挂载在 `index.html`,运行在浏览器/WebView 中。 +2. **Node.js preload 层**(`public/preload/services.js`)— CommonJS(`require()`),运行在 ZTools 主进程中。负责定时器、通知和存储。`public/preload/package.json` 设置了 `"type": "commonjs"`。 + +两者通过 `window.services`(preload 暴露)和 `window.ztools`(ZTools 宿主 API)通信。 + +## 关键文件 + +- `public/plugin.json` — ZTools 插件清单,定义 features、commands、preload 路径和开发 URL。 +- `public/preload/services.js` — 定时器管理、工作时段判断、桌面通知、存储(生产用 `ztools.dbStorage`,开发用文件存储)。 +- `public/exercise-guide.html` — 独立的原生 JS 弹窗(**不是** Vue 组件),在 `notify+popup` 模式下通过 `BrowserWindow` 加载。 +- `src/types.ts` — 所有 TypeScript 接口、默认配置、存储键名、工具函数。 +- `src/App.vue` — 标签页路由(统计/设置/教程),包含开发模式下 `window.services` 和 `window.ztools` 的 mock 降级。 +- `src/env.d.ts` — `window.services` 和 `Services` 接口的全局类型声明。 + +## 开发模式 + +`npm run dev` 时无 ZTools API 可用。`App.vue` 中的 mock: +- `window.services` — 用 `localStorage` 降级 +- `window.ztools` — 用 console.log 占位 + +preload 服务(`services.js`)在开发模式下**不会加载**。运动弹窗降级为 Vue `ExerciseGuide` 组件(模态覆盖层),而非真实 `BrowserWindow`。 + +## 构建 + +- `vite.config.js` 设置 `base: './'` 以使用相对路径(ZTools 插件加载必需)。 +- `tsconfig.json` 中 `"strict": false`,`"noImplicitAny": false`。 +- TypeScript 类型来自 `@ztools-center/ztools-api-types`。 + +## 存储 + +两个存储键:`tiga_config` 和 `tiga_stats`。 +- 生产环境:`window.ztools.dbStorage.getItem/setItem` +- 开发降级(preload):基于文件的 JSON,存储在 `userData` 路径 +- 开发降级(渲染层):`localStorage` diff --git a/plugins/tiga/CHANGELOG.md b/plugins/tiga/CHANGELOG.md new file mode 100644 index 00000000..0c68510f --- /dev/null +++ b/plugins/tiga/CHANGELOG.md @@ -0,0 +1,52 @@ +# 更新日志 + +## 1.1.0 (2026-06-16) + +### 新增 + +- 统计页新增"总打卡天数"和"总完成次数"展示 +- 设置页新增"恢复默认设置"按钮,一键重置为推荐配置 +- 教程文档全面重写,聚焦久坐办公人群,内容更通俗易读 +- 教程渲染器支持四级标题(####)和引用块(>) +- Element Plus 按需自动引入,减少打包体积 + +### 优化 + +- 默认运动参数调整为教程推荐值:收缩 5 秒、放松 10 秒、重复 2 次 +- 连续打卡算法修复:中间缺失一天即中断,真正计算连续天数 +- 连续打卡算法性能优化:O(n) 查找改为 O(1) Map 查找 +- 通知 API 优先使用 ZTools 平台 `showNotification`,仅在需要点击回调时降级到 Web Notification +- 设置页 watcher 缩窄为只监听 `workTimeMode` 字段,减少不必要的重计算 +- 统一计数逻辑为 `services.addRecord()` 单一入口,消除三处重复代码 +- 移除所有非 ZTools 环境的降级代码,简化整体架构 +- App.vue 大幅精简,移除开发模式 mock 和冗余弹窗逻辑 + +### 修复 + +- 修复通知点击后出现两个弹窗的问题(生产环境只弹一个) +- 修复统计页 `el-switch` 绑定只读 computed 的潜在问题 +- 修复 `exercise-guide.html` CSS 属性重复覆盖 +- 修复 `exercise-guide.html` 运动完成后打卡记录静默丢失的问题 + +## 1.0.0 (2026-06-11) + +### 新增 + +- 定时提醒功能,可自定义提醒间隔(1-120分钟)及工作时段 +- 桌面通知提醒,支持多种文案风格(随机/正经/搞怪) +- 运动引导弹窗,展示收缩/放松倒计时,引导用户完成运动 +- 统计打卡功能,记录每日完成次数和连续打卡天数 +- 每次完成的时间点记录,可在统计页查看详细时间 +- 教程指南,基于医学文献的专业提肛运动指导 +- 三种工作时段模式:固定时段、多时段、周几+时段 +- 两种提醒方式:仅通知、通知+弹窗 +- 两种计数方式:自动计数、手动计数 +- 自定义通知文案,支持添加正经和搞怪文案 +- 深色模式适配 + +### 优化 + +- 运动弹窗倒计时动画改为 requestAnimationFrame,60fps 平滑过渡 +- 运动弹窗关闭按钮样式简化 +- 设置页面切换后配置正确保留 +- 修复弹窗重复打开的问题 diff --git a/plugins/tiga/README.md b/plugins/tiga/README.md new file mode 100644 index 00000000..c1ad93fc --- /dev/null +++ b/plugins/tiga/README.md @@ -0,0 +1,99 @@ +# 提肛助手(tiga) + +> 定时提醒提肛运动,关爱久坐族盆底健康 + +## 最近更新 + +**v1.1.0** (2026-06-16) + +- 统计页新增总打卡天数和总完成次数 +- 设置页新增"恢复默认设置"按钮 +- 教程全面重写,聚焦久坐办公人群 +- 默认运动参数调整为推荐值(收缩 5 秒、放松 10 秒、重复 2 次) +- 修复通知点击后弹窗重复的问题 + +[完整更新日志](CHANGELOG.md) + +--- + +## 功能介绍 + +### 定时提醒 + +每 30 分钟(可自定义)自动发送桌面通知,提醒你站起来活动。专为久坐办公人群设计。 + +### 运动引导 + +弹出运动引导窗口,按节奏完成收缩/放松盆底肌训练,全程倒计时引导,60fps 平滑动画。 + +### 统计打卡 + +记录每次完成情况,展示今日完成次数、连续打卡天数、总打卡天数和总完成次数。 + +### 教程指南 + +专为久坐族编写的通俗教程,覆盖正确姿势、呼吸配合、训练频率、注意事项等。 + +## 使用方法 + +在 ZTools 中输入以下命令: + +| 命令 | 功能 | +|------|------| +| `提肛` / `提肛助手` | 打开设置页面 | +| `提肛统计` | 查看完成记录 | +| `提肛教程` | 学习正确姿势 | + +## 设置说明 + +### 提醒间隔 + +两次提醒之间的间隔时间,范围 1-120 分钟,默认 **30 分钟**(每坐半小时提醒一次)。 + +### 工作时段 + +只在设定的时间段内提醒: + +- **固定时段**:单个时间范围,如 09:00 - 18:00 +- **多时段**:多个时间段,如 09:00-12:00, 14:00-18:00 +- **周几+时段**:按周一到周日分别配置 + +### 提醒方式 + +- **仅插件通知**:直接弹出运动引导窗口 +- **桌面通知+弹窗**:发送系统通知,点击后弹出运动引导窗口 + +### 运动参数 + +- **收缩时长**:默认 5 秒 +- **放松时长**:默认 10 秒(收缩的 2 倍,给肌肉充分休息) +- **重复次数**:默认 2 次(偏保守,适合碎片化提醒) + +> 默认参数采用"碎片化提醒"策略:每次提醒只做 2 下,安全不累。如果你想做完整训练,可以把重复次数调到 10~15。 + +### 文案风格 + +- **随机**:从全部文案中随机选择 +- **正经**:只使用正式风格的文案 +- **搞怪**:只使用趣味风格的文案 + +支持自定义添加文案。 + +### 恢复默认设置 + +设置页底部可一键恢复所有设置为推荐值。 + +## 推荐用法 + +**"每天专心练 3 组,每次起身做 2 下。"** + +- **专心练**:选早、中、晚固定时间,安静做一组(10~15 次) +- **起身做**:每次站起来接水、上厕所时,顺便收紧 2 下,不求做满 + +## 技术栈 + +Vue 3 + Vite + TypeScript + Element Plus + +## 开源协议 + +MIT License diff --git a/plugins/tiga/auto-imports.d.ts b/plugins/tiga/auto-imports.d.ts new file mode 100644 index 00000000..8c1957bd --- /dev/null +++ b/plugins/tiga/auto-imports.d.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +// biome-ignore lint: disable +export {} +declare global { + const ElMessage: typeof import('element-plus/es').ElMessage + const ElMessageBox: typeof import('element-plus/es').ElMessageBox +} diff --git a/plugins/tiga/components.d.ts b/plugins/tiga/components.d.ts new file mode 100644 index 00000000..36e67cbf --- /dev/null +++ b/plugins/tiga/components.d.ts @@ -0,0 +1,36 @@ +/* eslint-disable */ +// @ts-nocheck +// biome-ignore lint: disable +// oxlint-disable +// ------ +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 + +export {} + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + ElAlert: typeof import('element-plus/es')['ElAlert'] + ElButton: typeof import('element-plus/es')['ElButton'] + ElCard: typeof import('element-plus/es')['ElCard'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] + ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] + ElDialog: typeof import('element-plus/es')['ElDialog'] + ElEmpty: typeof import('element-plus/es')['ElEmpty'] + ElForm: typeof import('element-plus/es')['ElForm'] + ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElRadio: typeof import('element-plus/es')['ElRadio'] + ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] + ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSwitch: typeof import('element-plus/es')['ElSwitch'] + ElTable: typeof import('element-plus/es')['ElTable'] + ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTag: typeof import('element-plus/es')['ElTag'] + ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] + ElTooltip: typeof import('element-plus/es')['ElTooltip'] + } +} diff --git a/plugins/tiga/docs/superpowers/plans/2026-06-10-tiga-implementation.md b/plugins/tiga/docs/superpowers/plans/2026-06-10-tiga-implementation.md new file mode 100644 index 00000000..c56fd911 --- /dev/null +++ b/plugins/tiga/docs/superpowers/plans/2026-06-10-tiga-implementation.md @@ -0,0 +1,1714 @@ +# 提肛助手插件实现计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 实现提肛助手 Ztools 插件,支持定时提醒、运动引导、统计打卡、教程指南。 + +**Architecture:** Vue 3 组件化架构,preload 层提供 Node.js 服务(定时器、存储、通知),Vue 层通过 IPC 事件与 preload 层通信。设置页面为主入口,统计、教程为独立页面,运动引导为弹窗组件。 + +**Tech Stack:** Vue 3 + TypeScript + Vite + Ztools API + +--- + +## 文件结构 + +| 文件 | 职责 | +|------|------| +| `src/types.ts` | 类型定义(Config、StatsData、文案等) | +| `public/preload/services.js` | Node.js 服务层(定时器、存储、通知) | +| `public/plugin.json` | 插件配置(features 入口定义) | +| `src/App.vue` | 路由入口(监听 IPC 事件、组件切换) | +| `src/Settings/index.vue` | 设置页面(配置项 UI + 存储) | +| `src/Stats/index.vue` | 统计页面(今日完成、打卡、历史) | +| `src/ExerciseGuide/index.vue` | 运动引导弹窗(倒计时 + 进度) | +| `src/Tutorial/index.vue` | 教程页面(渲染 markdown) | +| `src/assets/tutorial.md` | 教程内容 | + +--- + +## Task 1: 类型定义 + +**Files:** +- Create: `src/types.ts` + +- [ ] **Step 1: 创建类型定义文件** + +```typescript +// src/types.ts + +// 配置数据 +export interface Config { + // 提醒设置 + interval: number // 提醒间隔(分钟),默认 30 + workTimeMode: 'single' | 'multi' | 'weekly' // 工作时段模式 + workTime: WorkTimeConfig + + // 提醒与计数 + reminderMode: 'notify' | 'notify+popup' // 仅通知 / 通知+弹窗 + countMode: 'auto' | 'manual' // 自动计数 / 手动计数 + + // 运动参数 + exercise: { + contractSeconds: number // 收缩时长(秒) + relaxSeconds: number // 放松时长(秒) + repeatCount: number // 重复次数 + } + + // 文案风格 + style: 'random' | 'normal' | 'funny' + + // 运行状态 + enabled: boolean // 开关启停 +} + +// 工作时段配置 +export interface WorkTimeConfig { + single: { start: string; end: string } + multi: Array<{ start: string; end: string }> + weekly: Record +} + +// 统计数据 +export interface StatsData { + records: Array<{ + date: string // YYYY-MM-DD + count: number // 当日完成次数 + }> +} + +// 通知文案 +export interface NotificationText { + title: string + body: string +} + +// 默认配置 +export const DEFAULT_CONFIG: Config = { + interval: 30, + workTimeMode: 'single', + workTime: { + single: { start: '09:00', end: '18:00' }, + multi: [{ start: '09:00', end: '12:00' }, { start: '14:00', end: '18:00' }], + weekly: { + 0: null, // 周日 + 1: { start: '09:00', end: '18:00' }, + 2: { start: '09:00', end: '18:00' }, + 3: { start: '09:00', end: '18:00' }, + 4: { start: '09:00', end: '18:00' }, + 5: { start: '09:00', end: '18:00' }, + 6: null, // 周六 + } + }, + reminderMode: 'notify+popup', + countMode: 'manual', + exercise: { + contractSeconds: 5, + relaxSeconds: 5, + repeatCount: 10 + }, + style: 'random', + enabled: true +} + +// 存储键名 +export const STORAGE_KEYS = { + config: 'tiga_config', + stats: 'tiga_stats' +} + +// 正经文案 +export const NORMAL_TEXT: NotificationText = { + title: '提肛提醒', + body: '久坐伤身,该做提肛运动了,关爱您的盆底健康。' +} + +// 搞怪文案池 +export const FUNNY_TEXTS: NotificationText[] = [ + { title: '盆底肌呼叫中心', body: '您的盆底肌已欠费停机,请立即充值(提肛)恢复服务!' }, + { title: '提肛小助手', body: '别坐了别坐了,屁股要废了!起来动一动,提肛保健康!' }, + { title: '健康警报', body: '警告:您的臀部已连续静止太久,建议立即启动提肛程序!' }, + { title: '提肛时间到', body: '收缩、放松,让盆底肌跳起健康的舞蹈!' }, + { title: '盆底健康守护', body: '提肛一分钟,健康一整天!现在就开始吧~' }, + { title: '久坐预警', body: '您的屁股正在酝酿抗议,请立即安抚(提肛)!' }, + { title: '提肛大使', body: '来自未来的你发来消息:感谢现在坚持提肛的我!' }, + { title: '健康投递', body: '叮咚!您的盆底健康快递已送达,请签收(提肛)!' }, + { title: '提肛站', body: '下一站:提肛站。请各位乘客做好准备,收缩放松!' }, + { title: '盆底健身房', body: '您有一张免费的盆底健身券,有效期:现在!' } +] + +// 获取随机文案 +export function getRandomText(style: Config['style']): NotificationText { + if (style === 'normal') { + return NORMAL_TEXT + } + + const allTexts = [NORMAL_TEXT, ...FUNNY_TEXTS] + + if (style === 'funny') { + const index = Math.floor(Math.random() * FUNNY_TEXTS.length) + return FUNNY_TEXTS[index] + } + + // random: 从全部文案中随机 + const index = Math.floor(Math.random() * allTexts.length) + return allTexts[index] +} + +// 计算连续打卡天数 +export function calculateStreak(records: StatsData['records']): number { + if (!records.length) return 0 + + const today = new Date().toISOString().split('T')[0] + let streak = 0 + let checkDate = new Date(today) + + // 按日期降序排序 + const sortedRecords = [...records].sort((a, b) => b.date.localeCompare(a.date)) + + for (let i = 0; i < 365; i++) { // 最多检查一年 + const dateStr = checkDate.toISOString().split('T')[0] + const record = sortedRecords.find(r => r.date === dateStr) + + if (record && record.count > 0) { + streak++ + } else if (record && record.count === 0) { + break // 有记录但 count=0,中断 + } + // 无记录的日期跳过(不算中断) + + checkDate.setDate(checkDate.getDate() - 1) + } + + return streak +} + +// 获取今日日期字符串 +export function getTodayDate(): string { + return new Date().toISOString().split('T')[0] +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/types.ts +git commit -m "feat: add type definitions and utility functions" +``` + +--- + +## Task 2: preload 服务层 + +**Files:** +- Modify: `public/preload/services.js` + +- [ ] **Step 1: 扩展 preload 服务层** + +```javascript +// public/preload/services.js +const fs = require('node:fs') +const path = require('node:path') + +// 存储文件路径 +const getStoragePath = (key) => { + const userDataPath = window.ztools.getPath('userData') + return path.join(userDataPath, `tiga_${key}.json`) +} + +// 桌面通知点击回调存储 +let notificationClickCallback = null + +window.services = { + // ===== 定时器管理 ===== + timerId: null, + + startTimer(intervalMinutes) { + if (this.timerId) { + clearInterval(this.timerId) + } + this.timerId = setInterval(() => { + this._onTimerTick() + }, intervalMinutes * 60 * 1000) + }, + + stopTimer() { + if (this.timerId) { + clearInterval(this.timerId) + this.timerId = null + } + }, + + _onTimerTick() { + // 通知 Vue 层定时器触发 + window.ztools.ipcSend('tiga_timer_tick') + }, + + // ===== 工作时段判断 ===== + isInWorkTime(workTimeConfig, workTimeMode) { + const now = new Date() + const currentHour = now.getHours() + const currentMinute = now.getMinutes() + const currentTimeStr = `${currentHour.toString().padStart(2, '0')}:${currentMinute.toString().padStart(2, '0')}` + const currentDay = now.getDay() // 0-6, 0=周日 + + const isInRange = (start, end) => { + return currentTimeStr >= start && currentTimeStr <= end + } + + if (workTimeMode === 'single') { + return isInRange(workTimeConfig.single.start, workTimeConfig.single.end) + } + + if (workTimeMode === 'multi') { + return workTimeConfig.multi.some(period => + isInRange(period.start, period.end) + ) + } + + if (workTimeMode === 'weekly') { + const dayConfig = workTimeConfig.weekly[currentDay] + if (!dayConfig) return false // 该天未配置或为 null + return isInRange(dayConfig.start, dayConfig.end) + } + + return false + }, + + // ===== 桌面通知 ===== + sendNotification(title, body) { + const notification = new Notification(title, { + body: body, + requireInteraction: true // 保持通知不自动消失 + }) + + notification.onclick = () => { + // 通知 Vue 层通知被点击 + window.ztools.ipcSend('tiga_notification_clicked') + notification.close() + window.ztools.showMainWindow() + } + }, + + // ===== 存储 API ===== + getItem(key) { + const filePath = getStoragePath(key) + try { + if (fs.existsSync(filePath)) { + const content = fs.readFileSync(filePath, { encoding: 'utf-8' }) + return JSON.parse(content) + } + } catch (e) { + console.error('Failed to read storage:', e) + } + return null + }, + + setItem(key, value) { + const filePath = getStoragePath(key) + try { + fs.writeFileSync(filePath, JSON.stringify(value, null, 2), { encoding: 'utf-8' }) + } catch (e) { + console.error('Failed to write storage:', e) + } + }, + + removeItem(key) { + const filePath = getStoragePath(key) + try { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath) + } + } catch (e) { + console.error('Failed to remove storage:', e) + } + }, + + // ===== 初始化 ===== + init() { + // 读取配置,如果存在则启动定时器 + const config = this.getItem('tiga_config') + if (config && config.enabled) { + this.startTimer(config.interval) + } + } +} + +// 初始化 +window.services.init() +``` + +- [ ] **Step 2: Commit** + +```bash +git add public/preload/services.js +git commit -m "feat: extend preload services with timer, notification and storage" +``` + +--- + +## Task 3: plugin.json 配置 + +**Files:** +- Modify: `public/plugin.json` + +- [ ] **Step 1: 更新 plugin.json** + +```json +{ + "$schema": "node_modules/@ztools-center/ztools-api-types/resource/ztools.schema.json", + "name": "tiga", + "title": "提肛助手", + "description": "定时提醒提肛运动,关爱久坐族盆底健康", + "author": "咖啡八杯", + "version": "1.0.0", + "main": "index.html", + "preload": "preload/services.js", + "logo": "logo.png", + "development": { + "main": "http://localhost:5173" + }, + "features": [ + { + "code": "settings", + "explain": "提肛助手设置", + "icon": "logo.png", + "cmds": ["提肛", "提肛助手", "提肛设置"] + }, + { + "code": "stats", + "explain": "查看提肛统计", + "icon": "logo.png", + "cmds": ["提肛统计"] + }, + { + "code": "tutorial", + "explain": "提肛运动教程", + "icon": "logo.png", + "cmds": ["提肛教程"] + } + ] +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add public/plugin.json +git commit -m "feat: update plugin.json with tiga features" +``` + +--- + +## Task 4: App.vue 路由入口 + +**Files:** +- Modify: `src/App.vue` + +- [ ] **Step 1: 更新 App.vue 路由** + +```vue + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/App.vue +git commit -m "feat: update App.vue with routing and IPC event handling" +``` + +--- + +## Task 5: Settings 设置页面 + +**Files:** +- Create: `src/Settings/index.vue` + +- [ ] **Step 1: 创建 Settings 页面** + +```vue + + + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/Settings/index.vue +git commit -m "feat: create Settings page with all configuration options" +``` + +--- + +## Task 6: Stats 统计页面 + +**Files:** +- Create: `src/Stats/index.vue` + +- [ ] **Step 1: 创建 Stats 页面** + +```vue + + + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/Stats/index.vue +git commit -m "feat: create Stats page with today count and streak display" +``` + +--- + +## Task 7: ExerciseGuide 运动引导弹窗 + +**Files:** +- Create: `src/ExerciseGuide/index.vue` + +- [ ] **Step 1: 创建 ExerciseGuide 弹窗** + +```vue + + + + + +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/ExerciseGuide/index.vue +git commit -m "feat: create ExerciseGuide popup with countdown and progress" +``` + +--- + +## Task 8: Tutorial 教程页面 + +**Files:** +- Create: `src/Tutorial/index.vue` +- Create: `src/assets/tutorial.md` + +- [ ] **Step 1: 创建教程 markdown 内容** + +```markdown +# 提肛运动指南 + +## 什么是提肛运动? + +提肛运动是一种锻炼盆底肌的简单运动,通过有意识地收缩和放松肛门括约肌及相关肌肉群,增强盆底肌肉力量,改善盆底功能。 + +盆底肌像一张"吊网",支撑着膀胱、子宫、直肠等器官。久坐、怀孕、 aging 等因素会导致盆底肌松弛,引发尿失禁、便秘等问题。 + +## 正确姿势 + +### 基本要点 + +1. **姿势选择**:坐姿、站姿、躺姿均可,初学者建议坐姿或躺姿 +2. **身体放松**:保持全身放松,避免用力过猛 +3. **呼吸自然**:不要憋气,保持正常呼吸节奏 + +### 动作步骤 + +1. 吸气时收缩肛门和会阴部肌肉(像憋尿的感觉) +2. 保持收缩 3-5 秒 +3. 呼气时缓慢放松肌肉 +4. 休息 3-5 秒后重复 + +## 呼吸配合 + +正确的呼吸配合能增强锻炼效果: + +- **收缩时**:吸气或屏息均可,找到适合自己的方式 +- **放松时**:呼气,感受肌肉完全放松 + +建议初学者:收缩时吸气,放松时呼气,节奏自然。 + +## 注意事项 + +### 常见错误 + +- ❌ 收缩时憋气太久 +- ❌ 用力过猛、过快 +- ❌ 收缩腹部或臀部肌肉(应只收缩盆底肌) +- ❌ 练习过于频繁(建议间隔 2-3 小时) + +### 建议 + +- ✅ 循序渐进,从短时间开始 +- ✅ 每天练习 3-4 次,每次 10-15 个循环 +- ✅ 坚持 3 个月以上才能看到明显效果 +- ✅ 如有不适,请咨询医生 + +## 盆底肌健康小贴士 + +1. 避免长时间久坐,每小时起身活动 +2. 保持健康体重,减轻盆底压力 +3. 多喝水,预防便秘 +4. 定期进行提肛运动,养成习惯 + +--- + +💪 坚持提肛,守护盆底健康! +``` + +- [ ] **Step 2: 创建 Tutorial 页面** + +```vue + + + + + +``` + +- [ ] **Step 3: Commit** + +```bash +git add src/Tutorial/index.vue src/assets/tutorial.md +git commit -m "feat: create Tutorial page with markdown content" +``` + +--- + +## Task 9: 清理旧文件 + +**Files:** +- Delete: `src/Hello/index.vue` +- Delete: `src/Read/index.vue` +- Delete: `src/Write/index.vue` +- Modify: `README.md` (更新文档) + +- [ ] **Step 1: 删除旧的示例组件** + +```bash +rm -rf src/Hello src/Read src/Write +``` + +- [ ] **Step 2: 更新 README.md** + +```markdown +# tiga + +> 定时提醒提肛运动,关爱久坐族盆底健康 + +这是一个使用 **Vue 3 + Vite + TypeScript** 构建的 ZTools 插件。 + +## ✨ 功能特性 + +- **定时提醒** - 可自定义提醒间隔(默认 30 分钟)及工作时段 +- **桌面通知** - 到点发送桌面通知,支持多种文案风格 +- **运动引导** - 弹窗展示收缩/放松倒计时过程,引导用户完成运动 +- **教程指南** - 图文并茂的提肛姿势指南与呼吸配合教程 +- **统计打卡** - 每日完成次数统计与连续打卡记录 + +## 🚀 快速开始 + +### 安装依赖 + +```bash +npm install +``` + +### 开发模式 + +```bash +npm run dev +``` + +### 构建生产版本 + +```bash +npm run build +``` + +## 📖 使用说明 + +- 输入 `提肛` 或 `提肛助手` 进入设置页面 +- 输入 `提肛统计` 查看完成记录 +- 输入 `提肛教程` 学习正确姿势 + +## 📄 开源协议 + +MIT License +``` + +- [ ] **Step 3: Commit** + +```bash +git add -A +git commit -m "chore: remove old example components and update README" +``` + +--- + +## Task 10: 构建测试 + +**Files:** +- None (验证构建) + +- [ ] **Step 1: 运行构建** + +```bash +npm run build +``` + +Expected: 构建成功,输出到 `dist/` 目录 + +- [ ] **Step 2: 检查构建产物** + +```bash +ls dist/ +``` + +Expected: 包含 `index.html`、JS、CSS 等文件 + +--- + +## Spec Coverage Check + +| Spec Requirement | Task Coverage | +|-----------------|---------------| +| 定时提醒间隔配置 | Task 1 (types), Task 5 (Settings) | +| 工作时段配置 | Task 1 (types), Task 5 (Settings), Task 2 (isInWorkTime) | +| 桌面通知 | Task 2 (sendNotification) | +| 文案风格切换 | Task 1 (getRandomText), Task 5 (Settings) | +| 运动引导弹窗 | Task 7 (ExerciseGuide) | +| 运动参数配置 | Task 1 (types), Task 5 (Settings) | +| 统计展示 | Task 6 (Stats) | +| 连续打卡计算 | Task 1 (calculateStreak), Task 6 (Stats) | +| 教程页面 | Task 8 (Tutorial) | +| 开关启停 | Task 5 (Settings), Task 2 (startTimer/stopTimer) | +| 清除历史 | Task 5 (Settings) | +| IPC 事件通信 | Task 2 (ipcSend), Task 4 (App.vue ipcOn) | + +✅ All requirements covered. + +--- + +## Placeholder Check + +- ✅ No "TBD" or "TODO" placeholders +- ✅ All code blocks contain complete implementation +- ✅ All file paths are exact +- ✅ No references to undefined types/functions + +--- + +## Type Consistency Check + +- ✅ `Config` interface used consistently in Task 1, 4, 5 +- ✅ `StatsData` interface used consistently in Task 1, 6 +- ✅ `STORAGE_KEYS` used consistently in Task 1, 2, 4, 5, 6 +- ✅ `getRandomText` defined in Task 1, used in Task 4 +- ✅ `calculateStreak` defined in Task 1, used in Task 6 +- ✅ `getTodayDate` defined in Task 1, used in Task 4, 6 + +--- + +Plan complete and saved to `docs/superpowers/plans/2026-06-10-tiga-implementation.md`. + +**Two execution options:** + +**1. Subagent-Driven (recommended)** - I dispatch a fresh subagent per task, review between tasks, fast iteration + +**2. Inline Execution** - Execute tasks in this session using executing-plans, batch execution with checkpoints + +Which approach? \ No newline at end of file diff --git a/plugins/tiga/docs/superpowers/specs/2026-06-10-tiga-design.md b/plugins/tiga/docs/superpowers/specs/2026-06-10-tiga-design.md new file mode 100644 index 00000000..9ff25d25 --- /dev/null +++ b/plugins/tiga/docs/superpowers/specs/2026-06-10-tiga-design.md @@ -0,0 +1,347 @@ +# 提肛助手插件设计文档 + +> 定时提醒提肛运动,关爱久坐族盆底健康 + +## 概述 + +提肛助手是一个 Ztools 插件,帮助久坐用户养成提肛运动的习惯。通过定时提醒、运动引导、统计分析等功能,让盆底肌锻炼成为日常习惯。 + +## 功能清单 + +| 功能模块 | 描述 | +|---------|------| +| 定时提醒 | 可自定义提醒间隔(默认 30 分钟)及工作时段 | +| 桌面通知 | 到点发送桌面通知,支持多种文案风格 | +| 运动引导 | 弹窗展示收缩/放松倒计时过程,引导用户完成运动 | +| 教程指南 | 图文并茂的提肛姿势指南与呼吸配合教程 | +| 统计打卡 | 每日完成次数统计与连续打卡记录 | + +--- + +## 架构设计 + +### 文件结构 + +``` +tiga/ +├── public/ +│ ├── logo.png # 插件图标 +│ ├── plugin.json # 插件配置(features 入口) +│ └── preload/ +│ ├── package.json +│ └── services.js # Node.js 服务层 +│ ├── 定时器管理 +│ ├── 工作时段判断 +│ ├── 桌面通知发送 +│ └── 本地存储读写 +│ +├── src/ +│ ├── App.vue # 路由入口 +│ ├── main.ts # 应用入口 +│ ├── main.css # 全局样式 +│ │ +│ ├── Settings/ # 设置模块(主入口) +│ │ └── index.vue # 设置页面 +│ │ +│ ├── Stats/ # 统计模块 +│ │ └── index.vue # 统计展示页面 +│ │ +│ ├── ExerciseGuide/ # 运动引导弹窗 +│ │ └── index.vue # 倒计时引导界面 +│ │ +│ ├── Tutorial/ # 教程模块 +│ │ └── index.vue # 教程展示页面 +│ │ +│ └── assets/ +│ └── tutorial.md # 提肛教程内容 +``` + +### 数据流 + +``` +用户设置 → preload 层启动定时器 + → 定时器到达 → 检查工作时段 → 发送桌面通知 + → 用户点击通知 → Vue 层弹出 ExerciseGuide + → 用户完成运动 → 更新统计数据 → 存储到本地 +``` + +--- + +## 模块详细设计 + +### 1. Settings 设置页面 + +**功能**:用户配置所有提醒参数,作为插件主入口。 + +**配置项**: + +| 配置项 | 类型 | 默认值 | 说明 | +|-------|------|-------|------| +| 提醒间隔 | number | 30 | 分钟 | +| 工作时段模式 | enum | single | single/multi/weekly | +| 固定时段 | object | 09:00-18:00 | start, end | +| 多时段 | array | - | 多个时间段 | +| 周几时段 | object | - | 按周几配置时段 | +| 提醒方式 | enum | notify+popup | 仅通知 / 通知+弹窗 | +| 计数方式 | enum | manual | 自动计数 / 手动计数 | +| 收缩时长 | number | 5 | 秒 | +| 放松时长 | number | 5 | 秒 | +| 重复次数 | number | 10 | 次 | +| 文案风格 | enum | random | 随机/正经/搞怪 | +| 运行状态 | boolean | true | 开关启停 | + +**工作时段模式说明**: + +- **固定时段 (single)**:单个时间范围,如 09:00 - 18:00 +- **多时段 (multi)**:多个时间段,如 09:00-12:00, 14:00-18:00,支持添加/删除 +- **周几时段 (weekly)**:按周一到周日配置,未选中的日子不提醒 + +--- + +### 2. Stats 统计页面 + +**功能**:展示今日完成次数、连续打卡天数、历史记录。 + +**数据展示**: + +- 今日完成次数 +- 连续打卡天数(只要有完成就算打卡) +- 历史记录列表(日期 + 完成次数) +- 清除历史确认弹窗 + +**连续打卡计算逻辑**: + +- 从最近一天往前遍历记录 +- 遇到 count > 0 继续计算 +- 遇到 count = 0 停止,返回累计天数 +- 跳过未记录日期(不算中断) + +--- + +### 3. ExerciseGuide 运动引导弹窗 + +**功能**:展示收缩/放松倒计时,引导用户完成运动。 + +**运动流程**: + +1. 收缩阶段:显示"收缩中..." + 倒计时秒数 + 进度条 +2. 放松阶段:自动切换,显示"放松中..." + 倒计时 + 进度条 +3. 重复循环:按配置次数执行 +4. 结束提示:显示"已完成"/"跳过"按钮 + +**按钮行为**: + +- 点击"已完成" → 关闭弹窗 → 计数一次 → 更新统计 +- 点击"跳过" → 关闭弹窗 → 不计数 + +--- + +### 4. Tutorial 教程页面 + +**功能**:渲染 markdown 教程内容,展示提肛运动指南。 + +**内容**: + +- 什么是提肛运动 +- 正确姿势 +- 呼吸配合 +- 注意事项 + +**渲染方式**:Vue 组件渲染 markdown,样式适配 Ztools 主题。 + +--- + +### 5. preload/services.js 服务层 + +**核心职责**: + +```javascript +window.services = { + // 定时器管理 + startTimer(intervalMinutes, callback), + stopTimer(), + updateTimer(intervalMinutes), + + // 工作时段判断 + isInWorkTime(workTimeConfig), + + // 桌面通知 + sendNotification(title, body, onClickCallback), + + // 存储 API + getItem(key), + setItem(key, value), + removeItem(key) +} +``` + +--- + +## 数据存储设计 + +### 数据结构 + +```typescript +// 配置数据 +interface Config { + interval: number // 提醒间隔(分钟) + workTimeMode: 'single' | 'multi' | 'weekly' + workTime: { + single: { start: string, end: string } + multi: Array<{ start: string, end: string }> + weekly: Record + } + reminderMode: 'notify' | 'notify+popup' + countMode: 'auto' | 'manual' + exercise: { + contractSeconds: number + relaxSeconds: number + repeatCount: number + } + style: 'random' | 'normal' | 'funny' + enabled: boolean +} + +// 统计数据 +interface StatsData { + records: Array<{ + date: string // YYYY-MM-DD + count: number // 当日完成次数 + }> +} +``` + +### 存储键名 + +```typescript +const STORAGE_KEYS = { + config: 'tiga_config', + stats: 'tiga_stats' +} +``` + +### 存储方式 + +使用 `window.ztools` 提供的存储 API: + +- `window.ztools.getItem(key)` - 读取 +- `window.ztools.setItem(key, value)` - 写入 +- `window.ztools.removeItem(key)` - 删除 + +--- + +## 通知文案设计 + +### 正经风格 + +```json +{ + "title": "提肛提醒", + "body": "久坐伤身,该做提肛运动了,关爱您的盆底健康。" +} +``` + +### 搞怪风格(文案池) + +```json +[ + { + "title": "盆底肌呼叫中心", + "body": "您的盆底肌已欠费停机,请立即充值(提肛)恢复服务!" + }, + { + "title": "提肛小助手", + "body": "别坐了别坐了,屁股要废了!起来动一动,提肛保健康!" + }, + { + "title": "健康警报", + "body": "警告:您的臀部已连续静止太久,建议立即启动提肛程序!" + }, + { + "title": "提肛时间到", + "body": "收缩、放松,让盆底肌跳起健康的舞蹈!" + }, + { + "title": "盆底健康守护", + "body": "提肛一分钟,健康一整天!现在就开始吧~" + }, + { + "title": "久坐预警", + "body": "您的屁股正在酝酿抗议,请立即安抚(提肛)!" + }, + { + "title": "提肛大使", + "body": "来自未来的你发来消息:感谢现在坚持提肛的我!" + }, + { + "title": "健康投递", + "body": "叮咚!您的盆底健康快递已送达,请签收(提肛)!" + }, + { + "title": "提肛站", + "body": "下一站:提肛站。请各位乘客做好准备,收缩放松!" + }, + { + "title": "盆底健身房", + "body": "您有一张免费的盆底健身券,有效期:现在!" + } +] +``` + +### 文案选择逻辑 + +| 设置风格 | 实际文案 | +|---------|---------| +| 正经 | 固定使用正经文案(唯一一条) | +| 搞怪 | 从搞怪文案池(10条)随机抽取一条 | +| 随机 | 从正经(1条)+ 搞怪池(10条)共 11 条文案中随机抽取一条 | + +--- + +## plugin.json 配置 + +```json +{ + "name": "tiga", + "title": "提肛助手", + "description": "定时提醒提肛运动,关爱久坐族盆底健康", + "author": "咖啡八杯", + "version": "1.0.0", + "main": "index.html", + "preload": "preload/services.js", + "logo": "logo.png", + "features": [ + { + "code": "settings", + "explain": "提肛助手设置", + "icon": "logo.png", + "cmds": ["提肛", "提肛助手", "提肛设置"] + }, + { + "code": "stats", + "explain": "查看提肛统计", + "icon": "logo.png", + "cmds": ["提肛统计"] + }, + { + "code": "tutorial", + "explain": "提肛运动教程", + "icon": "logo.png", + "cmds": ["提肛教程"] + } + ] +} +``` + +--- + +## 实现优先级 + +1. **基础框架**:App.vue 路由 + plugin.json 配置 +2. **Settings 页面**:配置项 UI + 存储功能 +3. **preload 服务层**:定时器 + 存储 API +4. **桌面通知**:通知发送 + 点击回调 +5. **ExerciseGuide**:运动引导弹窗 +6. **Stats 页面**:统计展示 +7. **Tutorial 页面**:教程渲染 +8. **文案系统**:风格切换逻辑 \ No newline at end of file diff --git a/plugins/tiga/index.html b/plugins/tiga/index.html new file mode 100644 index 00000000..eaa17316 --- /dev/null +++ b/plugins/tiga/index.html @@ -0,0 +1,11 @@ + + + + + + + +
+ + + diff --git a/plugins/tiga/package-lock.json b/plugins/tiga/package-lock.json new file mode 100644 index 00000000..76ada06a --- /dev/null +++ b/plugins/tiga/package-lock.json @@ -0,0 +1,2107 @@ +{ + "name": "tiga", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tiga", + "version": "1.0.0", + "dependencies": { + "element-plus": "^2.14.1", + "vue": "^3.5.13" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "@ztools-center/ztools-api-types": "^1.0.1", + "typescript": "^5.3.0", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^32.1.0", + "vite": "^6.0.11", + "vue-tsc": "^2.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz", + "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz", + "integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.15.tgz", + "integrity": "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.15.tgz", + "integrity": "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.15.tgz", + "integrity": "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz", + "integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/shared": "3.5.35", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz", + "integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz", + "integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@vue/compiler-core": "3.5.35", + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.15", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz", + "integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/language-core": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.12.tgz", + "integrity": "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.15", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^1.0.3", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz", + "integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz", + "integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/shared": "3.5.35" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz", + "integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.35", + "@vue/runtime-core": "3.5.35", + "@vue/shared": "3.5.35", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz", + "integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "vue": "3.5.35" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz", + "integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==", + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.3.0.tgz", + "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.3.0", + "@vueuse/shared": "14.3.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.3.0.tgz", + "integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.3.0.tgz", + "integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@ztools-center/ztools-api-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ztools-center/ztools-api-types/-/ztools-api-types-1.0.3.tgz", + "integrity": "sha512-dv1eOAIasAupqKaQL/gESk1i2+RebdM/1gvZhrvH2D/bo3enCUsAGJ8nrHnlCLBSOGB81eC/SU0IH8BNsUlmvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alien-signals": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-1.0.13.tgz", + "integrity": "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/element-plus": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.14.1.tgz", + "integrity": "sha512-UFnm1+BckNi+azkKJ7L32q1uXs9ekr99Z9pWTQPeDR05jqEWUwQq51ro4kZMVrANbjknX3Z7ukCZwTi2T6Tr9A==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^4.2.0", + "@element-plus/icons-vue": "^2.3.2", + "@floating-ui/dom": "^1.7.6", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.8", + "@types/lodash": "^4.17.24", + "@types/lodash-es": "^4.17.12", + "@vueuse/core": "14.3.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.20", + "lodash": "^4.18.1", + "lodash-es": "^4.18.1", + "lodash-unified": "^1.0.3", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0", + "vue-component-type-helpers": "^3.3.1" + }, + "peerDependencies": { + "vue": "^3.3.7" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.2.1.tgz", + "integrity": "sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unimport": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-5.7.0.tgz", + "integrity": "sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "pkg-types": "^2.3.0", + "scule": "^1.3.0", + "strip-literal": "^3.1.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.11", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unimport/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin-auto-import": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/unplugin-auto-import/-/unplugin-auto-import-21.0.0.tgz", + "integrity": "sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "picomatch": "^4.0.3", + "unimport": "^5.6.0", + "unplugin": "^2.3.11", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@nuxt/kit": "^4.0.0", + "@vueuse/core": "*" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@vueuse/core": { + "optional": true + } + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-vue-components": { + "version": "32.1.0", + "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-32.1.0.tgz", + "integrity": "sha512-YiUkSxuRjab18XFOrX5VsIxXzccrfmHVGsGeJgSgklb829DQmCy9E4vvDUE4tuvZZdxyFJZX0Oc4TPnnxiiMyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "local-pkg": "^1.2.0", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "obug": "^2.1.1", + "picomatch": "^4.0.4", + "tinyglobby": "^0.2.16", + "unplugin": "^3.0.0", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@nuxt/kit": "^3.2.2 || ^4.0.0", + "vue": "^3.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/unplugin-vue-components/node_modules/unplugin": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz", + "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.35", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz", + "integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.35", + "@vue/compiler-sfc": "3.5.35", + "@vue/runtime-dom": "3.5.35", + "@vue/server-renderer": "3.5.35", + "@vue/shared": "3.5.35" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-3.3.4.tgz", + "integrity": "sha512-joip1uZTaQR0nD23N400gIdJ7xY+WiiiMA/BCKz842gvGBknqDQAzklUvDEhqFvvrhQY8S2ZANBMu4X70VMFGw==", + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.2.12.tgz", + "integrity": "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.15", + "@vue/language-core": "2.2.12" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/plugins/tiga/package.json b/plugins/tiga/package.json new file mode 100644 index 00000000..68809b08 --- /dev/null +++ b/plugins/tiga/package.json @@ -0,0 +1,23 @@ +{ + "name": "tiga", + "version": "1.1.0", + "description": "定时提醒提肛运动,关爱久坐族盆底健康", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build" + }, + "dependencies": { + "element-plus": "^2.14.1", + "vue": "^3.5.13" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "@ztools-center/ztools-api-types": "^1.0.1", + "typescript": "^5.3.0", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^32.1.0", + "vite": "^6.0.11", + "vue-tsc": "^2.0.0" + } +} diff --git a/plugins/tiga/public/exercise-guide.html b/plugins/tiga/public/exercise-guide.html new file mode 100644 index 00000000..461c3bc6 --- /dev/null +++ b/plugins/tiga/public/exercise-guide.html @@ -0,0 +1,454 @@ + + + + + + 提肛运动 + + + + +
+
+ +
+
+ + + + \ No newline at end of file diff --git a/plugins/tiga/public/logo.png b/plugins/tiga/public/logo.png new file mode 100644 index 00000000..bcfab521 Binary files /dev/null and b/plugins/tiga/public/logo.png differ diff --git a/plugins/tiga/public/plugin.json b/plugins/tiga/public/plugin.json new file mode 100644 index 00000000..ef689e64 --- /dev/null +++ b/plugins/tiga/public/plugin.json @@ -0,0 +1,35 @@ +{ + "$schema": "node_modules/@ztools-center/ztools-api-types/resource/ztools.schema.json", + "name": "tiga", + "title": "提肛助手", + "description": "定时提醒提肛运动,关爱久坐族盆底健康", + "author": "咖啡八杯", + "version": "1.1.0", + "categories": ["productivity"], + "main": "index.html", + "preload": "preload/services.js", + "logo": "logo.png", + "development": { + "main": "http://localhost:5173" + }, + "features": [ + { + "code": "settings", + "explain": "提肛助手设置", + "icon": "logo.png", + "cmds": ["提肛", "提肛助手", "提肛设置"] + }, + { + "code": "stats", + "explain": "查看提肛统计", + "icon": "logo.png", + "cmds": ["提肛统计"] + }, + { + "code": "tutorial", + "explain": "提肛运动教程", + "icon": "logo.png", + "cmds": ["提肛教程"] + } + ] +} \ No newline at end of file diff --git a/plugins/tiga/public/preload/package.json b/plugins/tiga/public/preload/package.json new file mode 100644 index 00000000..5bbefffb --- /dev/null +++ b/plugins/tiga/public/preload/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/plugins/tiga/public/preload/services.js b/plugins/tiga/public/preload/services.js new file mode 100644 index 00000000..48717ef3 --- /dev/null +++ b/plugins/tiga/public/preload/services.js @@ -0,0 +1,287 @@ +// public/preload/services.js +const fs = require('node:fs') +const path = require('node:path') + +// 将 Date 格式化为本地时区的 YYYY-MM-DD 字符串 +// (避免 toISOString 在 UTC+8 凌晨 0-8 点把日期算成昨天,导致今日打卡记录存到错误日期) +function formatLocalDate(d) { + const y = d.getFullYear() + const m = String(d.getMonth() + 1).padStart(2, '0') + const day = String(d.getDate()).padStart(2, '0') + return `${y}-${m}-${day}` +} + +// 存储文件路径 +const getStoragePath = (key) => { + const userDataPath = window.ztools.getPath('userData') + return path.join(userDataPath, `tiga_${key}.json`) +} + +// 弹窗实例存储(用于单例化) +let exerciseWindowInstance = null + +window.services = { + // ===== 定时器管理 ===== + timerId: null, + + startTimer(intervalMinutes) { + if (this.timerId) { + clearInterval(this.timerId) + } + this.timerId = setInterval(() => { + this._onTimerTick() + }, intervalMinutes * 60 * 1000) + }, + + stopTimer() { + if (this.timerId) { + clearInterval(this.timerId) + this.timerId = null + } + }, + + _onTimerTick() { + const config = this.getItem('tiga_config') + if (!config || !config.enabled) return + + if (!this.isInWorkTime(config.workTime, config.workTimeMode)) return + + if (config.reminderMode === 'notify+popup') { + // 通知+弹窗模式:发系统通知,点击后弹窗 + const texts = this._getNotificationTexts(config) + const text = texts[Math.floor(Math.random() * texts.length)] + this.sendNotification(text.title, text.body, () => { + this._showExercisePopup() + }) + } else { + // 仅通知模式:直接弹窗 + this._showExercisePopup() + } + + if (config.countMode === 'auto') { + this.addRecord() + } + }, + + // 显示运动引导弹窗 + _showExercisePopup() { + this._closeAndClearExerciseWindow() + + const display = window.ztools.getPrimaryDisplay() + const { width, height } = display.workAreaSize + const windowWidth = 140 + const windowHeight = 120 + const padding = 20 + + exerciseWindowInstance = window.ztools.createBrowserWindow( + 'exercise-guide.html', + { + width: windowWidth, + height: windowHeight, + x: width - windowWidth - padding, + y: height - windowHeight - padding, + frame: false, + alwaysOnTop: true, + resizable: false, + fullscreenable: false, + transparent: false, + backgroundColor: '#1a1a1a', + hasShadow: true, + skipTaskbar: true, + }, + () => { + if (exerciseWindowInstance) { + exerciseWindowInstance.setSize(windowWidth, windowHeight) + } + } + ) + }, + + // 获取通知文案 + _getNotificationTexts(config) { + const normalTexts = config.normalTexts || [ + { title: '提肛提醒', body: '久坐伤身,该做提肛运动了,关爱您的盆底健康。' }, + { title: '健康时刻', body: '收缩盆底肌,保持5秒,放松5秒,关爱久坐族健康。' }, + { title: '运动提醒', body: '定时提肛运动,预防痔疮,改善盆底功能。' } + ] + + const funnyTexts = config.funnyTexts || [ + { title: '盆底肌呼叫中心', body: '您的盆底肌已欠费停机,请立即充值(提肛)恢复服务!' }, + { title: '提肛小助手', body: '别坐了别坐了,屁股要废了!起来动一动,提肛保健康!' }, + { title: '健康警报', body: '警告:您的臀部已连续静止太久,建议立即启动提肛程序!' }, + { title: '提肛时间到', body: '收缩、放松,让盆底肌跳起健康的舞蹈!' }, + { title: '盆底健康守护', body: '提肛一分钟,健康一整天!现在就开始吧~' }, + { title: '久坐预警', body: '您的屁股正在酝酿抗议,请立即安抚(提肛)!' }, + { title: '提肛大使', body: '来自未来的你发来消息:感谢现在坚持提肛的我!' }, + { title: '健康投递', body: '叮咚!您的盆底健康快递已送达,请签收(提肛)!' }, + { title: '提肛站', body: '下一站:提肛站。请各位乘客做好准备,收缩放松!' }, + { title: '盆底健身房', body: '您有一张免费的盆底健身券,有效期:现在!' } + ] + + if (config.style === 'normal') return normalTexts + if (config.style === 'funny') return funnyTexts + return [...normalTexts, ...funnyTexts] + }, + + // 增加打卡记录(统一入口,供 Vue 层和弹窗调用) + addRecord() { + const now = new Date() + const today = formatLocalDate(now) + const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}` + const stats = this.getItem('tiga_stats') || { records: [] } + // 防御性检查:兼容存储损坏或旧版本数据格式 + if (!Array.isArray(stats.records)) stats.records = [] + + const todayRecord = stats.records.find(r => r.date === today) + if (todayRecord) { + todayRecord.count++ + if (!todayRecord.times) todayRecord.times = [] + todayRecord.times.push(time) + } else { + stats.records.push({ date: today, count: 1, times: [time] }) + } + + this.setItem('tiga_stats', stats) + }, + + // ===== 工作时段判断 ===== + isInWorkTime(workTimeConfig, workTimeMode) { + const now = new Date() + const currentHour = now.getHours() + const currentMinute = now.getMinutes() + const currentTimeStr = `${currentHour.toString().padStart(2, '0')}:${currentMinute.toString().padStart(2, '0')}` + const currentDay = now.getDay() // 0-6, 0=周日 + + const isInRange = (start, end) => { + return currentTimeStr >= start && currentTimeStr <= end + } + + if (workTimeMode === 'single') { + return isInRange(workTimeConfig.single.start, workTimeConfig.single.end) + } + + if (workTimeMode === 'multi') { + return workTimeConfig.multi.some(period => + isInRange(period.start, period.end) + ) + } + + if (workTimeMode === 'weekly') { + const dayConfig = workTimeConfig.weekly[currentDay] + if (!dayConfig) return false // 该天未配置或为 null + return isInRange(dayConfig.start, dayConfig.end) + } + + return false + }, + + // ===== 桌面通知 ===== + // 优先使用 ZTools 平台 API(showNotification), + // 仅在需要点击回调(notify+popup 模式)时使用 Web Notification API + sendNotification(title, body, onClick) { + if (onClick && typeof Notification !== 'undefined') { + // 需要点击回调:使用 Web Notification API(支持 onclick) + try { + const notification = new Notification(title, { + body: body, + requireInteraction: true + }) + + notification.onclick = () => { + onClick() + notification.close() + } + return + } catch (e) { + console.warn('Web Notification 不可用,降级到 ztools.showNotification') + } + } + + // 无需点击回调或 Web Notification 不可用:使用 ZTools 平台 API + if (window.ztools && window.ztools.showNotification) { + window.ztools.showNotification(`${title}: ${body}`) + } + }, + + // 强制关闭并清空运动弹窗引用 + _closeAndClearExerciseWindow() { + if (!exerciseWindowInstance) return + + try { + // WindowInstance 提供 close()(API 类型声明中另有拼写错误的 destory(),统一用 close) + if (typeof exerciseWindowInstance.close === 'function') { + exerciseWindowInstance.close() + } + } catch (e) { + console.error('关闭窗口失败:', e) + } + + exerciseWindowInstance = null + }, + + // ===== 存储 API ===== + // 使用 ZTools dbStorage API(与 exercise-guide.html 共享) + getItem(key) { + if (window.ztools && window.ztools.dbStorage) { + return window.ztools.dbStorage.getItem(key) + } + // fallback 到文件存储(开发模式) + const filePath = getStoragePath(key) + try { + if (fs.existsSync(filePath)) { + const content = fs.readFileSync(filePath, { encoding: 'utf-8' }) + return JSON.parse(content) + } + } catch (e) { + console.error('Failed to read storage:', e) + } + return null + }, + + setItem(key, value) { + if (window.ztools && window.ztools.dbStorage) { + window.ztools.dbStorage.setItem(key, value) + return + } + // fallback 到文件存储(开发模式) + const filePath = getStoragePath(key) + try { + fs.writeFileSync(filePath, JSON.stringify(value, null, 2), { encoding: 'utf-8' }) + } catch (e) { + console.error('Failed to write storage:', e) + } + }, + + removeItem(key) { + if (window.ztools && window.ztools.dbStorage) { + window.ztools.dbStorage.removeItem(key) + return + } + // fallback 到文件存储(开发模式) + const filePath = getStoragePath(key) + try { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath) + } + } catch (e) { + console.error('Failed to remove storage:', e) + } + }, + + // ===== 初始化 ===== + init() { + // 读取配置,如果存在则启动定时器 + const config = this.getItem('tiga_config') + if (config && config.enabled) { + this.startTimer(config.interval) + } + + // 注意:子窗口(exercise-guide.html)通过 window.ztools.sendToParent 通知关闭, + // 但 ZTools 主窗口侧未提供对应的监听 API(ipcOn 不存在)。 + // 因此不在此注册监听;运动弹窗实例引用会在下次 _showExercisePopup + // 调用 _closeAndClearExerciseWindow 时统一清理,不影响功能。 + } +} + +// 初始化 +window.services.init() \ No newline at end of file diff --git a/plugins/tiga/src/App.vue b/plugins/tiga/src/App.vue new file mode 100644 index 00000000..0750f736 --- /dev/null +++ b/plugins/tiga/src/App.vue @@ -0,0 +1,152 @@ + + + + + \ No newline at end of file diff --git a/plugins/tiga/src/Settings/index.vue b/plugins/tiga/src/Settings/index.vue new file mode 100644 index 00000000..ac64d838 --- /dev/null +++ b/plugins/tiga/src/Settings/index.vue @@ -0,0 +1,707 @@ + + + + + \ No newline at end of file diff --git a/plugins/tiga/src/Stats/index.vue b/plugins/tiga/src/Stats/index.vue new file mode 100644 index 00000000..9605bb4c --- /dev/null +++ b/plugins/tiga/src/Stats/index.vue @@ -0,0 +1,267 @@ + + + + + \ No newline at end of file diff --git a/plugins/tiga/src/Tutorial/index.vue b/plugins/tiga/src/Tutorial/index.vue new file mode 100644 index 00000000..5670c61c --- /dev/null +++ b/plugins/tiga/src/Tutorial/index.vue @@ -0,0 +1,162 @@ + + + + + \ No newline at end of file diff --git a/plugins/tiga/src/assets/tutorial.md b/plugins/tiga/src/assets/tutorial.md new file mode 100644 index 00000000..13617c7b --- /dev/null +++ b/plugins/tiga/src/assets/tutorial.md @@ -0,0 +1,92 @@ +# 久坐族提肛运动指南 + +> 专为办公室久坐人群设计。用最通俗的语言,帮你搞懂提肛运动:是什么、有什么用、怎么在工位上做、什么不能做。 + +## 一、什么是提肛运动? + +简单说,就是有意识地收缩和放松"肛门到尿道"之间的那圈肌肉——医学上叫**盆底肌**。 + +#### 怎么找到这块肌肉? + +下次小便时,试着突然中断尿流,那块被你收紧的肌肉,就是盆底肌。 + +#### 跟久坐有什么关系? + +盆底肌像一张"吊床",兜在骨盆底部,托着膀胱、直肠等器官。久坐时,整个上半身的重量都压在这张吊床上,时间久了它就会松弛、无力,各种问题就来了。 + +## 二、久坐为什么要做提肛? + +- **预防和改善漏尿**:打喷嚏、咳嗽、大笑时漏尿(压力性尿失禁),根源就是盆底肌无力。久坐族是高发人群。 +- **改善便秘**:久坐导致盆底肌紧张、不协调,排便时使不上劲。提肛能帮肌肉恢复正常的收缩-放松节奏。 +- **预防痔疮**:久坐让肛周血液循环变差,是痔疮的主要诱因。提肛能促进局部血流,预防和缓解痔疮。 +- **预防器官下垂**:长期久坐、盆底肌松弛,膀胱、直肠等器官可能下移。提前锻炼可以预防。 +- **提升生活质量**:盆底肌健康关系到排尿、排便、性功能。早锻炼,早受益。 + +## 三、工位上怎么做? + +核心原则:**规律、正确,而非高频。** + +### 1. 什么时候做? + +**最佳时机**:每坐 30 分钟站起来活动时——接水、上厕所、伸懒腰,顺便做几下。这也是本插件提醒你的节奏。 + +**其他机会**:开会时、等电梯、排队、看剧广告……坐着站着都能做,完全不引人注意。 + +### 2. 具体怎么做? + +**坐姿即可,三步完成:** + +- **收紧**:缓慢吸气,向上、向内收缩盆底肌,感觉会阴部被提起来。腹部、臀部、大腿保持放松,不要代偿。 +- **保持**:维持收紧 5~10 秒(刚开始做不到,从 3 秒开始)。 +- **放松**:完全呼气,彻底放松盆底肌,休息至少 10 秒。 + +**呼吸要点**:收紧时吸气,放松时呼气。**绝对不要憋气!** + +### 3. 做多少合适? + +#### 两种模式搞清楚 + +- **结构化训练(主要)**:每天选 2~3 个固定时段(如早起、午休前、睡前),专心做完一整组(10~15 次)。这是保证效果的核心。 +- **碎片化提醒(辅助)**:在完成结构化训练的基础上,每次起身接水、上厕所时顺便做 1~2 下,不求做满一组。本插件的 30 分钟提醒就是为此设计的。 + +> 简单记:**"每天专心练 3 组,每次起身做 2 下。"** + +#### 具体参数 + +- **收缩保持**:5~10 秒(新手可从 3 秒开始) +- **放松时间**:至少 10 秒(是收缩时长的 2 倍) +- **每次提醒做几次**:插件默认 2 次(碎片化提醒,安全不累) +- **专心训练做几组**:10~15 次为一组,每天 2~3 组 +- **每日上限**:100~150 次,不要超额! + +> 插件默认设置偏保守(每次提醒只做 2 下)。如果你想在固定时段做完整训练,可以在设置里把"重复次数"调到 10~15。 + +#### 为什么不能高频猛练? + +肌肉需要休息才能生长。如果一整天"每 30 分钟做一整组",肌肉会过度疲劳、变得僵硬,反而加重问题。**质量远比数量重要。** + +## 四、绝对不能做的事 + +- **不要憋气!** 憋气会让血压飙升,增加心脑血管风险,还会让腹压增大,反而向下挤压盆底。 +- **不要收紧肚子、屁股和大腿!** 只有会阴部在用力,其他地方都放松。做完如果肚子或大腿酸,说明发力错了。 +- **急性期不要做!** 肛门或盆腔有出血、感染、剧痛时,先去看医生。 +- **不要挑战极限频率!** "每 2 分钟做一次"或一天做几千次,属于过度训练,会损伤肌肉。 + +## 五、开始前的注意事项 + +- **有基础病先问医生**:高血压(>180 mmHg)、严重心脏病等,开始前请咨询医生。 +- **不适立刻停**:做的过程中或做完后会阴、腹部、腰部持续不适,立即停止,不改善就去看医生。 +- **坚持 3 个月见效**:这不是一两天的事,把它当成长期习惯。3 个月为一个疗程,然后自评效果。 +- **找不到感觉就躺平练**:先躺在床上、双腿弯曲来做,更容易找到正确发力点。 +- **特定情况找专业指导**:产后康复、严重便秘、术后恢复等,建议去盆底门诊找物理治疗师指导一次。 + +--- + +> **记住这个口诀就够了:** +> +> 每天专心练 3 组,每次起身做 2 下。 +> +> - **专心练**:选早、中、晚固定时间,安静做一组(10~15 次)。 +> - **起身做**:站起来接水、上厕所时,顺便收紧 2 下,不求做满。 + +坚持提肛,守护盆底健康! diff --git a/plugins/tiga/src/env.d.ts b/plugins/tiga/src/env.d.ts new file mode 100644 index 00000000..36ed1334 --- /dev/null +++ b/plugins/tiga/src/env.d.ts @@ -0,0 +1,50 @@ +/// +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent, Record, unknown> + export default component +} + +// 工作时段配置类型 +interface WorkTimeSingle { + start: string + end: string +} + +interface WorkTimeMulti { + multi: Array<{ start: string; end: string }> +} + +interface WorkTimeWeekly { + weekly: Record +} + +interface WorkTimeConfig { + single: WorkTimeSingle + multi: Array<{ start: string; end: string }> + weekly: Record +} + +// Preload services 类型声明(对应 public/preload/services.js) +interface Services { + timerId: ReturnType | null + startTimer: (intervalMinutes: number) => void + stopTimer: () => void + isInWorkTime: (workTimeConfig: WorkTimeConfig, workTimeMode: string) => boolean + sendNotification: (title: string, body: string, onClick?: () => void) => void + addRecord: () => void + getItem: (key: string) => T | null + setItem: (key: string, value: any) => void + removeItem: (key: string) => void + init: () => void +} + +declare global { + interface Window { + services: Services + } +} + +export {} diff --git a/plugins/tiga/src/main.css b/plugins/tiga/src/main.css new file mode 100644 index 00000000..fde1e6a6 --- /dev/null +++ b/plugins/tiga/src/main.css @@ -0,0 +1,27 @@ +html, +body { + margin: 0; + padding: 0; + background-color: var(--el-bg-color); + color: var(--el-text-color-primary); +} + +/* 深色模式滚动条样式 */ +.dark ::-webkit-scrollbar-track-piece { + background-color: var(--el-bg-color-page); +} + +.dark ::-webkit-scrollbar-thumb { + background-color: var(--el-border-color-darker); + border-color: var(--el-bg-color-page); +} + +/* 浅色模式滚动条样式 */ +::-webkit-scrollbar-track-piece { + background-color: var(--el-fill-color-blank); +} + +::-webkit-scrollbar-thumb { + background-color: var(--el-border-color-light); + border-color: var(--el-fill-color-blank); +} diff --git a/plugins/tiga/src/main.ts b/plugins/tiga/src/main.ts new file mode 100644 index 00000000..f21ef788 --- /dev/null +++ b/plugins/tiga/src/main.ts @@ -0,0 +1,31 @@ +import { createApp } from 'vue' +// Element Plus 样式:函数式 API(ElMessage、ElMessageBox)需要手动引入样式 +// 组件标签( 等)由 unplugin-vue-components 自动按需引入 +import 'element-plus/theme-chalk/el-message.css' +import 'element-plus/theme-chalk/el-message-box.css' +import 'element-plus/theme-chalk/dark/css-vars.css' +import './main.css' +import App from './App.vue' + +const app = createApp(App) + +// 检测深色模式并应用 +const initDarkMode = () => { + const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches + if (isDark) { + document.documentElement.classList.add('dark') + } +} + +initDarkMode() + +// 监听主题变化 +window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + if (e.matches) { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } +}) + +app.mount('#app') \ No newline at end of file diff --git a/plugins/tiga/src/types.ts b/plugins/tiga/src/types.ts new file mode 100644 index 00000000..4702f49a --- /dev/null +++ b/plugins/tiga/src/types.ts @@ -0,0 +1,186 @@ +// src/types.ts + +// 配置数据 +export interface Config { + // 提醒设置 + interval: number // 提醒间隔(分钟),默认 30 + workTimeMode: 'single' | 'multi' | 'weekly' // 工作时段模式 + workTime: WorkTimeConfig + + // 提醒与计数 + reminderMode: 'notify' | 'notify+popup' // 仅通知 / 通知+弹窗 + countMode: 'auto' | 'manual' // 自动计数 / 手动计数 + + // 运动参数 + exercise: { + contractSeconds: number // 收缩时长(秒) + relaxSeconds: number // 放松时长(秒) + repeatCount: number // 重复次数 + } + + // 文案风格 + style: 'random' | 'normal' | 'funny' + normalTexts: NotificationText[] // 正经文案列表(包含默认和自定义) + funnyTexts: NotificationText[] // 搞怪文案列表(包含默认和自定义) + + // 运行状态 + enabled: boolean // 开关启停 +} + +// 工作时段配置 +export interface WorkTimeConfig { + single: { start: string; end: string } + multi: Array<{ start: string; end: string }> + weekly: Record +} + +// 统计数据 +export interface StatsData { + records: Array<{ + date: string // YYYY-MM-DD + count: number // 当日完成次数 + times: string[] // 每次完成的时间点 HH:MM + }> +} + +// 通知文案 +export interface NotificationText { + title: string + body: string +} + +// 正经文案 +export const NORMAL_TEXTS: NotificationText[] = [ + { title: '提肛提醒', body: '久坐伤身,该做提肛运动了,关爱您的盆底健康。' }, + { title: '健康时刻', body: '收缩盆底肌,保持5秒,放松5秒,关爱久坐族健康。' }, + { title: '运动提醒', body: '定时提肛运动,预防痔疮,改善盆底功能。' } +] + +// 搞怪文案池 +export const FUNNY_TEXTS: NotificationText[] = [ + { title: '盆底肌呼叫中心', body: '您的盆底肌已欠费停机,请立即充值(提肛)恢复服务!' }, + { title: '提肛小助手', body: '别坐了别坐了,屁股要废了!起来动一动,提肛保健康!' }, + { title: '健康警报', body: '警告:您的臀部已连续静止太久,建议立即启动提肛程序!' }, + { title: '提肛时间到', body: '收缩、放松,让盆底肌跳起健康的舞蹈!' }, + { title: '盆底健康守护', body: '提肛一分钟,健康一整天!现在就开始吧~' }, + { title: '久坐预警', body: '您的屁股正在酝酿抗议,请立即安抚(提肛)!' }, + { title: '提肛大使', body: '来自未来的你发来消息:感谢现在坚持提肛的我!' }, + { title: '健康投递', body: '叮咚!您的盆底健康快递已送达,请签收(提肛)!' }, + { title: '提肛站', body: '下一站:提肛站。请各位乘客做好准备,收缩放松!' }, + { title: '盆底健身房', body: '您有一张免费的盆底健身券,有效期:现在!' } +] + +// 默认配置 +export const DEFAULT_CONFIG: Config = { + interval: 30, + workTimeMode: 'single', + workTime: { + single: { start: '09:00', end: '18:00' }, + multi: [{ start: '09:00', end: '12:00' }, { start: '14:00', end: '18:00' }], + weekly: { + 0: null, // 周日 + 1: { start: '09:00', end: '18:00' }, + 2: { start: '09:00', end: '18:00' }, + 3: { start: '09:00', end: '18:00' }, + 4: { start: '09:00', end: '18:00' }, + 5: { start: '09:00', end: '18:00' }, + 6: null, // 周六 + } + }, + reminderMode: 'notify+popup', + countMode: 'manual', + exercise: { + contractSeconds: 5, + relaxSeconds: 10, + repeatCount: 2 + }, + style: 'random', + normalTexts: [...NORMAL_TEXTS], + funnyTexts: [...FUNNY_TEXTS], + enabled: true +} + +// 存储键名 +export const STORAGE_KEYS = { + config: 'tiga_config', + stats: 'tiga_stats' +} + +// 获取随机文案 +export function getRandomText(style: Config['style'], normalTexts?: NotificationText[], funnyTexts?: NotificationText[]): NotificationText { + const normalList = normalTexts || NORMAL_TEXTS + const funnyList = funnyTexts || FUNNY_TEXTS + + if (style === 'normal') { + const index = Math.floor(Math.random() * normalList.length) + return normalList[index] + } + + if (style === 'funny') { + const index = Math.floor(Math.random() * funnyList.length) + return funnyList[index] + } + + // random: 从全部文案中随机 + const allTexts = [...normalList, ...funnyList] + const index = Math.floor(Math.random() * allTexts.length) + return allTexts[index] +} + +// 计算连续打卡天数(真正的连续:中间任何一天没打卡即中断) +// 易用性优化:若今天尚未打卡(例如上午还未开始), +// 但昨天有打卡,连续天数应依然有效,从昨天起向前计算。 +// 只有今天和昨天都没打卡时,连续才算中断。 +export function calculateStreak(records: StatsData['records']): number { + if (!records || !records.length) return 0 + + const today = getTodayDate() + + // 用 Map 做 O(1) 查找,替代之前的 Array.find O(n) + const recordMap = new Map(records.map(r => [r.date, r])) + + // 确定起始日期:若今天尚未打卡,则从昨天开始向前计算 + const todayRecord = recordMap.get(today) + const startFromToday = todayRecord && todayRecord.count > 0 + + let streak = 0 + const checkDate = parseLocalDate(today) + if (!startFromToday) { + // 今天未打卡:从昨天开始 + checkDate.setDate(checkDate.getDate() - 1) + } + + for (let i = 0; i < 365; i++) { + const dateStr = formatLocalDate(checkDate) + const record = recordMap.get(dateStr) + + if (!record || record.count <= 0) { + // 没有记录或 count=0:连续中断 + break + } + + streak++ + checkDate.setDate(checkDate.getDate() - 1) + } + + return streak +} + +// 将 YYYY-MM-DD 解析为本地时区的 Date(避免 new Date('YYYY-MM-DD') 按 UTC 0 时解析) +function parseLocalDate(dateStr: string): Date { + const [y, m, d] = dateStr.split('-').map(Number) + return new Date(y, m - 1, d) +} + +// 将 Date 格式化为本地时区的 YYYY-MM-DD 字符串 +function formatLocalDate(d: Date): string { + const y = d.getFullYear() + const m = String(d.getMonth() + 1).padStart(2, '0') + const day = String(d.getDate()).padStart(2, '0') + return `${y}-${m}-${day}` +} + +// 获取今日日期字符串(本地时区,避免 toISOString 在 UTC+8 凌晨 0-8 点把日期算成昨天) +export function getTodayDate(): string { + return formatLocalDate(new Date()) +} \ No newline at end of file diff --git a/plugins/tiga/tsconfig.json b/plugins/tiga/tsconfig.json new file mode 100644 index 00000000..11907535 --- /dev/null +++ b/plugins/tiga/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "strict": false, + "noImplicitAny": false, + "types": ["@ztools-center/ztools-api-types"] + }, + "include": ["src", "auto-imports.d.ts", "components.d.ts"] +} diff --git a/plugins/tiga/vite.config.js b/plugins/tiga/vite.config.js new file mode 100644 index 00000000..c8444643 --- /dev/null +++ b/plugins/tiga/vite.config.js @@ -0,0 +1,21 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + vue(), + // 自动导入 Element Plus 的 API(如 ElMessage、ElMessageBox) + AutoImport({ + resolvers: [ElementPlusResolver()], + }), + // 自动按需注册 Element Plus 组件(如 ) + Components({ + resolvers: [ElementPlusResolver()], + }), + ], + base: './' +})