diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..65a998d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +thirdparty/screeningzoom/**/*.cpp linguist-vendored=false +thirdparty/screeningzoom/**/*.h linguist-vendored=false +thirdparty/screeningzoom/**/*.hpp linguist-vendored=false + +# Wails auto-generated bindings +frontend/wailsjs/**/*.js linguist-generated=true diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..733dd58 --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,182 @@ +name: Build Release + +on: + push: + branches: + - master + workflow_dispatch: + +permissions: + contents: write + +jobs: + build-windows: + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install frontend dependencies + working-directory: frontend + run: npm ci + + - name: Install Wails CLI + shell: pwsh + run: | + go install github.com/wailsapp/wails/v2/cmd/wails@v2.10.2 + "$(go env GOPATH)\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Read version + id: version + shell: pwsh + run: | + $version = (Get-Content version.json -Raw | ConvertFrom-Json).appVersion + if ([string]::IsNullOrWhiteSpace($version)) { + throw "version.json appVersion is empty" + } + "app_version=$($version.Trim())" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append + + - name: Download runtime archive + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $appVersion = "${{ steps.version.outputs.app_version }}" + $repo = "${{ github.repository }}" + $runtimeDir = Join-Path $PWD "resources/runtime" + $runtimeArchive = Join-Path $runtimeDir "runtime.7z" + New-Item -ItemType Directory -Path $runtimeDir -Force | Out-Null + $downloaded = $false + + $candidates = @( + "https://github.com/$repo/releases/download/v$appVersion/runtime.7z" + ) + + $apiHeaders = @{ + Authorization = "Bearer $env:GITHUB_TOKEN" + "User-Agent" = "plotkitycat-actions" + } + + try { + $latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/$repo/releases/latest" -Headers $apiHeaders + $latestRuntime = $latestRelease.assets | Where-Object { $_.name -eq "runtime.7z" } | Select-Object -First 1 + if ($null -ne $latestRuntime -and -not [string]::IsNullOrWhiteSpace($latestRuntime.browser_download_url)) { + $candidates += $latestRuntime.browser_download_url + } + + $latestZip = $latestRelease.assets | Where-Object { $_.name -like "PlotKityCat-v*.zip" } | Select-Object -First 1 + if ($null -ne $latestZip -and -not [string]::IsNullOrWhiteSpace($latestZip.browser_download_url)) { + $zipPath = Join-Path $env:RUNNER_TEMP "latest-release.zip" + $extractDir = Join-Path $env:RUNNER_TEMP "latest-release" + Write-Host "Downloading fallback release zip from $($latestZip.browser_download_url)" + Invoke-WebRequest -Uri $latestZip.browser_download_url -OutFile $zipPath + if (Test-Path -LiteralPath $extractDir) { + Remove-Item -LiteralPath $extractDir -Recurse -Force + } + Expand-Archive -LiteralPath $zipPath -DestinationPath $extractDir -Force + $embeddedRuntime = Get-ChildItem -Path $extractDir -Recurse -Filter runtime.7z | Select-Object -First 1 + if ($null -ne $embeddedRuntime) { + Copy-Item -LiteralPath $embeddedRuntime.FullName -Destination $runtimeArchive -Force + $downloaded = $true + } + } + } catch { + Write-Host "Latest release lookup failed: $($_.Exception.Message)" + } + + if (-not $downloaded) { + foreach ($url in $candidates | Select-Object -Unique) { + try { + Write-Host "Downloading runtime from $url" + Invoke-WebRequest -Uri $url -OutFile $runtimeArchive + $downloaded = $true + break + } catch { + Write-Host "Runtime download failed from $url : $($_.Exception.Message)" + } + } + } + + if (-not $downloaded) { + throw "Unable to download runtime.7z from release assets." + } + + - name: Bootstrap vcpkg + shell: pwsh + run: | + $vcpkgRoot = Join-Path $env:RUNNER_TEMP "vcpkg" + git clone https://github.com/microsoft/vcpkg.git $vcpkgRoot + & (Join-Path $vcpkgRoot "bootstrap-vcpkg.bat") -disableMetrics + $vcpkgExe = Join-Path $vcpkgRoot "vcpkg.exe" + & $vcpkgExe install nlohmann-json:x64-windows-static + "VCPKG_ROOT=$vcpkgRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Build screening zoom helper + shell: pwsh + run: | + $toolchain = Join-Path $env:VCPKG_ROOT "scripts/buildsystems/vcpkg.cmake" + cmake -S thirdparty/screeningzoom ` + -B thirdparty/screeningzoom/build ` + -DCMAKE_BUILD_TYPE=Release ` + -DCMAKE_TOOLCHAIN_FILE="$toolchain" ` + -DVCPKG_TARGET_TRIPLET=x64-windows-static + cmake --build thirdparty/screeningzoom/build --config Release + + - name: Build versioned app + shell: pwsh + run: .\tools\build-versioned-app.ps1 -Version "${{ steps.version.outputs.app_version }}" + + - name: Prepare update artifacts + shell: pwsh + run: .\tools\prepare-update-release.ps1 -Version "${{ steps.version.outputs.app_version }}" + + - name: Package full release + shell: pwsh + run: .\tools\package-release.ps1 -Version "${{ steps.version.outputs.app_version }}" + + - name: Upload app binary + uses: actions/upload-artifact@v4 + with: + name: PlotKityCat-build-${{ steps.version.outputs.app_version }} + path: build/bin/PlotKityCat.exe + if-no-files-found: error + + - name: Upload update artifacts + uses: actions/upload-artifact@v4 + with: + name: PlotKityCat-update-${{ steps.version.outputs.app_version }} + path: build/update/${{ steps.version.outputs.app_version }} + if-no-files-found: error + + - name: Upload full release package + uses: actions/upload-artifact@v4 + with: + name: PlotKityCat-release-${{ steps.version.outputs.app_version }} + path: build/release/PlotKityCat-v${{ steps.version.outputs.app_version }}.zip + if-no-files-found: error + + - name: Publish GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ steps.version.outputs.app_version }} + name: v${{ steps.version.outputs.app_version }} + generate_release_notes: true + fail_on_unmatched_files: true + files: | + build/release/PlotKityCat-v${{ steps.version.outputs.app_version }}.zip + build/update/${{ steps.version.outputs.app_version }}/PlotKityCat-${{ steps.version.outputs.app_version }}-windows-amd64.exe + build/update/${{ steps.version.outputs.app_version }}/manifest.json + resources/runtime/runtime.7z diff --git a/.gitignore b/.gitignore index 1757a1b..6681547 100644 --- a/.gitignore +++ b/.gitignore @@ -7,12 +7,12 @@ build/release/ build/update/ /runtime/ /runtime.tmp/ -/Scripts/ *.exe *.dll *.zip !/resources/runtime/ !/resources/runtime/.gitkeep +/resources/runtime/runtime.7z .runtime-staging/ .runtime-staging.7z .tools_py/ @@ -25,9 +25,11 @@ nul .DS_Store Thumbs.db /.gocache/ +/.cache/ /.runtime-pack/ __pycache__/ *.py[cod] +thirdparty/screeningzoom/build/ # Legacy root docs Tec.md @@ -36,3 +38,6 @@ tec.md prd.md designcard.md designcard-tec.md + +# Local experiment sandbox (prompt eval / card gen) +.tmp/ diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b384e73..68267ae 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,9 +1,11 @@ # PlotKityCat Development +本地开发启动、runtime 与构建发布、Git 仓库约定。 + ## 环境 - Windows -- Go 1.21+ +- Go 1.22+ - Node.js 18+ - Wails v2 @@ -23,27 +25,28 @@ wails dev ## 版本 -应用版本唯一来源: +应用版本唯一来源为 `version.json` 中的 `appVersion`。构建与发布脚本默认都读取这里的值。 -- `version.json` +## 本地状态 -相关脚本都会默认读取这里的 `appVersion`。 +- `config/app-state.json` 保存版本化的应用状态,新手引导使用 `onboarding` 字段。 +- 新发布包不携带 `config/`;应用创建 Store 时先检查既有 `config/`,早于本次启动写入状态。 +- 历史用户写入 `suppressed / existing-user`,不自动显示教程。 +- 教程模板缺失时写入 `suppressed / template-missing`,后续启动不再重复尝试。 +- 新用户依次记录 `unseen`、`started`、`dismissed`、`completed`;后三者均停止自动启动。 +- `Scripts/新手引导/` 保存教程内容,状态与内容采用独立生命周期。 ## Runtime -项目运行依赖便携 Python runtime: +项目运行依赖便携 Python runtime。约定如下: -- runtime 占位目录:`resources/runtime/` -- 发布输入:本地放置的 `resources/runtime/runtime.zip` +- 占位目录:`resources/runtime/` +- 本地发布输入:`resources/runtime/runtime.7z` - 本地展开目录:`runtime/` - 临时展开目录:`runtime.tmp/` - 元数据文件:`runtime.version.json` - -约定: - -- `resources/runtime/runtime.zip` 默认不提交到 Git -- 该文件应通过 GitHub Release asset 或其他制品存储分发 -- 仓库仅跟踪 runtime 脚本、元数据和第三方补丁源码 +- runtime 通过 GitHub Release asset 分发 +- 仓库仅跟踪 runtime 脚本和元数据 准备 runtime 压缩包: @@ -51,7 +54,7 @@ wails dev .\tools\prepare-runtime.ps1 -SourceRuntimeDir <你的 runtime 目录> ``` -当前默认应保留的核心库: +默认核心库: - Python 标准库 - numpy @@ -59,11 +62,11 @@ wails dev - scipy - PyQt5 -`runtime.version.json` 应同步填写实际 Python 与核心库版本,不要长期保留 `pending`。 +`runtime.version.json` 必须填写真实版本,`pending` 仅为占位值。 从零重建 runtime 的完整说明见 [RUNTIME_BUILD.md](D:/projects/plotkitycat/RUNTIME_BUILD.md)。 -## 打包入口 +## 构建与打包 构建 exe: @@ -71,13 +74,15 @@ wails dev .\tools\build-versioned-app.ps1 ``` -生成发布 zip: +构建成功会同时生成 `build/bin/build-metadata.json`。打包与在线更新产物脚本会强制核对其中的版本,禁止复用旧 EXE。 + +生成完整发布包: ```powershell .\tools\package-release.ps1 ``` -生成自动更新发布物: +生成在线更新产物: ```powershell .\tools\prepare-update-release.ps1 @@ -86,15 +91,17 @@ wails dev 如果要覆盖默认版本: ```powershell -.\tools\build-versioned-app.ps1 -Version 0.0.2.1 -.\tools\package-release.ps1 -Version 0.0.2.1 -.\tools\prepare-update-release.ps1 -Version 0.0.2.1 +.\tools\build-versioned-app.ps1 -Version 0.0.3.5 +.\tools\package-release.ps1 -Version 0.0.3.5 +.\tools\prepare-update-release.ps1 -Version 0.0.3.5 ``` ## 发布前检查 - 确认 `version.json` 版本正确 -- 确认 `resources/runtime/runtime.zip` 存在 +- 确认 `resources/runtime/runtime.7z` 存在 +- 确认 `zoomit.exe` 已经准备好,优先放在 `resources/screeningzoom/zoomit.exe` +- 如果还没放到 `resources/screeningzoom/zoomit.exe`,至少确认存在开发构建产物 `thirdparty/screeningzoom/build/Release/zoomit.exe` - 确认 `runtime.version.json` 已填写真实版本 - 确认 `Scripts/` 中示例内容就是准备随包分发的内容 - 确认 `config/` 没有本机账号、缓存、更新状态等脏数据 @@ -107,21 +114,25 @@ wails dev - `frontend/` - `tools/` - `build/windows/` -- `resources/` 下需要随项目维护的静态资源 +- `resources/` 下需要随项目维护的静态资源与占位文件 - `resources/runtime/.gitkeep` +- `resources/screeningzoom/.gitkeep` - `README.md` - `DEVELOPMENT.md` +- `UPDATE_RELEASE.md` - `RUNTIME_BUILD.md` - `version.json` - `runtime.version.json` +- `Scripts/` 下随产品发布的预制工作区 不应跟踪: - `config/` - `runtime/` - `runtime.tmp/` -- `Scripts/` - `build/bin/` - `build/release/` - `build/update/` - `packaging/` + +发布服务器更新步骤见 [UPDATE_RELEASE.md](D:/projects/plotkitycat/UPDATE_RELEASE.md)。 diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..46d493a --- /dev/null +++ b/README.en.md @@ -0,0 +1,295 @@ +[简体中文](./README.md) | English + +
+ + + +
+ + + PlotKityCat Logo + + +
+ Cat paws +
+ +

+ + PlotKityCat — Plot it, Note it, Teach it + +

+ +

+ ∫   ∑   ∂   π   ∞ +

+ +

+ An AI-native visualization tool for mathematics teachers +

+ +

+ Generate Matplotlib visuals from natural language, and keep formulas, code, figures, and teaching notes in one workspace. +

+ +
◆   ◆   ◆
+ +
+ +

+ Release v0.0.5.0 + Windows AMD64 + AI Native + GPL-3.0 +

+ +

+ Wails v2 + Vue 3 + Python 3.13+ +

+ +

+ Download +  •  + Video +  •  + Features +  •  + Quick Start +  •  + Documentation +

+ +
+ + + +
+ +## What is PlotKityCat? + +PlotKityCat provides a natural-language-driven visualization workflow for middle-school and high-school mathematics. A teacher can describe a mathematical concept, diagram, or interactive scene; AI generates Matplotlib code that can then be edited, documented, refined, and reused inside the application. + +The application ships with an independent WinPython Runtime, making it suitable for USB drives, classroom computers, and portable working directories. Python code, Markdown, LaTeX formulas, and visualization output are organized around the same scene, reducing the need to jump between separate tools. + +## Video + +https://github.com/user-attachments/assets/ff90ac18-5fcf-42d4-85f3-be09b3309ed4 + +## Features + +### AI visualization + +- Generate Matplotlib plotting code from natural-language instructions. +- Generate, optimize, and repair code. +- Use prompts designed specifically for mathematics teaching and visual quality. +- Follow generation progress through the in-app animated cat. + +### Flexible AI integration + +- Connect to custom OpenAI-compatible APIs. +- Configure the Base URL, API Key, and Model. +- Keep the AI Provider decoupled from the generation workflow, making compatible services easier to integrate. + +### Teaching workspaces + +- Manage Python code, Markdown notes, and visualization output in one scene. +- Render LaTeX mathematics inside Markdown notes. +- Create, switch, rename, and organize multiple workspaces and scenes. +- Switch between note-only, code-only, and combined Layout Modes. +- Import and export `.pkcw` workspace packages. + +### Design Card + +- Generate visual design cards from selected note content. +- Refine a generated card with further instructions. +- Keep design versions for comparison and retrieval. + +### First-run guidance + +- Guide new users through the main workflow on first launch. +- Use a bundled onboarding workspace as the tutorial environment. +- Cover the main regions, essential interactions, and the basic generation flow. + +### Portable runtime and updates + +- Ship a complete package with WinPython Runtime, Matplotlib, NumPy, SciPy, and PyQt5. +- Run from a USB drive or an extracted portable directory. +- Check for updates, download them, and restart to install from Settings. +- Verify update files with both Size and SHA256 checks. +- Integrate ScreeningZoom for classroom presentation and focused explanation. + +## Downloads for Windows + +### Full package + +Recommended for first-time users. It includes the application, Runtime, bundled workspaces, and presentation component. + +**[Download PlotKityCat v0.0.5.0 full package](https://1820614751.cdn.123clouddisk.com/1820614751/44761845?v=332b4a2982f63f1e0a65285928ed58f6)** + +File: + +```text +PlotKityCat-v0.0.5.0.zip +``` + +### Application-only package + +For users who already have a complete PlotKityCat environment and only need to replace the application executable. + +**[Download PlotKityCat v0.0.5.0 application-only package](https://1820614751.cdn.123clouddisk.com/1820614751/44686802?v=6520da4b1fffa08ad1800deb574343ce)** + +File: + +```text +PlotKityCat-0.0.5.0-windows-amd64.zip +``` + +Existing users can check for later updates directly from Settings. + +> The current release target is Windows AMD64. macOS and Linux packages are not available yet. + +## Quick Start + +1. Download and extract the full package. +2. Run `PlotKityCat.exe`. +3. Follow the onboarding tour to learn the workspace. +4. Describe the mathematical visual you want to generate. +5. Continue refining the result in the code, note, and visualization regions. + +```text + /\_/\ + ( •ω•) "Draw a quadratic function with a parameter slider, + / >📐 and mark its vertex and axis of symmetry." + + ↓ AI + + y + │ ╭─╮ + │ ╭─╯ ╰─╮ + └────────────── x +``` + +In AI Settings, switch to Custom Mode and enter a service endpoint compatible with the OpenAI Chat Completions protocol. + +## What is inside a workspace? + +```text +Workspace +├─ Scene A +│ ├─ main.py # Matplotlib code +│ └─ note.md # Markdown / LaTeX teaching notes +├─ Scene B +│ ├─ main.py +│ └─ note.md +└─ .plotkitycat-scenes.json +``` + +Multiple workspaces can be packaged into a `.pkcw` file for backup, exchange, and migration. + +## Why I built it + +PlotKityCat began with a frustrating experience while I was building GGBPuppy. + +One day, I was digging through the GeoGebra Web API. AI kept producing GGB code that could not run, and I gradually realized that the model was only part of the problem: the available GGB API was incomplete. I wrote to their team as a developer to confirm what I had found. The response I received was a request for payment. + +> That night, I closed the window with its dull lines and colors. Later, I dreamed of Jobs. + +The experience made me rethink mathematical teaching software. Why should teachers be constrained by a closed interface? Why should AI keep guessing against an incomplete API? Why should the visual language of a mathematical figure always surrender to the habits of an old tool? + +So I turned to Matplotlib. Its Python ecosystem is open, mature, and inspectable, and it gives AI a much clearer space in which to express mathematical intent. That is where PlotKityCat began: teachers describe the teaching idea, code provides precision, and the image carries the explanation. + +Three principles guide the project: + +1. **Openness**: teaching tools should be inspectable, extensible, and shareable. +2. **Visual quality**: mathematical figures should be clear, restrained, and expressive. +3. **AI-native workflow**: teachers describe their intent, and the tool turns it into code and images. + +```text + /\_/\ + ( -.- ) Mathematics keeps it rigorous. + / >☕ The cat keeps you company while it renders. +``` + +## Technology + +| Layer | Technology | +| --- | --- | +| Desktop Shell | Wails v2 | +| Frontend | Vue 3, TypeScript, Vite, CodeMirror | +| Backend | Go | +| Visualization | Python 3.13, Matplotlib, NumPy, SciPy | +| Interactive Runtime | WinPython, PyQt5 | +| Notes | Markdown, MathJax / LaTeX | +| AI | OpenAI-compatible API | + +## Local Development + +### Requirements + +- Windows +- Go 1.22+; the Toolchain declared in `go.mod` is recommended +- Node.js 20+ +- Wails v2 + +### Run + +```powershell +cd frontend +npm install +cd .. +wails dev +``` + +### Runtime + +Prepare or rebuild the portable Runtime: + +```powershell +.\tools\prepare-runtime.ps1 -SourceRuntimeDir +``` + +See [RUNTIME_BUILD.md](./RUNTIME_BUILD.md) for details. + +### Release build + +```powershell +.\tools\build-versioned-app.ps1 +.\tools\prepare-update-release.ps1 +.\tools\package-release.ps1 +``` + +For versioning, the update server, and the complete release flow, see [UPDATE_RELEASE.md](./UPDATE_RELEASE.md). + +## Documentation + +- [Development and build](./DEVELOPMENT.md) +- [Runtime distribution and rebuild](./RUNTIME_BUILD.md) +- [Online updates and releases](./UPDATE_RELEASE.md) + +## Acknowledgements + +- [Matplotlib](https://matplotlib.org/): the core visualization engine. +- [ManimCat](https://github.com/Wing900/ManimCat): an early technical foundation and source of inspiration. +- [Wails](https://wails.io/): the desktop application framework joining Go and web technologies. +- [ZoomIt / PowerToys](https://github.com/microsoft/PowerToys/tree/main/src/modules/ZoomIt): the foundation of the classroom presentation helper. + +## Vision + +I hope more mathematical visualization resources can be developed, opened, and shared, making teaching clearer and high-quality educational resources easier to circulate. + +I also hope PlotKityCat can one day become a mathematical visualization community maintained by teachers, students, and creators together. + +```text + /\_/\ + ( ^.^ ) + / づ づ Let us draw mathematics clearly. +``` + +## License + +[GNU General Public License v3.0](./LICENSE) diff --git a/README.md b/README.md index 2332bed..991f094 100644 --- a/README.md +++ b/README.md @@ -1,149 +1,295 @@ -

- - PlotKityCat Logo - +简体中文 | [English](./README.en.md) + +

+ + + +
+ + + PlotKityCat Logo + + +
+ Cat paws +
+ +

+ + PlotKityCat — Plot it, Note it, Teach it + +

+ +

+ ∫   ∑   ∂   π   ∞

-

PlotKityCat

+

+ 面向数学教师的 AI-native 可视化教学工具 +

-

- 一款专为数学老师打造的 AI-native 可视化教学工具。 +

+ 用自然语言生成 Matplotlib 可视化,把公式、代码、图像与教学笔记放进同一个工作区。

-

- Wails - Vue - Go - Python - License +

◆   ◆   ◆
+ +
+ +

+ Release v0.0.5.0 + Windows AMD64 + AI Native + GPL-3.0 +

+ +

+ Wails v2 + Vue 3 + Python 3.13+ +

+ +

+ 下载 +  •  + 视频 +  •  + 功能 +  •  + 快速开始 +  •  + 开发文档

-## 简介 +
+ + + +
+ +## PlotKityCat 是什么 -请允许我为你介绍这只可爱的,小猫! -PlotKityCat 是一个开源的数学可视化工具,支持运行 Matplotlib 代码并生成交互式图形。集成 AI 功能,支持通过自然语言提示词生成绘图代码。软件采用便携式设计,支持优盘即插即用,方便在教室等不同环境下快速部署与演示。 +PlotKityCat 为初高中数学教学提供自然语言驱动的可视化工作流。教师可以描述一个数学概念、图像或交互场景,由 AI 生成 Matplotlib 代码,并在应用内继续编辑代码、组织笔记、调整图像与沉淀教学素材。 + +应用携带独立 WinPython Runtime,适合放入 U 盘、教室电脑或个人工作目录运行。代码、Markdown、LaTeX 公式和可视化结果围绕同一场景组织,减少在多个工具之间切换的成本。 ## 视频介绍 -https://github.com/user-attachments/assets/df8167a7-d1e9-4f6a-a42d-de15596a4456 +https://github.com/user-attachments/assets/ff90ac18-5fcf-42d4-85f3-be09b3309ed4 -## 开发初衷 +## 功能 -PlotKityCat 源于对 GGBPuppy 开发过程中 GGB Web API 封闭性的反思。我们转向 Matplotlib,为初高中数学可视化提供 AI-native 方案。 +### AI 可视化 -> 那天,我在研究GGB的webapi,AI总是写下错误的GGB代码,让我的另外一个项目GGBpuppy很受挫折。我突然发现一个GGB的api接口不完整,于是以开发者的口吻发了一封信给他们团队,结果收到了他们希望我付钱的要求......好吧,那天晚上关掉它肮脏线条和色彩的窗口,我梦见了Jobs..... +- 使用自然语言生成 Matplotlib 绘图代码。 +- 支持代码生成、优化与修复。 +- Prompt 已针对数学教学和可视化质量重构。 +- AI 工作期间使用小猫动画呈现生成状态。 -1. **开源**:好的工具应该像太阳一样,太阳是闭源的吗? -2. **美**:拒绝 GGB 沉闷的色彩与线条。 -3. **AI 原生**:通过 AI 直接生成可视化代码,无需老师学习编程。 +### 灵活的 AI 接口 -PlotKityCat 支持优盘便携,旨在让老师将其带入教室、讲台及学生手中。 +- 支持自定义 OpenAI-compatible API。 +- 可以配置 Base URL、API Key 与 Model。 +- AI Provider 与生成工作流解耦,方便接入兼容服务。 -## 功能特性 +### 教学工作区 -- **AI 绘图**:通过自然语言描述数学概念,由 AI 生成 Matplotlib 绘图代码。支持设计和生成代码双流程。 +- 场景内同时管理 Python 代码、Markdown 笔记和可视化结果。 +- Markdown 支持 LaTeX 数学公式。 +- 支持多个工作区和场景的创建、切换、重命名与组织。 +- 支持仅笔记、仅代码和组合视图等 Layout Mode。 +- 支持 `.pkcw` 工作区包导入与导出。 -- **笔记系统**:集成 Markdown 与 LaTeX 公式渲染,绑定代码,看到可视化的结果,更看到可视化的设计。 -- **便携运行**:内置 Python 运行时,支持U盘即插即用,让小猫真的可以在课堂中一展身手。 -- **.pck导入导出** : 支持导出和导入场景包,希望用户之间可以交流自己的可视化成果。 +### Design Card -## 技术栈 +- 从笔记内容生成可视化设计卡片。 +- 支持继续优化生成结果。 +- 保留设计版本,方便比较与回退到所需方案。 + +### 首次上手 + +- 新用户首次启动时进入新手教程。 +- 教程基于随应用发布的预制工作区。 +- 引导覆盖主要区域、常用交互和基础生成流程。 + +### 便携运行与更新 + +- 完整包包含 WinPython Runtime、Matplotlib、NumPy、SciPy 和 PyQt5。 +- 支持 U 盘与免安装目录运行。 +- 设置页支持检查更新、下载更新与重启安装。 +- 更新文件执行 Size 与 SHA256 完整性校验。 +- 集成 ScreeningZoom,辅助课堂放映与局部讲解。 + +## 下载 Windows 版 + +### 全新安装 + +适合第一次使用,包含应用、Runtime、预制工作区和放映组件。 + +**[下载 PlotKityCat v0.0.5.0 完整包](https://1820614751.cdn.123clouddisk.com/1820614751/44761845?v=332b4a2982f63f1e0a65285928ed58f6)** + +文件名: + +```text +PlotKityCat-v0.0.5.0.zip +``` + +### 已有完整环境 + +适合已经安装过完整包、只需要替换应用文件的用户。 + +**[下载 PlotKityCat v0.0.5.0 增量包](https://1820614751.cdn.123clouddisk.com/1820614751/44686802?v=6520da4b1fffa08ad1800deb574343ce)** -- **前端**: Vue 3, TypeScript, Vite -- **后端**: Go, Wails Framework -- **运行时**: WinPython (Matplotlib, NumPy, PyQt5) -- **AI 接口**: OpenAI API / 自定义兼容接口 +文件名: + +```text +PlotKityCat-0.0.5.0-windows-amd64.zip +``` + +已安装用户后续可以在设置中直接检查更新。 + +> 当前发布目标为 Windows AMD64。macOS 与 Linux 安装包暂未提供。 ## 快速开始 -1. **下载**:获取便携版压缩包。 -2. **配置**:设置 AI 服务商 API Key。 -3. **运行**:新建场景,笔记区输入描述,右键点击可视化或可视化设计运行。 +1. 下载并解压完整包。 +2. 运行 `PlotKityCat.exe`。 +3. 跟随新手教程熟悉工作区。 +4. 描述希望生成的数学图像。 +5. 在代码区、笔记区和可视化区域继续调整结果。 -## 开发者指南 +```text + /\_/\ + ( •ω•) “画出带参数滑块的二次函数, + / >📐 同时标出顶点和对称轴。” -### 环境要求 -- **Windows** -- **Go**: 1.21+ -- **Node.js**: 18+ -- **Wails**: v2.x + ↓ AI -### 开发启动 -```powershell -cd frontend -npm install -cd .. -wails dev + y + │ ╭─╮ + │ ╭─╯ ╰─╮ + └────────────── x ``` -### 版本来源 +在 AI 设置中可以切换到 Custom Mode,并填写兼容 OpenAI Chat Completions 协议的服务地址。 -应用版本唯一来源: +## 一个工作区里有什么 -- `version.json` +```text +工作区 +├─ 场景 A +│ ├─ main.py # Matplotlib 代码 +│ └─ note.md # Markdown / LaTeX 教学笔记 +├─ 场景 B +│ ├─ main.py +│ └─ note.md +└─ .plotkitycat-scenes.json +``` -### Runtime +多个工作区可以打包为 `.pkcw` 文件,用于备份、交换与迁移教学素材。 -项目运行依赖便携 Python runtime: +## 开发初衷 -- runtime 占位目录:`resources/runtime/` -- 发布输入:本地放置的 `resources/runtime/runtime.zip` -- 本地展开目录:`runtime/` -- 临时展开目录:`runtime.tmp/` -- 元数据文件:`runtime.version.json` +PlotKityCat 源于我开发 GGBPuppy 时的一次受挫。 -注意: +那天,我一直在研究 GeoGebra Web API。AI 总会写出无法运行的 GGB 代码,我逐渐发现,问题并不只来自模型:GGB 提供的接口并不完整。为了确认这件事,我以开发者的身份给他们的团队写了一封邮件,得到的答复却是需要付费。 -- `resources/runtime/runtime.zip` 默认不提交到 Git -- 这个文件应作为 release asset 或外部制品分发 +> 那天晚上,我关掉了那个有着沉闷线条和色彩的窗口。后来我梦见了 Jobs。 -准备 runtime 压缩包: +这件事让我开始重新思考数学教学软件:教师为什么必须被某个封闭接口限制?AI 为什么只能在残缺的 API 上反复猜测?一张数学图像的美感,为什么总要向旧工具的视觉习惯妥协? -```powershell -.\tools\prepare-runtime.ps1 -SourceRuntimeDir <你的 runtime 目录> +于是我转向 Matplotlib。它拥有开放、成熟且可审查的 Python 生态,也给 AI 留下了足够清晰的表达空间。PlotKityCat 就从这里开始:让教师描述教学意图,让代码负责精确,让图像负责表达。 + +我们希望守住三条原则: + +1. **开放**:教学工具应当可以被审查、扩展和分享。 +2. **美感**:数学图像应当清晰、克制,并具有表达力。 +3. **AI-native**:教师描述教学意图,工具将意图转换为代码与图像。 + +```text + /\_/\ + ( -.- ) 数学负责严谨 + / >☕ 小猫负责陪你等图 ``` -当前默认核心库: +## 技术栈 -- Python 标准库 -- numpy -- matplotlib -- scipy -- PyQt5 +| 层级 | 技术 | +| --- | --- | +| Desktop Shell | Wails v2 | +| Frontend | Vue 3、TypeScript、Vite、CodeMirror | +| Backend | Go | +| Visualization | Python 3.13、Matplotlib、NumPy、SciPy | +| Interactive Runtime | WinPython、PyQt5 | +| Notes | Markdown、MathJax / LaTeX | +| AI | OpenAI-compatible API | -如何从零重建 runtime,见 [RUNTIME_BUILD.md](D:/projects/plotkitycat/RUNTIME_BUILD.md)。 +## 本地开发 -### 打包入口 +### 环境 -构建 exe: +- Windows +- Go 1.22+,推荐使用 `go.mod` 指定的 Toolchain +- Node.js 20+ +- Wails v2 + +### 启动 ```powershell -.\tools\build-versioned-app.ps1 +cd frontend +npm install +cd .. +wails dev ``` -生成发布 zip: +### Runtime + +准备或重建便携 Runtime: ```powershell -.\tools\package-release.ps1 +.\tools\prepare-runtime.ps1 -SourceRuntimeDir <你的 Runtime 目录> ``` -生成自动更新发布物: +详细说明见 [RUNTIME_BUILD.md](./RUNTIME_BUILD.md)。 + +### 构建发布 ```powershell +.\tools\build-versioned-app.ps1 .\tools\prepare-update-release.ps1 +.\tools\package-release.ps1 ``` -更详细的开发与发布说明见 [DEVELOPMENT.md](D:/projects/PlotKityCat/DEVELOPMENT.md)。 +版本、更新服务器和完整发布流程见 [UPDATE_RELEASE.md](./UPDATE_RELEASE.md)。 + +## 文档 + +- [开发与构建](./DEVELOPMENT.md) +- [Runtime 分发与重建](./RUNTIME_BUILD.md) +- [在线更新与发布](./UPDATE_RELEASE.md) ## 致谢 -- [Matplotlib](https://matplotlib.org/): 本项目核心渲染引擎。 -- [ManimCat](https://github.com/Wing900/ManimCat): 提供了开发的基础和灵感。 +- [Matplotlib](https://matplotlib.org/):核心可视化引擎。 +- [ManimCat](https://github.com/Wing900/ManimCat):提供早期开发基础与灵感。 +- [Wails](https://wails.io/):Go 与 Web 技术结合的桌面应用框架。 +- [ZoomIt / PowerToys](https://github.com/microsoft/PowerToys/tree/main/src/modules/ZoomIt):课堂放映辅助能力的实现基础。 +## 愿景 +期待更多数学可视化资源被开发、开放与分享,让教学表达更加清晰,让优质教育资源更容易流动。 -## 期待 +也期待有一天,PlotKityCat 可以成为教师、学生和创作者共同维护的数学可视化社区。 + +```text + /\_/\ + ( ^.^ ) + / づ づ 一起把数学画清楚。 +``` -期待更多的可视化资源可以被开发,开源,开放,打破教育资源长期以来的垄断,让我们的教育越来越清晰,越来越公平! +## License -希望有一天,我的用户足够多,我们可以开一个PlotKityCat交流社区! +[GNU General Public License v3.0](./LICENSE) diff --git a/RUNTIME_BUILD.md b/RUNTIME_BUILD.md index d1985d7..ba8094a 100644 --- a/RUNTIME_BUILD.md +++ b/RUNTIME_BUILD.md @@ -2,53 +2,31 @@ ## 目标 -这份文档描述两件事: +Python runtime 的分发方式与从零重建流程。 +开发环境搭建见 [DEVELOPMENT.md](D:/projects/plotkitycat/DEVELOPMENT.md)。 -- 如何给别人分发现成的 Python runtime -- 如何从零重建 `resources/runtime/runtime.zip` +当前 runtime 仅使用原生 Python 包内容,不再注入 Matplotlib C++ fastpath 补丁。 -本项目的 runtime 是发布制品,不是源码仓库资产。 - -结论先说清楚: - -- `resources/runtime/runtime.zip` 默认不提交到 Git -- 仓库只跟踪 runtime 构建脚本、版本元数据和第三方补丁源码 -- 现成 runtime 应通过 GitHub Release asset、对象存储或内部制品库分发 - -## 仓库里有什么 +## 仓库约定 仓库中与 runtime 相关的内容: - `runtime.version.json` - `tools/prepare-runtime.ps1` - `tools/extract_winpython.py` -- `thirdparty/matplotlib_surface_fastpath/` - `resources/runtime/.gitkeep` 仓库中默认不会跟踪: -- `resources/runtime/runtime.zip` +- `resources/runtime/runtime.7z` - `runtime/` - `runtime.tmp/` -## 给别人交付现成 runtime - -如果只是让接手者继续打包发布,不要求他从零重建 runtime,最简单的交付方式是: - -1. 把本地现成的 `resources/runtime/runtime.zip` 上传为 GitHub Release asset,或放到稳定下载地址 -2. 告诉接手者把该文件放回: +## 分发现成 runtime -```text -resources/runtime/runtime.zip -``` - -3. 接手者即可继续运行: - -```powershell -.\tools\build-versioned-app.ps1 -.\tools\package-release.ps1 -.\tools\prepare-update-release.ps1 -``` +1. 将本地 `resources/runtime/runtime.7z` 上传为 GitHub Release asset +2. 接手者将文件放回 `resources/runtime/runtime.7z` +3. 接手者参照 [UPDATE_RELEASE.md](D:/projects/plotkitycat/UPDATE_RELEASE.md) 执行构建与打包 ## 从零重建 runtime @@ -59,43 +37,33 @@ resources/runtime/runtime.zip - 一个可用的 WinPython runtime 目录,版本需与 `runtime.version.json` 一致 - 如需从 WinPython 安装包提取目录,需要 Python 环境并安装 `py7zr` -当前仓库记录的目标 runtime 元数据见: - -- `runtime.version.json` +当前 runtime 目标元数据见 `runtime.version.json`。 当前版本要求: - distribution: `WinPython` - distributionVersion: `3.13.11.0 slim` - pythonVersion: `3.13.11` +- matplotlib: `3.11.0rc2` ### 2. 获取 WinPython runtime 目录 -你需要先拿到一个已经解压好的 WinPython 目录,目录内应至少包含: +准备一个已解压的 WinPython 目录,目录内至少包含: - `python.exe` - `Lib/` - `DLLs/` - `Lib/site-packages/` -如果你手里只有 WinPython 的 `.exe` 安装包,可用仓库脚本提取其内嵌 7z: +如果仅有 WinPython 的 `.exe` 安装包,可用仓库脚本提取其内嵌 7z: ```powershell python .\tools\extract_winpython.py --exe --archive <临时输出.7z> --dest <解压目标目录> ``` -注意: - -- 这个脚本依赖 `py7zr` -- 仓库不会自动帮你安装 `py7zr` - ### 3. 确认 runtime 版本元数据 -打开: - -- `runtime.version.json` - -确保里面的版本与准备注入的 runtime 实际一致,尤其是: +打开 `runtime.version.json`,确保其中版本和准备打包的 runtime 实际一致,至少核对: - `pythonVersion` - `numpy` @@ -103,25 +71,7 @@ python .\tools\extract_winpython.py --exe --archive < - `scipy` - `PyQt5` -`tools/prepare-runtime.ps1` 会根据 `pythonVersion` 选择 ABI 对应的扩展文件名,例如: - -- `cp313` 对应 `_surface_fastpath.cp313-win_amd64.pyd` - -### 4. 注入 PlotKityCat 的 Matplotlib surface fastpath - -`tools/prepare-runtime.ps1` 会自动把以下内容注入 runtime: - -- `thirdparty/matplotlib_surface_fastpath/src/mpl_surface_fastpath/` -- ABI 匹配的 `_surface_fastpath.cp-win_amd64.pyd` -- `thirdparty/matplotlib_surface_fastpath/vendor/win-amd64/` 下的 3 个 DLL - -这一步不要求接手者自行编译扩展,前提是仓库里已有目标 ABI 的 `.pyd`: - -- 当前仓库内已有 `cp312` 和 `cp313` 版本 - -如果未来升级 Python 小版本但 ABI 发生变化,必须同步补齐匹配的 `.pyd`。 - -### 5. 生成 runtime.zip +### 4. 生成 runtime.7z 在仓库根目录执行: @@ -129,29 +79,62 @@ python .\tools\extract_winpython.py --exe --archive < .\tools\prepare-runtime.ps1 -SourceRuntimeDir <你的WinPython目录> ``` +生成当前推荐的瘦身版 runtime,需显式启用以下裁剪开关: + +```powershell +.\tools\prepare-runtime.ps1 ` + -SourceRuntimeDir <你的WinPython目录> ` + -TrimQtForRelease ` + -TrimUnusedPythonPackagesForRelease ` + -TrimOptionalScientificPackagesForRelease ` + -TrimAIPythonPackagesForRelease ` + -TrimQtOptionalUiForRelease ` + -TrimPythonPackagingToolsForRelease ` + -TrimPythonRuntimeClutterForRelease ` + -TrimSuspiciousPythonPackagesForRelease +``` + +这些开关只作用于 staging 目录 `.runtime-pack/runtime`,不会修改原始 `SourceRuntimeDir`。 + 执行结果: -- 生成 `resources/runtime/runtime.zip` +- 生成 `resources/runtime/runtime.7z` - staging 目录默认使用 `.runtime-pack/` +- 默认使用仓库内 `tools/7zip/extra/x64/7za.exe` 生成 7z 压缩包 + +### 5. 裁剪开关含义 + +- `-TrimQtForRelease` + 删除 Qt 第一、第二梯队,例如 WebEngine、Designer、Multimedia、Location 及边缘平台插件 +- `-TrimUnusedPythonPackagesForRelease` + 删除 Jupyter、IPython、格式化工具、额外绘图库等低风险开发环境包 +- `-TrimOptionalScientificPackagesForRelease` + 删除 `cvxpy/scs/osqp/networkx/xarray/seaborn` 等非主链科学计算包 +- `-TrimAIPythonPackagesForRelease` + 删除 `langchain/google_genai/huggingface_hub/tiktoken` 等 Python AI 生态包 +- `-TrimQtOptionalUiForRelease` + 删除 `qml/`、边缘 Qt 插件,以及除中文外的大部分 Qt 翻译资源 +- `-TrimPythonPackagingToolsForRelease` + 删除 `pip/setuptools/wheel/pkg_resources` +- `-TrimPythonRuntimeClutterForRelease` + 删除 `__pycache__`、`testing/tests/examples/docs` 和 `PyQt5/Qt5/qsci` +- `-TrimSuspiciousPythonPackagesForRelease` + 删除 `ipympl/pydantic_ai/github/cryptography/lxml` ### 6. 人工验收 至少做这几项检查: -1. 解压生成的 `resources/runtime/runtime.zip` -2. 确认存在: - `Lib/site-packages/mpl_surface_fastpath/` -3. 确认存在: - `DLLs/libgomp-1.dll` - `DLLs/libstdc++-6.dll` - `DLLs/libgcc_s_seh-1.dll` -4. 确认扩展 ABI 与 `runtime.version.json` 的 `pythonVersion` 一致 +1. 使用 7-Zip 解压生成的 `resources/runtime/runtime.7z` +2. 确认存在 `Lib/site-packages/matplotlib/` +3. 确认 `runtime.version.json` 中的核心版本与实际包内容一致 +4. 确认 runtime 内不包含额外的本地开发补丁或外部编译依赖 ## 发布建议 -`runtime.zip` 不要放进普通 Git 提交历史。 +`runtime.7z` 不纳入 Git 提交历史。 -建议的分发方式: +分发方式: - GitHub Release assets - 对象存储 @@ -159,23 +142,12 @@ python .\tools\extract_winpython.py --exe --archive < 推荐约定: -- 每次 runtime 发生变化,都发布一个单独的 runtime asset +- 每次 runtime 发生变化,都发布到对应版本的 GitHub Release +- 当前使用的资产名是 `runtime.7z` - 在 release 说明里注明对应的 `runtime.version.json` -## 常见误区 - -### 误区 1:仓库里应该直接提交 `runtime.zip` - -不建议。这个文件体积大、变更频率低、二进制 diff 意义弱,更适合作为发布制品管理。 - -### 误区 2:只要有 `prepare-runtime.ps1`,别人就一定能重建 runtime - -不对。别人还需要: +下载地址格式(示例 `v0.0.3.1`): -- 正确版本的 WinPython 源目录 -- 与 `pythonVersion` 匹配的 fastpath `.pyd` -- 明确的分发和验收规则 - -### 误区 3:运行时依赖开发者本机 DLL 路径 - -不应该。runtime 必须只依赖它自身携带的 `DLLs/` 和包目录内容。 +```text +https://github.com/Wing900/PlotKityCat/releases/download/v版本号/runtime.7z +``` diff --git a/Scripts/go.mod b/Scripts/go.mod new file mode 100644 index 0000000..38fd82d --- /dev/null +++ b/Scripts/go.mod @@ -0,0 +1,3 @@ +module plotkitycat/scripts + +go 1.22 diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/.plotkitycat-scenes.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/.plotkitycat-scenes.json" new file mode 100644 index 0000000..5df9e06 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/.plotkitycat-scenes.json" @@ -0,0 +1,13 @@ +{ + "scenes": [ + "介绍", + "例子1(题目)", + "例子2(圆的面积公式)", + "例子3(题目-图片)", + "洛伦兹吸引子", + "波的干涉与衍射", + "太阳系", + "手绘正弦波", + "作者留言" + ] +} \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/assets/001-image.png" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/assets/001-image.png" new file mode 100644 index 0000000..6915dde Binary files /dev/null and "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/assets/001-image.png" differ diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/main.py" new file mode 100644 index 0000000..0692c4c --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/main.py" @@ -0,0 +1,117 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np + +# 默认抗锯齿与排版字体配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 11 +}) + +# 莫兰迪粉彩星系色彩 +MORANDI = { + 'bg': '#FAF8F5', # 暖黄浅底底色 + 'text_dark': '#2D2D2D', # 主标/正文深灰 + 'text_muted': '#5F6466', # 辅助正文灰 + 'cloud_blue': '#E5EDF1', # 极淡莫兰迪蓝(主云) + 'cloud_pink': '#F6ECE6', # 极淡莫兰迪粉(左云) + 'cloud_green': '#E8F0EB',# 极淡莫兰迪绿(右云) + 'panel_beige': '#FAF3EC',# 浅沙色面板底色 + 'breeze_blue': '#C3D1D6',# 微风灰蓝线 + 'breeze_pink': '#DFB5B2' # 微风粉线 +} + +# 建立 10x12 竖版精致海报 +fig = plt.figure(figsize=(9, 11), facecolor=MORANDI['bg']) +ax = fig.add_axes([0, 0, 1, 1]) +ax.set_xlim(0, 10) +ax.set_ylim(0, 12) +ax.axis('off') + + +# ========================================== +# 1. 矢量数学云朵绘制函数 (代数图形合并) +# ========================================== +def draw_vector_cloud(ax, cx, cy, rx, ry, color, alpha=1.0, zorder=2): + """通过多点圆包络与矩形拼接,解算出平滑的卡通云朵""" + offsets = [ + (-0.5 * rx, -0.05 * ry, 0.55 * ry), + (0.5 * rx, -0.05 * ry, 0.55 * ry), + (-0.2 * rx, 0.25 * ry, 0.75 * ry), + (0.2 * rx, 0.20 * ry, 0.75 * ry), + (0.0 * rx, -0.15 * ry, 0.60 * ry) + ] + for dx, dy, r in offsets: + circle = plt.Circle((cx + dx, cy + dy), r, color=color, alpha=alpha, zorder=zorder) + ax.add_patch(circle) + # 填充中间空隙的矩形 + rect = plt.Rectangle((cx - 0.5 * rx, cy - 0.3 * ry), 1.0 * rx, 0.5 * ry, color=color, alpha=alpha, zorder=zorder) + ax.add_patch(rect) + + +# ========================================== +# 2. 绘制承载云朵的“数学之风”(正弦干涉曲线) +# ========================================== +x_w = np.linspace(0.5, 9.5, 300) +y_w1 = 6.2 + 0.35 * np.sin(0.7 * x_w + 0.5) +ax.plot(x_w, y_w1, color=MORANDI['breeze_blue'], lw=1.0, ls='--', alpha=0.5, zorder=1) + +y_w2 = 5.9 + 0.30 * np.sin(0.7 * x_w - 0.8) +ax.plot(x_w, y_w2, color=MORANDI['breeze_pink'], lw=0.8, ls=':', alpha=0.5, zorder=1) + + +# ========================================== +# 3. 绘制三朵漂浮的数学云 +# ========================================== +# (A) 主云朵(蓝灰色) +draw_vector_cloud(ax, cx=5.0, cy=8.4, rx=3.2, ry=1.1, color=MORANDI['cloud_blue'], alpha=0.9, zorder=2) + +# (B) 左侧云朵(淡粉色) +draw_vector_cloud(ax, cx=2.5, cy=5.1, rx=2.1, ry=0.85, color=MORANDI['cloud_pink'], alpha=0.9, zorder=2) + +# (C) 右侧云朵(淡绿色) +draw_vector_cloud(ax, cx=7.5, cy=5.1, rx=2.1, ry=0.85, color=MORANDI['cloud_green'], alpha=0.9, zorder=2) + + +# ========================================== +# 4. 精确文字排版布局 (提炼关键信息) +# ========================================== + +# --- 顶层主标题 --- +ax.text(5.0, 10.8, "P L O T K I T Y C A T", fontsize=18, color=MORANDI['text_dark'], weight='bold', ha='center') +ax.text(5.0, 10.4, "一个为教室互动场景打造的 Matplotlib 编译器", fontsize=11, color=MORANDI['text_muted'], ha='center') +ax.plot([4.0, 6.0], [10.1, 10.1], color=MORANDI['breeze_blue'], lw=1.5) + +# --- 主云文本 (Introduce) --- +ax.text(5.0, 8.7, "看见数学 · 教室编译", fontsize=13, color=MORANDI['text_dark'], weight='bold', ha='center', zorder=3) +ax.text(5.0, 8.3, "把复杂的画图过程留给 AI 托管", fontsize=10, color=MORANDI['text_muted'], ha='center', zorder=3) +ax.text(5.0, 7.9, "把最直观的数学互动作品轻易带入课堂", fontsize=10, color=MORANDI['text_muted'], ha='center', zorder=3) + +# --- 左云文本 (AI体验) --- +ax.text(2.5, 5.3, "AI 原生体验", fontsize=11, color=MORANDI['text_dark'], weight='bold', ha='center', zorder=3) +ax.text(2.5, 4.9, "教师无须担忧怎么画", fontsize=9.5, color=MORANDI['text_muted'], ha='center', zorder=3) +ax.text(2.5, 4.6, "重心转向“画什么”", fontsize=9.5, color=MORANDI['text_muted'], ha='center', zorder=3) + +# --- 右云文本 (互动与美) --- +ax.text(7.5, 5.3, "学生互动交互", fontsize=11, color=MORANDI['text_dark'], weight='bold', ha='center', zorder=3) +ax.text(7.5, 4.9, "超越静态图片与动画", fontsize=9.5, color=MORANDI['text_muted'], ha='center', zorder=3) +ax.text(7.5, 4.6, "保留最纯粹的数学之美", fontsize=9.5, color=MORANDI['text_muted'], ha='center', zorder=3) + + +# --- 底部社群与链接面板 --- +rect_p = plt.Rectangle((1.5, 1.4), 7.0, 1.9, facecolor=MORANDI['panel_beige'], edgecolor='none', alpha=0.9, zorder=1) +ax.add_patch(rect_p) + +# 教程地址加粗 +ax.text(5.0, 2.7, "社群与在线教程:https://tour.5051001.xyz", fontsize=10.5, color=MORANDI['text_dark'], weight='bold', ha='center', zorder=2) +ax.text(5.0, 2.2, "“ 今虽羸弱,而心犹诚。 ”", fontsize=9.5, color=MORANDI['text_muted'], style='italic', ha='center', zorder=2) +ax.text(5.0, 1.7, "欢迎加入 QQ 社群关注最新发布版本", fontsize=9.5, color=MORANDI['text_muted'], ha='center', zorder=2) + +# 页脚 +ax.text(5.0, 0.7, "Create beautiful stories with PlotKityCat Engine", fontsize=8.5, color=MORANDI['text_muted'], alpha=0.5, ha='center') + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/note.md" new file mode 100644 index 0000000..0782168 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\273\213\347\273\215/note.md" @@ -0,0 +1,20 @@ +## Introduce +**PlotKityCat**是一个为数学课教室互动场景打造的matplotlib编译器!它可以放在U盘当中,把你的可视化作品简单轻易的带到课堂之中! + +**PlotKityCat**集成了AI功能,让数学老师不再为怎么画而烦恼,把数学可视化的重心从“怎么画”转移到“画什么”上来。 + +我们想做的事情是: +- 为老师提供AI原生体验,把画的过程交给AI +- 为学生提供互动体验,而不仅仅是图片和动画 +- 保留美感,无论是软件本身的美,还是数学之美 + +![001-image]() + +## 今虽羸弱,而心犹诚! + +在这里,你可以看到我们的软件教程和社群: + +https://tour.5051001.xyz + + +> 我们团队还很弱小🥰,加入我们的QQ群可以关注我们的最新版本发布。 \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/main.py" new file mode 100644 index 0000000..b465a75 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/main.py" @@ -0,0 +1,138 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np + +# 默认抗锯齿与排版字体配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 11 +}) + +# 精致的莫兰迪色表 +MORANDI = { + 'bg': '#FAF8F5', # 温暖黄白底色 + 'text_dark': '#2D2D2D', # 主标/正文深灰 + 'text_muted': '#5F6466', # 辅助正文灰 + 'accent_pink': '#EAD1C3',# 脏粉色(大字报强调色) + 'accent_blue': '#A9C9D6',# 灰蓝色 + 'panel_blue': '#EEF4F6', # 灰蓝轻量面板底色 + 'panel_beige': '#F5EFE8',# 浅沙色轻量面板底色 + 'grid': '#E5EAEB' # 背景弱格栅 +} + +# 建立 10x15 的高比例海报画布 +fig = plt.figure(figsize=(10, 15), facecolor=MORANDI['bg']) +ax = fig.add_axes([0, 0, 1, 1]) +ax.set_xlim(0, 10) +ax.set_ylim(0, 15) +ax.axis('off') + +# ========================================== +# 1. 绘制背景数学艺术水印(呼应“看见数学”) +# ========================================== +# (a) 顶部:抽象的利萨茹共振曲线水印 +t_l = np.linspace(0, 2 * np.pi, 500) +x_l = 8.0 + 1.2 * np.sin(3 * t_l) +y_l = 13.5 + 0.8 * np.sin(4 * t_l) +ax.plot(x_l, y_l, color=MORANDI['accent_blue'], lw=1.2, alpha=0.35, zorder=0) + +# (b) 底部:黄金对数螺旋线水印 +theta = np.linspace(0, 5 * np.pi, 800) +r = 0.08 * np.exp(0.18 * theta) +x_spiral = 1.2 + r * np.cos(theta) +y_spiral = 2.0 + r * np.sin(theta) +ax.plot(x_spiral, y_spiral, color=MORANDI['accent_pink'], lw=1.2, alpha=0.35, zorder=0) + +# (c) 极淡的底框设计格栅线 +for gx in np.linspace(0.5, 9.5, 10): + ax.axvline(gx, color=MORANDI['grid'], lw=0.5, ls=':', zorder=0) +for gy in np.linspace(0.5, 14.5, 15): + ax.axhline(gy, color=MORANDI['grid'], lw=0.5, ls=':', zorder=0) + + +# ========================================== +# 2. 段落排版辅助函数(支持自动换行与段落控制) +# ========================================== +def draw_block_text(ax, lines, x, y_start, spacing=0.36, fontsize=11, color=MORANDI['text_muted'], fontweight='normal'): + curr_y = y_start + for line in lines: + ax.text(x, curr_y, line, fontsize=fontsize, color=color, weight=fontweight, va='top', ha='left') + curr_y -= spacing + return curr_y + + +# ========================================== +# 3. 大字报正文海报内容排版 +# ========================================== + +# --- 顶层海报标题区域 --- +ax.text(0.8, 14.2, "PLOTKITYCAT · MATH VISUALIZATION", fontsize=10, color=MORANDI['accent_blue'], weight='bold', ha='left') +ax.text(0.8, 13.6, "看见数学 · 教室之旅", fontsize=24, color=MORANDI['text_dark'], weight='bold', ha='left') +ax.plot([0.8, 9.2], [13.2, 13.2], color=MORANDI['text_dark'], lw=2, zorder=1) + +# --- 模块一:后续发展 --- +ax.text(0.8, 12.6, "后续发展", fontsize=13, color=MORANDI['text_dark'], weight='bold', ha='left') + +# 重磅口号面板(脏粉色双层立体色块) +rect_bg1 = plt.Rectangle((0.75, 11.25), 8.5, 0.95, facecolor=MORANDI['accent_pink'], alpha=0.4, edgecolor='none', zorder=1) +rect_bg2 = plt.Rectangle((0.8, 11.3), 8.4, 0.9, facecolor=MORANDI['accent_pink'], edgecolor='none', zorder=2) +ax.add_patch(rect_bg1) +ax.add_patch(rect_bg2) + +ax.text(1.2, 11.75, "我们永远只会做一件事,", fontsize=15, color=MORANDI['text_dark'], weight='bold', zorder=3) +ax.text(1.2, 11.45, "把看见数学带到教室去!", fontsize=15, color=MORANDI['text_dark'], weight='bold', zorder=3) + +# --- 模块二:如何设计可视化场景是我们未来要研究的课题 --- +ax.text(0.8, 10.6, "如何设计可视化场景是我们未来要研究的课题", fontsize=13, color=MORANDI['text_dark'], weight='bold', ha='left') + +# 灰蓝轻量内容面板 +rect_p1 = plt.Rectangle((0.8, 5.7), 8.4, 4.5, facecolor=MORANDI['panel_blue'], edgecolor='none', alpha=0.9, zorder=1) +ax.add_patch(rect_p1) + +lines_p1 = [ + "在AI加持的PlotKityCat中,代码不重要,AI训练了成千上万的Matplotlib", + "代码,它们绘图的技术远高于百分之90的Matplotlib学习者——你大可不", + "必担心AI写不出你想要的代码,而是该好好想想如何清楚地表达明白。", + "", + "技术是术,长期以来的数学可视化中,几何画板和GGB等软件解决的是", + "“用什么来可视化的问题”,这些就是术的问题。但如今在PlotKityCat", + "中,这些问题被解决了。", + "", + "让我们转向道的探索吧!怎么样的可视化场景更符合学生的认知?怎么样", + "的互动更能引起学生爬梯子的学习欲望?我相信这将会大有可为!", + "", + "如果你不擅长设计,欢迎加入相关社群,作者会分享自己的可视化理论来", + "共同交流!" +] +draw_block_text(ax, lines_p1, 1.1, 9.9, spacing=0.33, fontsize=10.5, color=MORANDI['text_muted']) + +# --- 模块三:作者联系方式与开源信念 --- +ax.text(0.8, 5.0, "作者联系方式 & 独立承诺", fontsize=13, color=MORANDI['text_dark'], weight='bold', ha='left') + +# 浅沙色暖意内容面板 +rect_p2 = plt.Rectangle((0.8, 1.2), 8.4, 3.4, facecolor=MORANDI['panel_beige'], edgecolor='none', alpha=0.9, zorder=1) +ax.add_patch(rect_p2) + +# 联系邮箱单独加粗高亮 +ax.text(1.1, 4.25, "邮箱:wingflow@qq.com", fontsize=11, color=MORANDI['text_dark'], weight='bold', zorder=2) + +lines_p2 = [ + "作为一个独立开发者,接受商务合作,可以有偿提供技术解决方案。", + "", + "此外,无论您是否付费AI订阅,作者向您保证:不会阉割软件功能,不会", + "增加广告。您的使用就已经是对本软件的最大支持,我不靠这个吃饭!您", + "随时可以进我们的社群进行交流,寻求软件帮助——在保持礼貌的情况下。", + "", + "如果软件帮助到了您,很欢迎在社群中分享您的使用经验,本软件并无赞", + "助入口,您的分享就是最好的赞助。" +] +draw_block_text(ax, lines_p2, 1.1, 3.9, spacing=0.31, fontsize=10, color=MORANDI['text_muted']) + +# --- 页脚标志 --- +ax.text(5.0, 0.7, "Designed on PlotKityCat · Matplotlib Engine", fontsize=9, color=MORANDI['text_muted'], alpha=0.6, ha='center') + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/note.md" new file mode 100644 index 0000000..f547db6 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\275\234\350\200\205\347\225\231\350\250\200/note.md" @@ -0,0 +1,5 @@ +## 后续发展 + +我们永远只会做一件事,把看见数学带到教室去! + +让AI成为数学表达的工具,让更多人可以用自然语言表达出自己的数学想法。 \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/.ai-design-versions.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/.ai-design-versions.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/.ai-design-versions.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/meta.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/meta.json" new file mode 100644 index 0000000..f158995 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/meta.json" @@ -0,0 +1,7 @@ +{ + "id": "card-001", + "createdAt": 1781406182448, + "updatedAt": 1781406182448, + "title": "阳马折线最值模型(空间双重展开)", + "order": 1 +} diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/plan.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/plan.py" new file mode 100644 index 0000000..0654730 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/plan.py" @@ -0,0 +1,81 @@ +系统样式 = { + "环境背景": "#F9FBF9", + "几何线框": "#4A5568", + "填充面": "#E2E8F0", + "高亮折线": "#E53E3E", + "展开轨迹": "#3182CE", + "网格线": "#EDF2F7", + "文本主色": "#2D3748" +} + +图形系统.初始化状态: + 展开进度 = 0.0 # 0.0 为闭合3D状态,1.0 为完全展平状态 + 当前_E_位置 = 0.6 # E点在AB上的比例 + 当前_F_位置 = 0.4 # F点在BC上的比例 + +图形X.三维阳马透视: + # 顶点定义 (3D) + A = [0, 0, 0] + B = [2, 0, 0] + C = [2, 2, 0] + D = [0, 2, 0] + P = [0, 0, 1] + + 计算动态展开坐标(进度): + # 侧面 PAB 绕 AB 旋转 90度 * 进度 + P_prime = 旋转点(P, 轴=AB, 角度=90 * 进度) + # 点 D 绕 BC 旋转 90度 * 进度 + D_prime = 旋转点(D, 轴=BC, 角度=-90 * 进度) + 返回 P_prime, D_prime + + 绘制.渲染: + 绘制底面(A, B, C, D, 填充=填充面, 透明度=0.3) + 绘制侧面(P, A, B, 填充=填充面, 透明度=0.2) + 绘制侧面(P, A, D, 填充=填充面, 透明度=0.1) + + # 动态翻折轨迹 + 如果 进度 > 0: + 绘制弧线轨迹(从=P, 到=P_prime, 颜色=展开轨迹, 样式=虚线) + 绘制弧线轨迹(从=D, 到=D_prime, 颜色=展开轨迹, 样式=虚线) + + # 空间折线 PEFD + 绘制线段(P_prime, E, 颜色=高亮折线, 粗细=2) + 绘制线段(E, F, 颜色=高亮折线, 粗细=2) + 绘制线段(F, D_prime, 颜色=高亮折线, 粗细=2) + 绘制线段(D_prime, P_prime, 颜色=高亮折线, 样式=点划线, 说明="最短路径") + +图形Y.平面展开网格 (2D): + # 建立平面直角坐标系 + # P' 为 (-1, 2), A为(0, 2), B为(0,0), C为(2,0), D'为(2, -2) + 绘制直角坐标系(网格大小=1.0) + + 绘制点("P'", [-1, 2]) + 绘制点("A", [0, 2]) + 绘制点("B", [0, 0]) + 绘制点("C", [2, 0]) + 绘制点("D'", [2, -2]) + + 绘制直线(从=[-1, 2], 到=[2, -2], 颜色=高亮折线, 粗细=2.5, 说明="直线距离 = 5") + 标记交点_E(坐标=[0, 0.67], 标签="E (0, 2/3)") + 标记交点_F(坐标=[1.0, 0], 标签="F (1, 0)") + +图形Z.定量分析: + 计算周长: + 定长_PD = 根号(5) + 动长_PE_EF_FD = 距离(P_prime, E) + 距离(E, F) + 距离(F, D_prime) + 最小动长 = 5.0 + 总周长 = 动长_PE_EF_FD + 定长_PD + + 绘制.关系图表: + 水平条形图(当前折线长, 目标最小值=5.0, 颜色=高亮折线) + +交互.控制面板: + 拖拽.展开滑块(范围=[0.0, 1.0], 默认=0.0): + 接收 变化值: + 图形系统.状态.展开进度 = 变化值 + 系统事件.全局重绘() + + 拖拽.点E滑块(范围=[0.0, 2.0]): + 接收 变化值: + 更新点E位置 + 系统事件.全局重绘() diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/preview.svg" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/preview.svg" new file mode 100644 index 0000000..ff0df8e --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/design_cards/card-001/preview.svg" @@ -0,0 +1,185 @@ + + + + 阳马空间折线最值模型 (双重翻折法) + 《九章算术》商功:底面为矩形,一棱垂直于底面的四棱锥 + + + + + + + 3D 空间翻折示意 (展开进度: 60%) + + + + + + + + + + + + + + + + + + + + + + + + + P' + + + + + + + E + + + F + + + + + + + + + + + + P + A + B + C + D + + + 2 + 2 + 1 + + + + + + + 2D 展开坐标系分析 (完全展平状态) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + B(0,0) + + A(0,2) + + C(2,0) + + D(2,2) + + P'(-1,2) + + D'(2,-2) + + E(0, 2/3) + + F(1,0) + + + + + Δx = 3 + Δy = 4 + + + + + + + 数学推导与证明 + + + + + 空间周长 L = PE + EF + FD + PD + + + 定长边 PD = √(PA² + AD²) = √(1² + 2²) = √5 + + + 折线段最值 (PE + EF + FD)min = P'D' = √(3² + 4²) = 5 + + + + + 最小周长 + 5 + √5 + + + + + + + 探索与验证控制面板 + + + + 动态展开进度 (3D → 2D) + + + + + + + 60% (翻折中) + + + + + 动点偏离度 (验证两点之间直线最短) + + + + 最值点(共线) + + + diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/main.py" new file mode 100644 index 0000000..44c808e --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/main.py" @@ -0,0 +1,252 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from mpl_toolkits.mplot3d.art3d import Poly3DCollection +from matplotlib.widgets import Slider, Button + +# 默认参数配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +mpl.rcParams["axes3d.mouserotationstyle"] = "azel" + +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 10 +}) + +# 马卡龙莫兰迪色系配置 +MORANDI = { + 'bg': '#FAF8F5', # 暖白背景 + 'line_main': '#3D405B', # 深灰主色 + 'line_aux': '#C5C9D0', # 浅灰辅助线 + 'line_highlight': '#D66853',# 柔和砖红 (高亮折线) + 'line_optimal': '#5B8C85', # 莫兰迪青蓝 (最优/展开路径) + 'face_base': '#E0E5DC', # 莫兰迪淡绿 (底面) + 'face_fold1': '#F4F1DE', # 莫兰迪淡黄 (翻折面PAB) + 'face_fold2': '#E4C1F9', # 莫兰迪淡紫 (翻折面DBC) + 'text': '#3D405B', +} + +# 基础几何常数 +# A(0,2,0), B(0,0,0), C(2,0,0), D(2,2,0), P(0,2,1) +A_val = np.array([0.0, 2.0, 0.0]) +B_val = np.array([0.0, 0.0, 0.0]) +C_val = np.array([2.0, 0.0, 0.0]) + +def get_P_t(t): + """计算 P 点随展开进度 t 旋转的坐标""" + theta = t * np.pi / 2 + return np.array([-np.sin(theta), 2.0, np.cos(theta)]) + +def get_D_t(t): + """计算 D 点随展开进度 t 旋转的坐标""" + phi = t * np.pi + return np.array([2.0, 2.0 * np.cos(phi), 2.0 * np.sin(phi)]) + +# 创建画布 +fig = plt.figure(figsize=(12, 6.5), facecolor=MORANDI['bg']) + +# [左子图]: 3D 空间翻折 +ax3d = fig.add_axes([0.03, 0.18, 0.45, 0.77], projection='3d', proj_type='ortho') +ax3d.set_facecolor(MORANDI['bg']) +ax3d.view_init(elev=22, azim=-55) +ax3d.set_axis_off() + +# 设置 3D 视界范围 +ax3d.set_xlim(-1.2, 2.2) +ax3d.set_ylim(-2.2, 2.2) +ax3d.set_zlim(-0.2, 2.2) + +# [右子图]: 2D 平面展开 +ax2d = fig.add_axes([0.55, 0.22, 0.40, 0.68]) +ax2d.set_facecolor(MORANDI['bg']) +ax2d.set_aspect('equal') +ax2d.spines['top'].set_visible(False) +ax2d.spines['right'].set_visible(False) +ax2d.spines['left'].set_color(MORANDI['line_aux']) +ax2d.spines['bottom'].set_color(MORANDI['line_aux']) +ax2d.tick_params(colors=MORANDI['line_main']) +ax2d.grid(True, linestyle='--', color=MORANDI['line_aux'], linewidth=0.5) +ax2d.set_xlim(-1.5, 2.5) +ax2d.set_ylim(-2.5, 2.5) + +# --- 3D 部分静态绘制 --- +# 静态底面 ABCD +poly_base = Poly3DCollection([[A_val, B_val, C_val, np.array([2.0, 2.0, 0.0])]], + facecolors=MORANDI['face_base'], alpha=0.4, edgecolors=MORANDI['line_aux'], linewidths=0.8) +ax3d.add_collection3d(poly_base) + +# 原始阳马框架 (虚线) +pyramid_edges = [ + (A_val, np.array([0, 2, 1])), + (B_val, np.array([0, 2, 1])), + (C_val, np.array([0, 2, 1])), + (np.array([2, 2, 0]), np.array([0, 2, 1])), +] +for p1, p2 in pyramid_edges: + ax3d.plot([p1[0], p2[0]], [p1[1], p2[1]], [p1[2], p2[2]], color=MORANDI['line_aux'], ls=':', lw=0.8) + +# 3D 动态面定义 +poly_pab = Poly3DCollection([], facecolors=MORANDI['face_fold1'], alpha=0.5, edgecolors=MORANDI['line_main'], linewidths=1) +poly_dbc = Poly3DCollection([], facecolors=MORANDI['face_fold2'], alpha=0.4, edgecolors=MORANDI['line_main'], linewidths=1) +ax3d.add_collection3d(poly_pab) +ax3d.add_collection3d(poly_dbc) + +# 3D 动态线条与点 +line_3d_fold, = ax3d.plot([], [], [], color=MORANDI['line_highlight'], lw=2.2, zorder=10) +line_p_track, = ax3d.plot([], [], [], color=MORANDI['line_optimal'], ls='--', lw=1.0) +line_d_track, = ax3d.plot([], [], [], color=MORANDI['line_optimal'], ls='--', lw=1.0) + +dots_3d = ax3d.scatter([], [], [], color=MORANDI['line_highlight'], s=25, zorder=12) + +# 3D 文本标注 +txt_P = ax3d.text(0, 0, 0, r"$P$", fontsize=12, color=MORANDI['text']) +txt_D = ax3d.text(0, 0, 0, r"$D$", fontsize=12, color=MORANDI['text']) +ax3d.text(A_val[0], A_val[1]-0.1, A_val[2], r"$A$", fontsize=11, color=MORANDI['text']) +ax3d.text(B_val[0]-0.1, B_val[1]-0.1, B_val[2], r"$B$", fontsize=11, color=MORANDI['text']) +ax3d.text(C_val[0]+0.1, C_val[1]-0.1, C_val[2], r"$C$", fontsize=11, color=MORANDI['text']) +txt_E_3d = ax3d.text(0, 0, 0, r"$E$", fontsize=11, color=MORANDI['line_highlight']) +txt_F_3d = ax3d.text(0, 0, 0, r"$F$", fontsize=11, color=MORANDI['line_highlight']) + +# --- 2D 部分静态绘制 --- +# 绘制底面矩形 ABCD 边界 +rect_x = [0, 2, 2, 0, 0] +rect_y = [2, 2, 0, 0, 2] +ax2d.plot(rect_x, rect_y, color=MORANDI['line_aux'], ls='--', lw=1.2, label="底面矩形") + +# 绘制最优直虚线 P'D' +ax2d.plot([-1, 2], [2, -2], color=MORANDI['line_optimal'], ls='--', lw=1.2, label="理论最短路径") + +# 2D 动态折线与点 +line_2d_fold, = ax2d.plot([], [], color=MORANDI['line_highlight'], lw=2.2, marker='o', ms=5, zorder=5) +dot_p_prime, = ax2d.plot([], [], 'o', color=MORANDI['line_optimal'], ms=6) +dot_d_prime, = ax2d.plot([], [], 'o', color=MORANDI['line_optimal'], ms=6) + +# 2D 文本标注 +ax2d.text(-0.2, 2.1, r"$A(0,2)$", color=MORANDI['text']) +ax2d.text(-0.4, -0.3, r"$B(0,0)$", color=MORANDI['text']) +ax2d.text(2.1, -0.2, r"$C(2,0)$", color=MORANDI['text']) +ax2d.text(2.1, 2.1, r"$D(2,2)$", color=MORANDI['text']) +txt_p_prime = ax2d.text(0, 0, "", color=MORANDI['line_optimal'], fontweight='bold') +txt_d_prime = ax2d.text(0, 0, "", color=MORANDI['line_optimal'], fontweight='bold') +txt_E_2d = ax2d.text(0, 0, "", color=MORANDI['line_highlight']) +txt_F_2d = ax2d.text(0, 0, "", color=MORANDI['line_highlight']) + +# 页面标题与数学信息展示 +ax3d.text2D(0.05, 0.95, "阳马折线最值模型 (空间双重展开)", transform=ax3d.transAxes, fontsize=14, fontweight='bold', color=MORANDI['text']) +info_text = ax2d.text(-1.3, -2.2, "", fontsize=10, bbox=dict(facecolor='white', alpha=0.8, edgecolor=MORANDI['line_aux'])) + +# --- 交互控件布局 --- +ax_slider_t = fig.add_axes([0.12, 0.08, 0.30, 0.025], facecolor=MORANDI['bg']) +ax_slider_e = fig.add_axes([0.55, 0.08, 0.15, 0.025], facecolor=MORANDI['bg']) +ax_slider_f = fig.add_axes([0.75, 0.08, 0.15, 0.025], facecolor=MORANDI['bg']) +ax_btn = fig.add_axes([0.92, 0.07, 0.05, 0.04], facecolor=MORANDI['bg']) + +slider_t = Slider(ax_slider_t, '展开进度', 0.0, 1.0, valinit=0.0, color=MORANDI['line_optimal']) +slider_e = Slider(ax_slider_e, 'E点坐标', 0.0, 2.0, valinit=0.6, color=MORANDI['line_highlight']) +slider_f = Slider(ax_slider_f, 'F点坐标', 0.0, 2.0, valinit=1.4, color=MORANDI['line_highlight']) +btn_opt = Button(ax_btn, '最值', color='white', hovercolor='#F1F6F6') + +# 去除滑块轴线以保证美观 +for ax in [ax_slider_t, ax_slider_e, ax_slider_f]: + for spine in ax.spines.values(): + spine.set_visible(False) + ax.set_xticks([]) + ax.set_yticks([]) + +# --- 更新逻辑 --- +def update(val): + t = slider_t.val + y_E = slider_e.val + x_F = slider_f.val + + # 1. 计算三维坐标 + P_t = get_P_t(t) + D_t = get_D_t(t) + E_t = np.array([0.0, y_E, 0.0]) + F_t = np.array([x_F, 0.0, 0.0]) + + # 2. 更新 3D 图形 + poly_pab.set_verts([[P_t, A_val, B_val]]) + poly_dbc.set_verts([[D_t, B_val, C_val]]) + + # 绘制空间折线 P(t) -> E -> F -> D(t) + xs = [P_t[0], E_t[0], F_t[0], D_t[0]] + ys = [P_t[1], E_t[1], F_t[1], D_t[1]] + zs = [P_t[2], E_t[2], F_t[2], D_t[2]] + line_3d_fold.set_data(xs, ys) + line_3d_fold.set_3d_properties(zs) + + # 绘制翻折圆弧轨迹 + t_space = np.linspace(0, t, 30) + p_tr = np.array([get_P_t(ti) for ti in t_space]) + d_tr = np.array([get_D_t(ti) for ti in t_space]) + line_p_track.set_data(p_tr[:, 0], p_tr[:, 1]) + line_p_track.set_3d_properties(p_tr[:, 2]) + line_d_track.set_data(d_tr[:, 0], d_tr[:, 1]) + line_d_track.set_3d_properties(d_tr[:, 2]) + + # 更新散点和文本 + dots_3d._offsets3d = (xs, ys, zs) + txt_P.set_position_3d(P_t + np.array([-0.1, 0.1, 0.1])) + txt_D.set_position_3d(D_t + np.array([0.1, 0.1, 0.1])) + txt_E_3d.set_position_3d(E_t + np.array([-0.2, 0.0, 0.1])) + txt_F_3d.set_position_3d(F_t + np.array([0.0, -0.2, 0.1])) + + # 3. 更新 2D 图形 + P_2d = np.array([-1.0, 2.0]) + D_2d = np.array([2.0, -2.0]) + + # 折线: P' -> E -> F -> D' + line_2d_fold.set_data([P_2d[0], 0, x_F, D_2d[0]], [P_2d[1], y_E, 0, D_2d[1]]) + + # 动画过程中,P' 和 D' 对应的点逐渐显现或淡入 (这里直接显示终点) + dot_p_prime.set_data([P_2d[0]], [P_2d[1]]) + dot_d_prime.set_data([D_2d[0]], [D_2d[1]]) + + txt_p_prime.set_position((P_2d[0]-0.5, P_2d[1]-0.1)) + txt_p_prime.set_text(r"$P'(-1,2)$") + txt_d_prime.set_position((D_2d[0]+0.1, D_2d[1]-0.1)) + txt_d_prime.set_text(r"$D'(2,-2)$") + + txt_E_2d.set_position((0.1, y_E+0.05)) + txt_E_2d.set_text(f"$E(0, {y_E:.2f})$") + txt_F_2d.set_position((x_F-0.2, -0.35)) + txt_F_2d.set_text(f"$F({x_F:.2f}, 0)$") + + # 4. 计算实时长度与显示 + dist_PE = np.linalg.norm(P_2d - np.array([0.0, y_E])) + dist_EF = np.linalg.norm(np.array([0.0, y_E]) - np.array([x_F, 0.0])) + dist_FD = np.linalg.norm(np.array([x_F, 0.0]) - D_2d) + current_fold_len = dist_PE + dist_EF + dist_FD + + fixed_PD = np.sqrt(5.0) # PD = sqrt(1^2 + 2^2) = sqrt(5) + total_len = current_fold_len + fixed_PD + + info_text.set_text( + f"定长边 $PD = \\sqrt{{5}} \\approx {fixed_PD:.3f}$\n" + f"当前动折线 $PE+EF+FD = {current_fold_len:.3f}$\n" + f"当前总周长 $L = {total_len:.3f}$\n" + f"理论最小周长 $L_{{min}} = 5 + \\sqrt{{5}} \\approx 7.236$" + ) + + fig.canvas.draw_idle() + +# 最值一键复位 +def reset_to_optimal(event): + slider_e.set_val(2.0/3.0) + slider_f.set_val(1.0) + slider_t.set_val(1.0) + +# 绑定事件 +slider_t.on_changed(update) +slider_e.on_changed(update) +slider_f.on_changed(update) +btn_opt.on_clicked(reset_to_optimal) + +# 初始化状态 +update(None) + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/note.md" new file mode 100644 index 0000000..b7e7753 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2201\357\274\210\351\242\230\347\233\256\357\274\211/note.md" @@ -0,0 +1,10 @@ +11. 《九章算术》卷五《商功》中描述几何体“阳马”为“底面为矩形,一棱垂直于底面的四棱锥”. 在阳马 $P-ABCD$ 中,$PA$ $\perp$平面 $ABCD$,$PA = 1$,$AB = AD = 2$,点 $E, F$ 分别在棱 $AB, BC$ 上,则空间四边形 $PEFD$ 的周长的最小值为( ) + +A. $3 + \sqrt{5}$ +B. $4 + \sqrt{5}$ +C. $5 + \sqrt{5}$ +D. $6 + \sqrt{5}$ + + + +:::design-card{id="card-001"} \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/main.py" new file mode 100644 index 0000000..c3cbd04 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/main.py" @@ -0,0 +1,215 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.patches import Wedge, Rectangle +from matplotlib.widgets import Slider, Button + +# 默认参数设置,确保中文字体与 Latex 公式美观 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 11 +}) + +# 莫兰迪配色方案 +MORANDI = { + 'bg': '#FBF9F6', # 温暖白背景 + 'even_sector': '#E8C5C0', # 莫兰迪粉红(下排) + 'odd_sector': '#BAC7D5', # 莫兰迪蓝(上排) + 'aux_line': '#9BA4A6', # 辅助灰色 + 'text': '#3C4043', # 深灰文字 + 'accent': '#D0BC96', # 麦芽黄(强调) + 'slider_track': '#EAE5DF', + 'slider_fill': '#C8B195' +} + +# 初始参数 +init_n = 16 +init_t = 0.0 # 0.0 表示圆,1.0 表示拼成的长方形 +R = 1.0 + +fig = plt.figure(figsize=(10, 6), facecolor=MORANDI['bg']) +ax = fig.add_axes([0.05, 0.20, 0.90, 0.75]) +ax.set_aspect('equal') +ax.axis('off') + +# 设置视口范围,能够同时容纳圆与展开后的长方形 +ax.set_xlim(-2.0, 2.0) +ax.set_ylim(-1.3, 1.3) + +# 动态保存绘制的 patch 和 text 引用 +patches = [] +labels = [] +aux_shapes = [] + +def interpolate_angle(alpha, beta): + """计算最小旋转角差值,使扇形旋转过渡自然""" + diff = beta - alpha + diff = (diff + np.pi) % (2 * np.pi) - np.pi + return alpha, diff + +def draw_geometry(n, t): + """绘制指定份数 n 和展开进度 t 的几何图形""" + # 清除旧的图形和标注 + for p in patches: + p.remove() + patches.clear() + for l in labels: + l.remove() + labels.clear() + for s in aux_shapes: + s.remove() + aux_shapes.clear() + + theta = 2 * np.pi / n + # 计算当前近似长方形的半宽与半高 + W_half = (n * R * np.sin(theta / 2)) / 2 + H_half = (R * np.cos(theta / 2)) / 2 + + # 绘制辅助背景虚线框(长方形目标位置) + if t > 0.1: + rect = Rectangle( + (-W_half, -H_half), 2 * W_half, 2 * H_half, + fill=False, edgecolor=MORANDI['aux_line'], + linestyle='--', linewidth=0.8, alpha=t + ) + ax.add_patch(rect) + aux_shapes.append(rect) + + # 循环绘制每个扇形 + for i in range(n): + # 1. 计算圆状态下的初始参数 + alpha = (i + 0.5) * theta + + # 2. 计算长方形状态下的目标位置与方向 + # 偶数在下朝上,奇数在上朝下 + if i % 2 == 0: + x_target = (i - (n - 1) / 2) * R * np.sin(theta / 2) + y_target = -H_half + beta = np.pi / 2 + color = MORANDI['even_sector'] + else: + x_target = (i - (n - 1) / 2) * R * np.sin(theta / 2) + y_target = H_half + beta = -np.pi / 2 + color = MORANDI['odd_sector'] + + # 3. 线性插值计算当前状态的中心位置与旋转角 + cx = t * x_target + cy = t * y_target + + alpha_init, diff = interpolate_angle(alpha, beta) + current_angle = alpha_init + t * diff + + # Wedge 的角度单位是角度值 + angle_deg = np.degrees(current_angle) + theta_half_deg = np.degrees(theta / 2) + + theta1 = angle_deg - theta_half_deg + theta2 = angle_deg + theta_half_deg + + # 4. 绘制扇形 + wedge = Wedge( + (cx, cy), R, theta1, theta2, + facecolor=color, edgecolor='white', linewidth=0.8, alpha=0.9 + ) + ax.add_patch(wedge) + patches.append(wedge) + + # 5. 添加数学标注 + if t < 0.5: + # 圆状态下的半径标注 + r_line, = ax.plot([0, R * np.cos(0.5)], [0, R * np.sin(0.5)], color=MORANDI['text'], lw=1.2, ls='-') + patches.append(r_line) + r_text = ax.text(0.5 * R * np.cos(0.5) - 0.05, 0.5 * R * np.sin(0.5) + 0.1, r"$r$", + color=MORANDI['text'], fontsize=14, ha='center') + labels.append(r_text) + + if t > 0.5: + # 近似长方形的宽与高标注 + # 底部标注(宽 = 2 * W_half ≈ \pi * r) + w_text = ax.text(0, -H_half - 0.25, r"宽 $\approx \pi r$", + color=MORANDI['text'], fontsize=12, ha='center', alpha=t) + # 右侧标注(高 = r) + h_text = ax.text(W_half + 0.15, 0, r"高 $\approx r$", + color=MORANDI['text'], fontsize=12, va='center', ha='left', alpha=t) + labels.append(w_text) + labels.append(h_text) + + # 顶部公式展示 + formula_str = r"拼合面积 $\approx$ 长 $\times$ 宽 $= \pi r \times r = \pi r^2$" + title_text = ax.text(0, 1.1, formula_str if t > 0.8 else r"圆的面积公式推导", + color=MORANDI['text'], fontsize=14, ha='center', weight='bold') + labels.append(title_text) + +# 首次绘制 +draw_geometry(init_n, init_t) + +# 创建 UI 交互控件 +ax_slider_t = fig.add_axes([0.15, 0.08, 0.40, 0.03], facecolor=MORANDI['bg']) +ax_slider_n = fig.add_axes([0.15, 0.03, 0.40, 0.03], facecolor=MORANDI['bg']) +ax_btn_reset = fig.add_axes([0.75, 0.04, 0.10, 0.06], facecolor=MORANDI['bg']) + +slider_t = Slider( + ax=ax_slider_t, + label='拼接程度 ', + valmin=0.0, + valmax=1.0, + valinit=init_t, + valfmt='%.2f', + color=MORANDI['slider_fill'], + track_color=MORANDI['slider_track'] +) +slider_t.label.set_color(MORANDI['text']) +slider_t.valtext.set_color(MORANDI['text']) + +slider_n = Slider( + ax=ax_slider_n, + label='等分份数 $n$ ', + valmin=4, + valmax=64, + valinit=init_n, + valstep=2, + valfmt='%d', + color=MORANDI['slider_fill'], + track_color=MORANDI['slider_track'] +) +slider_n.label.set_color(MORANDI['text']) +slider_n.valtext.set_color(MORANDI['text']) + +# 移除滑动条边框 +for ax_slider in [ax_slider_t, ax_slider_n]: + for spine in ax_slider.spines.values(): + spine.set_visible(False) + +btn_reset = Button( + ax=ax_btn_reset, + label='重置', + color='white', + hovercolor='#F1F6F6' +) +for spine in ax_btn_reset.spines.values(): + spine.set_color(MORANDI['aux_line']) + spine.set_linewidth(0.8) +btn_reset.label.set_color(MORANDI['text']) + +# 更新回调函数 +def update(val): + t = slider_t.val + n = int(slider_n.val) + draw_geometry(n, t) + fig.canvas.draw_idle() + +slider_t.on_changed(update) +slider_n.on_changed(update) + +def reset(event): + slider_t.reset() + slider_n.reset() + +btn_reset.on_clicked(reset) + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/note.md" new file mode 100644 index 0000000..e7eeb81 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2202\357\274\210\345\234\206\347\232\204\351\235\242\347\247\257\345\205\254\345\274\217\357\274\211/note.md" @@ -0,0 +1,2 @@ +我希望向小朋友展示大名鼎鼎的圆的面积公式,互动式,让学生看到更多的细节,包括扇形一个一个“拼”成长方形的过程 + diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/.ai-code-versions.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/.ai-code-versions.json" new file mode 100644 index 0000000..a763d82 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/.ai-code-versions.json" @@ -0,0 +1,16 @@ +[ + { + "id": "1780015949027183600", + "label": "版本01", + "note": "初始版本", + "code": "import matplotlib as mpl\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection\nfrom matplotlib.widgets import Slider, Button, CheckButtons\n\n# 优化图形渲染与交互设置\nmpl.rcParams['lines.antialiased'] = True\nmpl.rcParams['patch.antialiased'] = True\nmpl.rcParams[\"axes3d.mouserotationstyle\"] = \"azel\"\n\nplt.rcParams.update({\n \"font.family\": \"SimSun\", \n \"mathtext.fontset\": \"stix\", \n \"axes.unicode_minus\": False, \n \"font.size\": 11\n})\n\n# 精选莫兰迪配色系\nMORANDI = {\n 'plane_alpha': '#E8DCC4', # 温柔暖沙(底面)\n 'plane_beta': '#A7BCA6', # 雅致豆绿(滑动条与辅助圆)\n 'plane_gamma': '#B0C4DE', # 雾灰蓝(侧面与曲线)\n 'line_main': '#34495E', # 黛蓝/深灰\n 'line_aux': '#BDC3C7', # 浅银灰\n 'accent': '#E74C3C' # 珊瑚红(高线与焦点)\n}\n\n# 辅助函数:生成圆的三维坐标\ndef make_circle_3d(center, radius, plane='xy'):\n theta = np.linspace(0, 2*np.pi, 100)\n x = np.cos(theta) * radius\n y = np.sin(theta) * radius\n if plane == 'xy':\n return center[0] + x, center[1] + y, np.full_like(x, center[2])\n elif plane == 'xz':\n return center[0] + x, np.full_like(x, center[1]), center[2] + y\n elif plane == 'yz':\n return np.full_like(x, center[0]), center[1] + x, center[2] + y\n\n# 初始化画布\nfig = plt.figure(figsize=(11, 6), facecolor='white')\n\n# [左图]: 3D 正交投影视图\nax1 = fig.add_axes([0.02, 0.15, 0.48, 0.8], projection='3d', proj_type='ortho')\nax1.set_axis_off()\nax1.view_init(elev=22, azim=-35)\nax1.set_xlim(-1.1, 1.1)\nax1.set_ylim(-1.1, 1.1)\nax1.set_zlim(-1.1, 0.2)\nax1.set_box_aspect([1, 1, 0.6])\n\n# [右图]: 2D 曲线分析视图\nax2 = fig.add_axes([0.58, 0.22, 0.36, 0.6])\nax2.spines['top'].set_visible(False)\nax2.spines['right'].set_visible(False)\nax2.spines['left'].set_color(MORANDI['line_aux'])\nax2.spines['bottom'].set_color(MORANDI['line_aux'])\nax2.set_title(\"四棱锥体积 $V(h)$ 与高 $h$ 的变化关系\", pad=15, fontsize=13)\nax2.set_xlabel(\"高 $h$\", color=MORANDI['line_main'])\nax2.set_ylabel(\"体积 $V$\", color=MORANDI['line_main'])\n\n# 绘制外接球的三大主截面圆以勾勒球体空间\ncx_xy, cy_xy, cz_xy = make_circle_3d((0,0,0), 1, 'xy')\ncx_xz, cy_xz, cz_xz = make_circle_3d((0,0,0), 1, 'xz')\ncx_yz, cy_yz, cz_yz = make_circle_3d((0,0,0), 1, 'yz')\n\neq_line, = ax1.plot(cx_xy, cy_xy, cz_xy, color=MORANDI['line_aux'], lw=0.8, ls='--')\nmer_line1, = ax1.plot(cx_xz, cy_xz, cz_xz, color=MORANDI['line_aux'], lw=0.8, ls='--')\nmer_line2, = ax1.plot(cx_yz, cy_yz, cz_yz, color=MORANDI['line_aux'], lw=0.8, ls='--')\nsphere_lines = [eq_line, mer_line1, mer_line2]\n\n# 球心标注\nax1.scatter([0], [0], [0], color=MORANDI['line_main'], s=20, zorder=5)\nax1.text(0.05, 0.05, 0.05, \"$O$\", color=MORANDI['line_main'], fontsize=12, weight='bold')\n\n# 几何参数初始值 (最大体积状态时的理论解析解)\nh_init = 1.0 / np.sqrt(3) # ≈ 0.577\nr_init = np.sqrt(1 - h_init**2)\n\n# 动态截面圆\ns_cx, s_cy, s_cz = make_circle_3d((0,0,-h_init), r_init, 'xy')\nsec_circle_line, = ax1.plot(s_cx, s_cy, s_cz, color=MORANDI['plane_beta'], lw=1.2)\n\n# 四棱锥顶点坐标定义\nO = [0, 0, 0]\nA_init = [r_init, 0, -h_init]\nB_init = [0, r_init, -h_init]\nC_init = [-r_init, 0, -h_init]\nD_init = [0, -r_init, -h_init]\n\n# 渲染立体表面 (Poly3DCollection)\npoly_sides = Poly3DCollection([[O, A_init, B_init], [O, B_init, C_init], [O, C_init, D_init], [O, D_init, A_init]], \n facecolors=MORANDI['plane_gamma'], edgecolors='none', alpha=0.25)\npoly_base = Poly3DCollection([[A_init, B_init, C_init, D_init]], \n facecolors=MORANDI['plane_alpha'], edgecolors='none', alpha=0.4)\nax1.add_collection3d(poly_sides)\nax1.add_collection3d(poly_base)\n\n# 渲染侧棱与底面边缘\nedge_OA, = ax1.plot([O[0], A_init[0]], [O[1], A_init[1]], [O[2], A_init[2]], color=MORANDI['line_main'], lw=1.0)\nedge_OB, = ax1.plot([O[0], B_init[0]], [O[1], B_init[1]], [O[2], B_init[2]], color=MORANDI['line_main'], lw=1.0)\nedge_OC, = ax1.plot([O[0], C_init[0]], [O[1], C_init[1]], [O[2], C_init[2]], color=MORANDI['line_main'], lw=1.0)\nedge_OD, = ax1.plot([O[0], D_init[0]], [O[1], D_init[1]], [O[2], D_init[2]], color=MORANDI['line_main'], lw=1.0)\n\nedge_base, = ax1.plot([A_init[0], B_init[0], C_init[0], D_init[0], A_init[0]], \n [A_init[1], B_init[1], C_init[1], D_init[1], A_init[1]], \n [A_init[2], B_init[2], C_init[2], D_init[2], A_init[2]], color=MORANDI['line_main'], lw=1.2)\n\n# 四棱锥的高线\nheight_line, = ax1.plot([0, 0], [0, 0], [0, -h_init], color=MORANDI['accent'], lw=1.5, ls='-.')\ntext_h = ax1.text(0.05, 0.05, -h_init/2, f\"$h = {h_init:.2f}$\", color=MORANDI['line_main'], fontsize=10)\n\n# 2D 曲线绘制\nh_vals = np.linspace(0.001, 0.999, 200)\nV_vals = (2.0/3.0) * h_vals * (1.0 - h_vals**2)\nax2.plot(h_vals, V_vals, color=MORANDI['plane_gamma'], lw=1.8)\n\n# 标注理论最大值参考虚线\nV_max = (2.0/3.0) * h_init * (1.0 - h_init**2)\nax2.axvline(h_init, color=MORANDI['line_aux'], ls=':', lw=0.8)\nax2.axhline(V_max, color=MORANDI['line_aux'], ls=':', lw=0.8)\n\n# 实时阴影面积与指示器\nfill_area = ax2.fill_between(h_vals, V_vals, where=(h_vals \u003c= h_init), color=MORANDI['plane_gamma'], alpha=0.15)\ndot2d, = ax2.plot([h_init], [V_max], 'o', color=MORANDI['accent'], ms=6)\n\nax2.set_xlim(0, 1)\nax2.set_ylim(0, 0.3)\nax2.set_xticks([0, 1/3, h_init, 1])\nax2.set_xticklabels([\"0\", r\"$\\frac{1}{3}$\", r\"$\\frac{\\sqrt{3}}{3}$\", \"1\"])\nax2.set_yticks([0, V_max, 0.3])\nax2.set_yticklabels([\"0\", r\"$V_{max}$\", \"\"])\n\n# 添加页面顶部标题与结论\nfig.text(0.5, 0.93, \"已知球 $O$ 的半径为 $1$, 四棱锥的顶点为 $O$, 底面四个顶点在球面上\", ha='center', fontsize=14, color=MORANDI['line_main'], weight='bold')\nfig.text(0.5, 0.88, \"当四棱锥体积最大时, 其高 $h = \\\\frac{\\\\sqrt{3}}{3} \\\\approx 0.58$\", ha='center', fontsize=12, color=MORANDI['accent'])\n\n# 设计交互 UI 组件\nax_slider = fig.add_axes([0.10, 0.06, 0.35, 0.02])\nax_chk = fig.add_axes([0.55, 0.04, 0.15, 0.06])\nax_btn = fig.add_axes([0.75, 0.04, 0.12, 0.05])\n\nslider = Slider(ax_slider, '高度 $h$', 0.05, 0.95, valinit=h_init, color=MORANDI['plane_beta'], track_color='#EFEFEF')\nax_slider.set_axis_off()\nslider.valtext.set_color(MORANDI['line_main'])\n\nchk = CheckButtons(ax_chk, ['显示外接球廓线'], [True])\nax_chk.set_axis_off()\nfor patch in ax_chk.patches:\n patch.set_edgecolor(MORANDI['line_aux'])\n patch.set_linewidth(0.8)\n patch.set_facecolor('white')\nfor line in ax_chk.lines:\n line.set_color(MORANDI['line_main'])\n line.set_linewidth(1.2)\n\nbtn = Button(ax_btn, '理论最大体积点', color='white', hovercolor='#F5F5F5')\nfor spine in ax_btn.spines.values(): \n spine.set_color(MORANDI['line_aux'])\n spine.set_linewidth(0.8)\nbtn.label.set_color(MORANDI['line_main'])\n\n# 动态刷新回调逻辑\ndef update(val):\n global fill_area, text_h\n h = slider.val\n r = np.sqrt(1.0 - h**2)\n \n # 1. 刷新截面圆\n cx, cy, cz = make_circle_3d((0,0,-h), r, 'xy')\n sec_circle_line.set_data(cx, cy)\n sec_circle_line.set_3d_properties(cz)\n \n # 2. 刷新四棱锥顶点\n A = [r, 0, -h]\n B = [0, r, -h]\n C = [-r, 0, -h]\n D = [0, -r, -h]\n \n poly_sides.set_verts([[O, A, B], [O, B, C], [O, C, D], [O, D, A]])\n poly_base.set_verts([[A, B, C, D]])\n \n edge_OA.set_data([O[0], A[0]], [O[1], A[1]])\n edge_OA.set_3d_properties([O[2], A[2]])\n edge_OB.set_data([O[0], B[0]], [O[1], B[1]])\n edge_OB.set_3d_properties([O[2], B[2]])\n edge_OC.set_data([O[0], C[0]], [O[1], C[1]])\n edge_OC.set_3d_properties([O[2], C[2]])\n edge_OD.set_data([O[0], D[0]], [O[1], D[1]])\n edge_OD.set_3d_properties([O[2], D[2]])\n \n edge_base.set_data([A[0], B[0], C[0], D[0], A[0]], [A[1], B[1], C[1], D[1], A[1]])\n edge_base.set_3d_properties([A[2], B[2], C[2], D[2], A[2]])\n \n # 3. 刷新高线与数值文本\n height_line.set_data([0, 0], [0, 0])\n height_line.set_3d_properties([0, -h])\n \n text_h.remove()\n text_h = ax1.text(0.05, 0.05, -h/2, f\"$h = {h:.2f}$\", color=MORANDI['line_main'], fontsize=10)\n \n # 4. 刷新右侧 2D 焦点位置与阴影\n V = (2.0/3.0) * h * (1.0 - h**2)\n dot2d.set_data([h], [V])\n \n fill_area.remove()\n fill_area = ax2.fill_between(h_vals, V_vals, where=(h_vals \u003c= h), color=MORANDI['plane_gamma'], alpha=0.15)\n \n fig.canvas.draw_idle()\n\ndef toggle_visibility(label):\n state = chk.get_status()[0]\n for line in sphere_lines:\n line.set_visible(state)\n fig.canvas.draw_idle()\n\n# 绑定交互事件\nslider.on_changed(update)\nchk.on_clicked(toggle_visibility)\nbtn.on_clicked(lambda event: slider.reset())\n\nplt.show()", + "createdAt": 1780015949027 + }, + { + "id": "1780015953971718400", + "label": "版本02", + "note": "把圆曲面画上去", + "code": "import matplotlib as mpl\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection\nfrom matplotlib.widgets import Slider, Button, CheckButtons\n\n# 优化图形渲染与交互设置\nmpl.rcParams['lines.antialiased'] = True\nmpl.rcParams['patch.antialiased'] = True\nmpl.rcParams[\"axes3d.mouserotationstyle\"] = \"azel\"\n\nplt.rcParams.update({\n \"font.family\": \"SimSun\", \n \"mathtext.fontset\": \"stix\", \n \"axes.unicode_minus\": False, \n \"font.size\": 11\n})\n\n# 精选莫兰迪配色系\nMORANDI = {\n 'plane_alpha': '#E8DCC4', # 温柔暖沙(底面)\n 'plane_beta': '#A7BCA6', # 雅致豆绿(滑动条与辅助圆)\n 'plane_gamma': '#B0C4DE', # 雾灰蓝(侧面与曲线)\n 'line_main': '#34495E', # 黛蓝/深灰\n 'line_aux': '#BDC3C7', # 浅银灰\n 'accent': '#E74C3C' # 珊瑚红(高线与焦点)\n}\n\n# 辅助函数:生成圆的三维坐标\ndef make_circle_3d(center, radius, plane='xy'):\n theta = np.linspace(0, 2*np.pi, 100)\n x = np.cos(theta) * radius\n y = np.sin(theta) * radius\n if plane == 'xy':\n return center[0] + x, center[1] + y, np.full_like(x, center[2])\n elif plane == 'xz':\n return center[0] + x, np.full_like(x, center[1]), center[2] + y\n elif plane == 'yz':\n return np.full_like(x, center[0]), center[1] + x, center[2] + y\n\n# 初始化画布\nfig = plt.figure(figsize=(11, 6), facecolor='white')\n\n# [左图]: 3D 正交投影视图\nax1 = fig.add_axes([0.02, 0.15, 0.48, 0.8], projection='3d', proj_type='ortho')\nax1.set_axis_off()\nax1.view_init(elev=22, azim=-35)\nax1.set_xlim(-1.1, 1.1)\nax1.set_ylim(-1.1, 1.1)\nax1.set_zlim(-1.1, 0.2)\nax1.set_box_aspect([1, 1, 0.6])\n\n# [右图]: 2D 曲线分析视图\nax2 = fig.add_axes([0.58, 0.22, 0.36, 0.6])\nax2.spines['top'].set_visible(False)\nax2.spines['right'].set_visible(False)\nax2.spines['left'].set_color(MORANDI['line_aux'])\nax2.spines['bottom'].set_color(MORANDI['line_aux'])\nax2.set_title(\"四棱锥体积 $V(h)$ 与高 $h$ 的变化关系\", pad=15, fontsize=13)\nax2.set_xlabel(\"高 $h$\", color=MORANDI['line_main'])\nax2.set_ylabel(\"体积 $V$\", color=MORANDI['line_main'])\n\n# 绘制外接球的三大主截面圆以勾勒球体空间\ncx_xy, cy_xy, cz_xy = make_circle_3d((0,0,0), 1, 'xy')\ncx_xz, cy_xz, cz_xz = make_circle_3d((0,0,0), 1, 'xz')\ncx_yz, cy_yz, cz_yz = make_circle_3d((0,0,0), 1, 'yz')\n\n# 生成并绘制球体表面网格数据\nu_grid = np.linspace(0, 2 * np.pi, 60)\nv_grid = np.linspace(0, np.pi, 60)\nx_sp = np.outer(np.cos(u_grid), np.sin(v_grid))\ny_sp = np.outer(np.sin(u_grid), np.sin(v_grid))\nz_sp = np.outer(np.ones(np.size(u_grid)), np.cos(v_grid))\nsphere_surf = ax1.plot_surface(x_sp, y_sp, z_sp, color=MORANDI['plane_gamma'], alpha=0.08, edgecolor='none', zorder=0)\n\neq_line, = ax1.plot(cx_xy, cy_xy, cz_xy, color=MORANDI['line_aux'], lw=0.8, ls='--')\nmer_line1, = ax1.plot(cx_xz, cy_xz, cz_xz, color=MORANDI['line_aux'], lw=0.8, ls='--')\nmer_line2, = ax1.plot(cx_yz, cy_yz, cz_yz, color=MORANDI['line_aux'], lw=0.8, ls='--')\nsphere_lines = [eq_line, mer_line1, mer_line2, sphere_surf]\n\n# 球心标注\nax1.scatter([0], [0], [0], color=MORANDI['line_main'], s=20, zorder=5)\nax1.text(0.05, 0.05, 0.05, \"$O$\", color=MORANDI['line_main'], fontsize=12, weight='bold')\n\n# 几何参数初始值 (最大体积状态时的理论解析解)\nh_init = 1.0 / np.sqrt(3) # ≈ 0.577\nr_init = np.sqrt(1 - h_init**2)\n\n# 动态截面圆\ns_cx, s_cy, s_cz = make_circle_3d((0,0,-h_init), r_init, 'xy')\nsec_circle_line, = ax1.plot(s_cx, s_cy, s_cz, color=MORANDI['plane_beta'], lw=1.2)\n\n# 四棱锥顶点坐标定义\nO = [0, 0, 0]\nA_init = [r_init, 0, -h_init]\nB_init = [0, r_init, -h_init]\nC_init = [-r_init, 0, -h_init]\nD_init = [0, -r_init, -h_init]\n\n# 渲染立体表面 (Poly3DCollection)\npoly_sides = Poly3DCollection([[O, A_init, B_init], [O, B_init, C_init], [O, C_init, D_init], [O, D_init, A_init]], \n facecolors=MORANDI['plane_gamma'], edgecolors='none', alpha=0.25)\npoly_base = Poly3DCollection([[A_init, B_init, C_init, D_init]], \n facecolors=MORANDI['plane_alpha'], edgecolors='none', alpha=0.4)\nax1.add_collection3d(poly_sides)\nax1.add_collection3d(poly_base)\n\n# 渲染侧棱与底面边缘\nedge_OA, = ax1.plot([O[0], A_init[0]], [O[1], A_init[1]], [O[2], A_init[2]], color=MORANDI['line_main'], lw=1.0)\nedge_OB, = ax1.plot([O[0], B_init[0]], [O[1], B_init[1]], [O[2], B_init[2]], color=MORANDI['line_main'], lw=1.0)\nedge_OC, = ax1.plot([O[0], C_init[0]], [O[1], C_init[1]], [O[2], C_init[2]], color=MORANDI['line_main'], lw=1.0)\nedge_OD, = ax1.plot([O[0], D_init[0]], [O[1], D_init[1]], [O[2], D_init[2]], color=MORANDI['line_main'], lw=1.0)\n\nedge_base, = ax1.plot([A_init[0], B_init[0], C_init[0], D_init[0], A_init[0]], \n [A_init[1], B_init[1], C_init[1], D_init[1], A_init[1]], \n [A_init[2], B_init[2], C_init[2], D_init[2], A_init[2]], color=MORANDI['line_main'], lw=1.2)\n\n# 四棱锥的高线\nheight_line, = ax1.plot([0, 0], [0, 0], [0, -h_init], color=MORANDI['accent'], lw=1.5, ls='-.')\ntext_h = ax1.text(0.05, 0.05, -h_init/2, f\"$h = {h_init:.2f}$\", color=MORANDI['line_main'], fontsize=10)\n\n# 2D 曲线绘制\nh_vals = np.linspace(0.001, 0.999, 200)\nV_vals = (2.0/3.0) * h_vals * (1.0 - h_vals**2)\nax2.plot(h_vals, V_vals, color=MORANDI['plane_gamma'], lw=1.8)\n\n# 标注理论最大值参考虚线\nV_max = (2.0/3.0) * h_init * (1.0 - h_init**2)\nax2.axvline(h_init, color=MORANDI['line_aux'], ls=':', lw=0.8)\nax2.axhline(V_max, color=MORANDI['line_aux'], ls=':', lw=0.8)\n\n# 实时阴影面积与指示器\nfill_area = ax2.fill_between(h_vals, V_vals, where=(h_vals \u003c= h_init), color=MORANDI['plane_gamma'], alpha=0.15)\ndot2d, = ax2.plot([h_init], [V_max], 'o', color=MORANDI['accent'], ms=6)\n\nax2.set_xlim(0, 1)\nax2.set_ylim(0, 0.3)\nax2.set_xticks([0, 1/3, h_init, 1])\nax2.set_xticklabels([\"0\", r\"$\\frac{1}{3}$\", r\"$\\frac{\\sqrt{3}}{3}$\", \"1\"])\nax2.set_yticks([0, V_max, 0.3])\nax2.set_yticklabels([\"0\", r\"$V_{max}$\", \"\"])\n\n# 添加页面顶部标题与结论\nfig.text(0.5, 0.93, \"已知球 $O$ 的半径为 $1$, 四棱锥的顶点为 $O$, 底面四个顶点在球面上\", ha='center', fontsize=14, color=MORANDI['line_main'], weight='bold')\nfig.text(0.5, 0.88, \"当四棱锥体积最大时, 其高 $h = \\\\frac{\\\\sqrt{3}}{3} \\\\approx 0.58$\", ha='center', fontsize=12, color=MORANDI['accent'])\n\n# 设计交互 UI 组件\nax_slider = fig.add_axes([0.10, 0.06, 0.35, 0.02])\nax_chk = fig.add_axes([0.55, 0.04, 0.15, 0.06])\nax_btn = fig.add_axes([0.75, 0.04, 0.12, 0.05])\n\nslider = Slider(ax_slider, '高度 $h$', 0.05, 0.95, valinit=h_init, color=MORANDI['plane_beta'], track_color='#EFEFEF')\nax_slider.set_axis_off()\nslider.valtext.set_color(MORANDI['line_main'])\n\nchk = CheckButtons(ax_chk, ['显示外接球廓线'], [True])\nax_chk.set_axis_off()\nfor patch in ax_chk.patches:\n patch.set_edgecolor(MORANDI['line_aux'])\n patch.set_linewidth(0.8)\n patch.set_facecolor('white')\nfor line in ax_chk.lines:\n line.set_color(MORANDI['line_main'])\n line.set_linewidth(1.2)\n\nbtn = Button(ax_btn, '理论最大体积点', color='white', hovercolor='#F5F5F5')\nfor spine in ax_btn.spines.values(): \n spine.set_color(MORANDI['line_aux'])\n spine.set_linewidth(0.8)\nbtn.label.set_color(MORANDI['line_main'])\n\n# 动态刷新回调逻辑\ndef update(val):\n global fill_area, text_h\n h = slider.val\n r = np.sqrt(1.0 - h**2)\n \n # 1. 刷新截面圆\n cx, cy, cz = make_circle_3d((0,0,-h), r, 'xy')\n sec_circle_line.set_data(cx, cy)\n sec_circle_line.set_3d_properties(cz)\n \n # 2. 刷新四棱锥顶点\n A = [r, 0, -h]\n B = [0, r, -h]\n C = [-r, 0, -h]\n D = [0, -r, -h]\n \n poly_sides.set_verts([[O, A, B], [O, B, C], [O, C, D], [O, D, A]])\n poly_base.set_verts([[A, B, C, D]])\n \n edge_OA.set_data([O[0], A[0]], [O[1], A[1]])\n edge_OA.set_3d_properties([O[2], A[2]])\n edge_OB.set_data([O[0], B[0]], [O[1], B[1]])\n edge_OB.set_3d_properties([O[2], B[2]])\n edge_OC.set_data([O[0], C[0]], [O[1], C[1]])\n edge_OC.set_3d_properties([O[2], C[2]])\n edge_OD.set_data([O[0], D[0]], [O[1], D[1]])\n edge_OD.set_3d_properties([O[2], D[2]])\n \n edge_base.set_data([A[0], B[0], C[0], D[0], A[0]], [A[1], B[1], C[1], D[1], A[1]])\n edge_base.set_3d_properties([A[2], B[2], C[2], D[2], A[2]])\n \n # 3. 刷新高线与数值文本\n height_line.set_data([0, 0], [0, 0])\n height_line.set_3d_properties([0, -h])\n \n text_h.remove()\n text_h = ax1.text(0.05, 0.05, -h/2, f\"$h = {h:.2f}$\", color=MORANDI['line_main'], fontsize=10)\n \n # 4. 刷新右侧 2D 焦点位置与阴影\n V = (2.0/3.0) * h * (1.0 - h**2)\n dot2d.set_data([h], [V])\n \n fill_area.remove()\n fill_area = ax2.fill_between(h_vals, V_vals, where=(h_vals \u003c= h), color=MORANDI['plane_gamma'], alpha=0.15)\n \n fig.canvas.draw_idle()\n\ndef toggle_visibility(label):\n state = chk.get_status()[0]\n for line in sphere_lines:\n line.set_visible(state)\n fig.canvas.draw_idle()\n\n# 绑定交互事件\nslider.on_changed(update)\nchk.on_clicked(toggle_visibility)\nbtn.on_clicked(lambda event: slider.reset())\n\nplt.show()", + "createdAt": 1780015953971 + } +] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/assets/001-image.png" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/assets/001-image.png" new file mode 100644 index 0000000..b3148cd Binary files /dev/null and "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/assets/001-image.png" differ diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/.ai-design-versions.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/.ai-design-versions.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/.ai-design-versions.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/meta.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/meta.json" new file mode 100644 index 0000000..7ba6fae --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/meta.json" @@ -0,0 +1,7 @@ +{ + "id": "card-001", + "createdAt": 1781405984919, + "updatedAt": 1781405984919, + "title": "球内接四棱锥体积最大化问题", + "order": 1 +} diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/plan.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/plan.py" new file mode 100644 index 0000000..eb61ff8 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/plan.py" @@ -0,0 +1,50 @@ +系统样式 = { + "环境背景": "#F9F9F7", # 暖白背景 + "主线框": "#4A4A4A", # 深灰 + "球体轮廓": "#D4D4D4", # 浅灰 + "球体填充": "#F0F4F6", # 极轻微蓝灰 + "底面填充": "#D5E4E6", # 马卡龙淡青 + "高亮红色": "#E5989B", # 莫兰迪柔粉红 + "辅助虚线": "#99AAB5" # 蓝灰虚线 +} + +图形系统.初始化状态: + 球半径_R = 1.0 + 当前高度_h = 0.577 # 默认初始值为 1/√3 (最大体积点) + 显示辅助面_开关 = 真 + +图形X.3D空间模型: + 球心O = [0, 0, 0] + 计算底面半径 r = 根号(球半径_R^2 - 当前高度_h^2) + + 绘制.球体背景(中心=球心O, 半径=球半径_R, 颜色=球体填充) + 绘制.球心O(点, 颜色=主线框) + + # 动态投影计算底面四个顶点 (绕Z轴旋转45度以获得良好透视) + 顶点V1 = [r * cos(45), r * sin(45), -当前高度_h] + 顶点V2 = [-r * cos(45), r * sin(45), -当前高度_h] + 顶点V3 = [-r * cos(45), -r * sin(45), -当前高度_h] + 顶点V4 = [r * cos(45), -r * sin(45), -当前高度_h] + + 绘制.四边形底面(V1, V2, V3, V4, 填充=底面填充, 边框=主线框) + 绘制.侧棱(从=球心O, 到=各顶点, 前方实线, 后方虚线) + 绘制.高(从=球心O, 到=底面中心, 颜色=高亮红色, 粗细=2.0) + +图形Y.函数曲线分析: + 自变量横轴 = 高度 h (范围 [0, 1]) + 因变量纵轴 = 体积 V(h) = (2/3) * h * (1 - h^2) + + 绘制.坐标系(横轴="h", 纵轴="V") + 绘制.理论曲线(样式=实线, 颜色=主线框) + 绘制.极值标记点(坐标=[0.577, 0.256], 颜色=高亮红色) + 绘制.当前状态点(随着滑动条动态移动) + +交互.控制面板: + 拖拽.高度滑块(范围=[0.0, 1.0], 默认=0.577): + 接收 变化值: + 当前高度_h = 变化值 + 重绘图形() + + 点击.设为最大体积按钮(): + 当前高度_h = 0.577 + 重绘图形() diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/preview.svg" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/preview.svg" new file mode 100644 index 0000000..26ac65e --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/design_cards/card-001/preview.svg" @@ -0,0 +1,138 @@ + + + + 球内接四棱锥最大体积模型 + 顶点在球心,底面四个顶点在球面上 + + + + + + + 3D 空间结构 (h = 0.577) + + + + + + + + + + + O + + + + + + + + + + + + + + + + + + + + + + + + + + + h = 1/√3 + + + + r + + + A + B + C + D + + + + + + + 体积随高度变化曲线: V(h) = 2/3 · h(1 - h²) + + + + + + h + 0 + 1.0 + + + + V + 0.25 + + + + + + + + + + + + + + Max V ≈ 0.256 + √3/3 + + + + + + + 数学推导关系 + + + 1. 底面圆半径: r = √(R² - h²) = √(1 - h²) + 2. 最大底面积 (正方形): S = 2r² = 2(1 - h²) + 3. 四棱锥体积: V = 1/3 · S · h = 2/3(h - h³) + + + 求导得: V'(h) = 2/3(1 - 3h²) = 0 ⇒ h = √3/3 + + + + + + + 交互式调节 + + + + 调节四棱锥高度 (h): + + + + + 0.577 (√3/3) + + + + + + 吸附到最大体积状态 + + + diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/main.py" new file mode 100644 index 0000000..3c85429 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/main.py" @@ -0,0 +1,210 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from mpl_toolkits.mplot3d.art3d import Poly3DCollection +from matplotlib.widgets import Slider, Button + +# 基础配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 11 +}) + +# 莫兰迪色系定义 +BG_COLOR = '#FAF9F6' # 暖白背景 +TEXT_COLOR = '#2F3A40' # 深灰 +LINE_AUX = '#AEB9BF' # 辅助灰蓝 +LINE_MAIN = '#4A5B66' # 主线 +COLOR_SPHERE = '#D0D9D9' # 球体轮廓线 +COLOR_PYRAMID_SIDE = '#B8DCCF' # 侧面粉绿 +COLOR_PYRAMID_BASE = '#BFD4E6' # 底面粉蓝 +COLOR_HEIGHT = '#D99B9B' # 高度高亮粉红 +COLOR_CURVE = '#AFC4E8' # 2D曲线粉蓝 + +# 初始参数:体积最大时的理论高度为 sqrt(3)/3 ≈ 0.577 +h_init = 1.0 / np.sqrt(3) + +# 创建画布 +fig = plt.figure(figsize=(11, 6), facecolor=BG_COLOR) + +# [左子图]: 3D 几何展示 +ax3d = fig.add_axes([0.05, 0.15, 0.45, 0.8], projection='3d', proj_type='ortho') +ax3d.set_axis_off() +ax3d.set_facecolor(BG_COLOR) +ax3d.view_init(elev=22, azim=-55) +ax3d.set_xlim(-1.1, 1.1) +ax3d.set_ylim(-1.1, 1.1) +ax3d.set_zlim(-1.1, 1.1) +ax3d.set_box_aspect([1, 1, 1]) + +# [右子图]: 2D 曲线展示 +ax2d = fig.add_axes([0.58, 0.25, 0.35, 0.55]) +ax2d.set_facecolor(BG_COLOR) +ax2d.spines['top'].set_visible(False) +ax2d.spines['right'].set_visible(False) +ax2d.spines['left'].set_color(LINE_AUX) +ax2d.spines['bottom'].set_color(LINE_AUX) +ax2d.tick_params(colors=TEXT_COLOR) +ax2d.set_xlabel("高 $h$", color=TEXT_COLOR, fontsize=12) +ax2d.set_ylabel("体积 $V$", color=TEXT_COLOR, fontsize=12) +ax2d.set_title("四棱锥体积与高的变化曲线", color=TEXT_COLOR, fontsize=13, pad=15) + +# 1. 绘制球体经纬大圆线作为透视参考 +theta = np.linspace(0, 2 * np.pi, 100) +ax3d.plot(np.cos(theta), np.sin(theta), 0, color=COLOR_SPHERE, lw=0.8, ls='--') +ax3d.plot(np.cos(theta), 0, np.sin(theta), color=COLOR_SPHERE, lw=0.8, ls='--') +ax3d.plot(0, np.cos(theta), np.sin(theta), color=COLOR_SPHERE, lw=0.8, ls='--') + +# 标注球心 O +ax3d.plot([0], [0], [0], 'o', color=TEXT_COLOR, ms=4) +text_O = ax3d.text(0.05, 0.05, 0.08, r"$O$", color=TEXT_COLOR, fontsize=12) + + +def get_pyramid_geometry(h): + """根据给定的高 h 计算内接四棱锥的各个顶点及底面外接圆""" + r = np.sqrt(1.0 - h**2) + O = np.array([0, 0, 0]) + + # 底面正方形的四个顶点在球面上 + A = np.array([r, 0, -h]) + B = np.array([0, r, -h]) + C = np.array([-r, 0, -h]) + D = np.array([0, -r, -h]) + + sides = [[O, A, B], [O, B, C], [O, C, D], [O, D, A]] + base = [[A, B, C, D]] + + # 计算底面截面圆轨道 + t_vals = np.linspace(0, 2 * np.pi, 100) + circle_x = r * np.cos(t_vals) + circle_y = r * np.sin(t_vals) + circle_z = -h * np.ones_like(t_vals) + + return sides, base, (circle_x, circle_y, circle_z), A, B, C, D + + +# 初始化 3D 棱锥和截面圆 +sides, base, circle_data, pA, pB, pC, pD = get_pyramid_geometry(h_init) + +poly_sides = Poly3DCollection(sides, facecolors=COLOR_PYRAMID_SIDE, edgecolors=LINE_MAIN, linewidths=1.0, alpha=0.35) +ax3d.add_collection3d(poly_sides) + +poly_base = Poly3DCollection(base, facecolors=COLOR_PYRAMID_BASE, edgecolors=LINE_MAIN, linewidths=1.2, alpha=0.5) +ax3d.add_collection3d(poly_base) + +line_circle, = ax3d.plot(circle_data[0], circle_data[1], circle_data[2], color=LINE_AUX, lw=0.8, ls=':') +line_height, = ax3d.plot([0, 0], [0, 0], [0, -h_init], color=COLOR_HEIGHT, lw=2.2, ls='-') +dot_base_center, = ax3d.plot([0], [0], [-h_init], 'o', color=COLOR_HEIGHT, ms=4) + +# 动态文本容器 +text_artists = [] + + +def update_labels(A, B, C, D, h): + """刷新3D顶点和高度标注位置""" + for artist in text_artists: + artist.remove() + text_artists.clear() + + tA = ax3d.text(A[0] + 0.05, A[1], A[2] - 0.05, r"$A$", color=TEXT_COLOR, fontsize=11) + tB = ax3d.text(B[0], B[1] + 0.05, B[2] - 0.05, r"$B$", color=TEXT_COLOR, fontsize=11) + tC = ax3d.text(C[0] - 0.08, C[1], C[2] - 0.05, r"$C$", color=TEXT_COLOR, fontsize=11) + tD = ax3d.text(D[0], D[1] - 0.08, D[2] - 0.05, r"$D$", color=TEXT_COLOR, fontsize=11) + th = ax3d.text(0.06, 0, -h / 2, r"$h$", color=COLOR_HEIGHT, fontsize=12, weight='bold') + + text_artists.extend([tA, tB, tC, tD, th]) + + +update_labels(pA, pB, pC, pD, h_init) + +# 2D 曲线绘制 +h_vals = np.linspace(0.0, 1.0, 200) +v_vals = (2.0 / 3.0) * (h_vals - h_vals**3) +ax2d.plot(h_vals, v_vals, color=COLOR_CURVE, lw=2.0, label=r"$V = \frac{2}{3}(h - h^3)$") + +# 标注理论最大值点 +h_opt = 1.0 / np.sqrt(3) +v_opt = (2.0 / 3.0) * (h_opt - h_opt**3) +ax2d.plot(h_opt, v_opt, 'o', color=COLOR_HEIGHT, ms=6) +ax2d.text(h_opt + 0.03, v_opt + 0.01, r"最大值点 $h = \frac{\sqrt{3}}{3}$", color=TEXT_COLOR, fontsize=10) + +# 当前状态点与垂线指示 +line_v_loc, = ax2d.plot([h_init, h_init], [0, v_opt], color=LINE_AUX, lw=0.8, ls='--') +dot_current, = ax2d.plot([h_init], [v_opt], 'o', color=LINE_MAIN, ms=6) + +# 填充当前包络面积的半透明阴影 +fill_container = [ax2d.fill_between(h_vals, 0, v_vals, where=(h_vals <= h_init), color=COLOR_CURVE, alpha=0.15)] +ax2d.legend(frameon=False, loc='upper left') + +# [控件轴定义] +ax_slider = fig.add_axes([0.15, 0.06, 0.28, 0.03], facecolor=BG_COLOR) +slider_h = Slider( + ax=ax_slider, + label='高度 $h$ ', + valmin=0.01, + valmax=0.99, + valinit=h_init, + valfmt='%.3f', + color=COLOR_PYRAMID_SIDE, + track_color='#EAEAEA', + initcolor='none' +) +for spine in ax_slider.spines.values(): + spine.set_visible(False) +ax_slider.set_xticks([]) +ax_slider.set_yticks([]) +slider_h.label.set_color(TEXT_COLOR) +slider_h.valtext.set_color(TEXT_COLOR) + +ax_btn = fig.add_axes([0.48, 0.06, 0.08, 0.03], facecolor=BG_COLOR) +btn_reset = Button(ax_btn, '最大体积', color='white', hovercolor='#F1F6F6') +for spine in ax_btn.spines.values(): + spine.set_color(LINE_AUX) + spine.set_linewidth(0.8) +btn_reset.label.set_color(TEXT_COLOR) + + +# 交互更新逻辑 +def update(val): + h = slider_h.val + + # 重新计算 3D 结构 + sides, base, circle_data, A, B, C, D = get_pyramid_geometry(h) + + # 更新三维棱锥和高度线 + poly_sides.set_verts(sides) + poly_base.set_verts(base) + line_circle.set_data(circle_data[0], circle_data[1]) + line_circle.set_3d_properties(circle_data[2]) + line_height.set_data([0, 0], [0, 0]) + line_height.set_3d_properties([0, -h]) + dot_base_center.set_data([0], [0]) + dot_base_center.set_3d_properties([-h]) + + # 更新顶点标签位置 + update_labels(A, B, C, D, h) + + # 更新 2D 指示器 + v = (2.0 / 3.0) * (h - h**3) + dot_current.set_data([h], [v]) + line_v_loc.set_data([h, h], [0, v]) + + # 重绘阴影区域 + fill_container[0].remove() + fill_container[0] = ax2d.fill_between(h_vals, 0, v_vals, where=(h_vals <= h), color=COLOR_CURVE, alpha=0.15) + + fig.canvas.draw_idle() + + +def reset(event): + slider_h.set_val(h_opt) + + +slider_h.on_changed(update) +btn_reset.on_clicked(reset) + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/note.md" new file mode 100644 index 0000000..80f39bb --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\344\276\213\345\255\2203\357\274\210\351\242\230\347\233\256-\345\233\276\347\211\207\357\274\211/note.md" @@ -0,0 +1,5 @@ +![001-image]() + +图片也是可以选中后可视化的,拖拽,互动式 + +:::design-card{id="card-001"} \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/main.py" new file mode 100644 index 0000000..d3a5081 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/main.py" @@ -0,0 +1,243 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.animation import FuncAnimation +from matplotlib.widgets import Slider, Button, CheckButtons + +# 默认抗锯齿与字体配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 11 +}) + +# 精确调配的马卡龙莫兰迪浅色系 +MORANDI = { + 'bg': '#FAF8F5', # 暖黄浅底色 + 'text': '#4A4A4A', # 灰黑色文字 + 'orbit': '#CCD5DB', # 辅助灰蓝轨道线 + 'sun': '#EBC29B', # 柔和暖橙太阳 + 'mercury': '#B6C8D4', # 粉蓝灰水星 + 'venus': '#EAD1C3', # 莫兰迪粉橘金星 + 'earth': '#A9C9D6', # 浅绿蓝地球 + 'mars': '#DFB5B2', # 浅粉红火星 + 'jupiter': '#DECFA4', # 浅粉黄木星 + 'asteroid': '#D5DFE2', # 小行星带淡灰 +} + +# 倾斜投影比例因子(产生斜侧视的3D透视感) +tilt = 0.35 + +# 太阳系天体代数与轨道参数配置(R: 轨道半长轴, omega: 相对角速度) +PLANETS = [ + {'name': '水星', 'R': 1.1, 'omega': 3.2, 'color': MORANDI['mercury'], 'size': 6}, + {'name': '金星', 'R': 1.7, 'omega': 1.6, 'color': MORANDI['venus'], 'size': 9}, + {'name': '地球', 'R': 2.4, 'omega': 1.0, 'color': MORANDI['earth'], 'size': 10}, + {'name': '火星', 'R': 3.1, 'omega': 0.6, 'color': MORANDI['mars'], 'size': 8}, + {'name': '木星', 'R': 4.1, 'omega': 0.25, 'color': MORANDI['jupiter'], 'size': 14}, +] + +# 初始化画布 +fig = plt.figure(figsize=(10.5, 6.5), facecolor=MORANDI['bg']) + +# [主视图空间] +ax = fig.add_axes([0.02, 0.12, 0.72, 0.82]) +ax.set_facecolor(MORANDI['bg']) +ax.set_xlim(-5.0, 5.0) +ax.set_ylim(-2.1, 2.1) +ax.set_aspect('equal') +ax.axis('off') + +# 1. 绘制太阳多层晕光 +for r_glow, alpha in zip([0.16, 0.28, 0.42], [0.8, 0.35, 0.12]): + ax.add_patch(plt.Circle((0, 0), r_glow, color=MORANDI['sun'], alpha=alpha, zorder=3)) + +# 2. 绘制背景星盘精细刻度环 +ticks_x = [] +ticks_y = [] +for th in np.linspace(0, 2 * np.pi, 96, endpoint=False): + ticks_x.extend([4.8 * np.cos(th), 4.72 * np.cos(th), None]) + ticks_y.extend([4.8 * tilt * np.sin(th), 4.72 * tilt * np.sin(th), None]) +ax.plot(ticks_x, ticks_y, color=MORANDI['orbit'], lw=0.6, alpha=0.8, zorder=0) + +theta_orbit = np.linspace(0, 2 * np.pi, 250) +ax.plot(4.8 * np.cos(theta_orbit), 4.8 * tilt * np.sin(theta_orbit), color=MORANDI['orbit'], lw=0.8, alpha=0.5, zorder=0) + +# 3. 生成小行星带分布(介于火星与木星轨道之间) +np.random.seed(101) +num_asteroids = 160 +ast_r = np.random.uniform(3.4, 3.7, num_asteroids) +ast_theta = np.random.uniform(0, 2 * np.pi, num_asteroids) +ast_x = ast_r * np.cos(ast_theta) +ast_y = tilt * ast_r * np.sin(ast_theta) +ax.scatter(ast_x, ast_y, s=1.2, color=MORANDI['asteroid'], alpha=0.7, zorder=1) + +# 4. 实例天体轨道与定位点 +orbit_lines = [] +planet_dots = [] + +for p in PLANETS: + # 投影后的椭圆轨道线 + x_o = p['R'] * np.cos(theta_orbit) + y_o = tilt * p['R'] * np.sin(theta_orbit) + line, = ax.plot(x_o, y_o, color=MORANDI['orbit'], lw=0.8, ls='--', alpha=0.6, zorder=1) + orbit_lines.append(line) + + # 行星实体点 + dot, = ax.plot([], [], 'o', color=p['color'], ms=p['size'], zorder=4) + planet_dots.append(dot) + +# 5. 单独建立月球运动实体 +moon_dot, = ax.plot([], [], 'o', color='#AEB9BF', ms=3, zorder=5) +moon_orbit, = ax.plot([], [], color=MORANDI['orbit'], lw=0.4, ls=':', alpha=0.6, zorder=2) + +# 状态管理 +state = { + 't': 0.0, + 'speed': 1.0, + 'is_running': True, + 'visible': [True] * len(PLANETS) +} + +# 顶层标题 +ax.text(0, 1.95, "太阳系多星体轨道运行投影系统", ha='center', va='center', fontsize=14, color=MORANDI['text']) + +# [交互组件空间布局] +ax_chk = fig.add_axes([0.76, 0.32, 0.20, 0.45], facecolor=MORANDI['bg']) +ax_chk.set_axis_off() + +ax_slider = fig.add_axes([0.12, 0.06, 0.42, 0.03], facecolor=MORANDI['bg']) +ax_btn = fig.add_axes([0.58, 0.05, 0.11, 0.05], facecolor=MORANDI['bg']) + +# 6. 配置多维状态复选框 +chk = CheckButtons( + ax=ax_chk, + labels=[p['name'] for p in PLANETS], + actives=[True] * len(PLANETS), + label_props={ + 'color': [MORANDI['text']] * len(PLANETS), + }, + frame_props={ + 'edgecolor': [MORANDI['orbit']] * len(PLANETS), + 'facecolor': ['white'] * len(PLANETS), + 'linewidth': [0.8] * len(PLANETS), + 's': [45] * len(PLANETS), + }, + check_props={ + 'facecolor': [p['color'] for p in PLANETS], + 'linewidth': [1.0] * len(PLANETS), + 's': [45] * len(PLANETS), + } +) + +for spine in ax_chk.spines.values(): + spine.set_visible(False) + +# 7. 配置时间流速滑块 +slider = Slider( + ax=ax_slider, + label='运行速度', + valmin=0.0, + valmax=3.0, + valinit=1.0, + valfmt='%1.1fx', + color=MORANDI['mercury'], + track_color='#E8EEEE', + initcolor='none', + handle_style={ + 'facecolor': MORANDI['text'], + 'edgecolor': 'white', + 'size': 10 + } +) +for spine in ax_slider.spines.values(): + spine.set_visible(False) +ax_slider.set_xticks([]) +ax_slider.set_yticks([]) +slider.label.set_color(MORANDI['text']) +slider.valtext.set_color(MORANDI['text']) + +# 8. 配置启停按钮 +btn = Button(ax_btn, '暂停', color='white', hovercolor='#F1F6F6') +for spine in ax_btn.spines.values(): + spine.set_color(MORANDI['orbit']) + spine.set_linewidth(0.8) +btn.label.set_color(MORANDI['text']) + + +# 核心坐标更新函数 +def update_positions(t_val): + for i, p in enumerate(PLANETS): + if state['visible'][i]: + angle = p['omega'] * t_val + x = p['R'] * np.cos(angle) + y = tilt * p['R'] * np.sin(angle) + planet_dots[i].set_data([x], [y]) + planet_dots[i].set_visible(True) + orbit_lines[i].set_visible(True) + else: + planet_dots[i].set_visible(False) + orbit_lines[i].set_visible(False) + + # 单独处理地月子系统的代数坐标关联 (Earth index = 2) + if state['visible'][2]: + earth_angle = PLANETS[2]['omega'] * t_val + x_e = PLANETS[2]['R'] * np.cos(earth_angle) + y_e = tilt * PLANETS[2]['R'] * np.sin(earth_angle) + + # 月球绕地轨道半径与其相对极高频角速度 + r_m = 0.22 + moon_angle = 12.0 * earth_angle + x_m = x_e + r_m * np.cos(moon_angle) + y_m = y_e + tilt * r_m * np.sin(moon_angle) + + moon_dot.set_data([x_m], [y_m]) + moon_dot.set_visible(True) + + # 实时生成并投射月球运行微轨 + x_mo = x_e + r_m * np.cos(theta_orbit) + y_mo = y_e + tilt * r_m * np.sin(theta_orbit) + moon_orbit.set_data(x_mo, y_mo) + moon_orbit.set_visible(True) + else: + moon_dot.set_visible(False) + moon_orbit.set_visible(False) + + +# 9. 注册回调函数与状态驱动 +def animate(frame): + if state['is_running']: + state['t'] += 0.02 * state['speed'] + update_positions(state['t']) + return planet_dots + [moon_dot, moon_orbit] + orbit_lines + + +def on_slider_change(val): + state['speed'] = val + + +def on_check_clicked(label): + for i, p in enumerate(PLANETS): + if p['name'] == label: + state['visible'][i] = not state['visible'][i] + update_positions(state['t']) + fig.canvas.draw_idle() + + +def on_button_clicked(event): + state['is_running'] = not state['is_running'] + btn.label.set_text('播放' if not state['is_running'] else '暂停') + fig.canvas.draw_idle() + + +slider.on_changed(on_slider_change) +chk.on_clicked(on_check_clicked) +btn.on_clicked(on_button_clicked) + +# 启动平滑动画 +ani = FuncAnimation(fig, animate, interval=25, blit=False) + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/note.md" new file mode 100644 index 0000000..1df388a --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\345\244\252\351\230\263\347\263\273/note.md" @@ -0,0 +1 @@ +一个太阳系的运行图,逼真壮观 \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/main.py" new file mode 100644 index 0000000..5e66407 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/main.py" @@ -0,0 +1,85 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np + +# 1. 启用手绘草图风格 (XKCD 风格) +plt.xkcd(scale=1, length=100, randomness=2) + +plt.rcParams.update({ + "font.family": "sans-serif", + "mathtext.fontset": "cm", + "font.size": 11 +}) + +# 莫兰迪粉彩配色 +MORANDI = { + 'bg': '#FAF8F5', # 暖黄底色 + 'text': '#3A3A3A', # 灰黑主文字 + 'line': '#5F6466', # 轴线与主要边框 + 'circle_fill': '#A9C9D6',# 莫兰迪浅蓝 (圆盘) + 'wave_fill': '#DFB5B2', # 莫兰迪浅粉 (正弦波填充) + 'aux_dash': '#CCD5DB' # 辅助投影虚线 +} + +# 2. 初始化画布与轴线 +fig = plt.figure(figsize=(9.5, 4.8), facecolor=MORANDI['bg']) +ax = fig.add_axes([0.02, 0.02, 0.96, 0.96]) +ax.set_facecolor(MORANDI['bg']) +ax.set_xlim(-3.5, 6.8) +ax.set_ylim(-1.5, 1.8) +ax.set_aspect('equal') # 严格保证圆与波形的几何比例 +ax.axis('off') + +# 3. 几何参数解算 +theta = 1.18 # 设定当前观测的旋转角 (约 67.6 度) +cx, cy = -2.0, 0.0 # 单位圆中心坐标 +r = 1.0 # 单位圆半径 + +# 4. 绘制底图参考坐标系 (手绘风) +ax.plot([-3.2, -0.8], [0, 0], color=MORANDI['aux_dash'], lw=1.0, ls=':', zorder=0) +ax.plot([-2.0, -2.0], [-1.2, 1.2], color=MORANDI['aux_dash'], lw=1.0, ls=':', zorder=0) +ax.plot([-0.2, 6.5], [0, 0], color=MORANDI['line'], lw=1.2, zorder=1) # 波动轴线 +ax.plot([0, 0], [-1.2, 1.2], color=MORANDI['line'], lw=1.2, zorder=1) # 波动y轴 + +# 5. 绘制单位圆上的扇形区域 +t_wedge = np.linspace(0, theta, 100) +x_w = np.concatenate([[cx], cx + r * np.cos(t_wedge), [cx]]) +y_w = np.concatenate([[cy], cy + r * np.sin(t_wedge), [cy]]) +ax.fill(x_w, y_w, color=MORANDI['circle_fill'], alpha=0.45, zorder=1) +ax.plot(cx + r * np.cos(t_wedge), cy + r * np.sin(t_wedge), color=MORANDI['line'], lw=1.8, zorder=2) + +# 绘制完整的圆弧背景 +t_full = np.linspace(0, 2 * np.pi, 200) +ax.plot(cx + r * np.cos(t_full), cy + r * np.sin(t_full), color=MORANDI['line'], lw=1.0, ls='--', alpha=0.5, zorder=1) + +# 6. 绘制正弦波波形 +x_wave = np.linspace(0, 2 * np.pi, 250) +y_wave = np.sin(x_wave) +ax.plot(x_wave, y_wave, color=MORANDI['line'], lw=1.8, zorder=2) + +# 填充正弦波对应区间 +x_fill = np.linspace(0, theta, 150) +ax.fill_between(x_fill, 0, np.sin(x_fill), color=MORANDI['wave_fill'], alpha=0.45, zorder=1) + +# 7. 解算关键投影点 +px, py = cx + r * np.cos(theta), r * np.sin(theta) # 圆上旋转质点 P +qx, qy = theta, np.sin(theta) # 波形对应投影点 Q + +# 绘制旋转矢量半径与质点 +ax.plot([cx, px], [cy, py], color=MORANDI['line'], lw=2.0, zorder=3) +ax.plot([px], [py], 'o', color=MORANDI['line'], ms=8, zorder=4) +ax.plot([qx], [qy], 'o', color=MORANDI['line'], ms=8, zorder=4) + +# 绘制跨越空间的主投影虚线 (点 P 投影至点 Q) +ax.plot([px, qx], [py, qy], color=MORANDI['line'], lw=1.2, ls=':', alpha=0.8, zorder=3) + +# 8. 极简英文标注 +ax.text(1.5, 1.4, "How a Sine Wave is Born", fontsize=14, color=MORANDI['text'], weight='bold', ha='center') +ax.text(cx, -1.3, "Unit Circle", fontsize=10.5, color=MORANDI['text'], ha='center') +ax.text(3.14, -1.3, "Sine Wave", fontsize=10.5, color=MORANDI['text'], ha='center') + +# 极小几何符号 +ax.text(cx + 0.3 * np.cos(theta/2), cy + 0.25 * np.sin(theta/2), r"$\theta$", fontsize=11, color=MORANDI['text'], ha='center') +ax.text(qx + 0.1, qy + 0.1, r"$y = \sin\theta$", fontsize=10.5, color=MORANDI['text'], ha='left') + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/note.md" new file mode 100644 index 0000000..468b56e --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\211\213\347\273\230\346\255\243\345\274\246\346\263\242/note.md" @@ -0,0 +1,2 @@ +手绘风格的正弦波产生 +静态图 \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/main.py" new file mode 100644 index 0000000..24820f1 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/main.py" @@ -0,0 +1,171 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.colors import LinearSegmentedColormap +from matplotlib.widgets import Slider, Button + +# 基础环境配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +mpl.rcParams["axes3d.mouserotationstyle"] = "azel" +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 10 +}) + +# Macaron Color Palette +MACARON = { + 'bg_light': '#FDFBF7', + 'wave_low': '#B5EAD7', # Mint pastel + 'wave_mid': '#E8F5E9', # Very light green/white + 'wave_high': '#FFB7B2', # Soft pink + 'line_dark': '#5D6D7E', # Slate gray + 'line_light': '#BDC3C7', # Light gray + 'accent': '#FFDAC1' # Peach pastel +} + +# Create custom colormap for the wave field +colors = [MACARON['wave_low'], MACARON['wave_mid'], MACARON['wave_high']] +macaron_cmap = LinearSegmentedColormap.from_list("macaron_wave", colors, N=256) + +# Grid generation +x = np.linspace(-10, 10, 120) +y = np.linspace(0.1, 15, 120) +X, Y = np.meshgrid(x, y) + +# Physics calculation functions +def calculate_waves(X, Y, d, lam, phase=0.0): + """Calculate the wave displacement field from two coherent sources.""" + k = 2 * np.pi / lam + # Distance from sources S1(-d/2, 0) and S2(d/2, 0) + r1 = np.sqrt((X + d/2)**2 + Y**2 + 0.2) + r2 = np.sqrt((X - d/2)**2 + Y**2 + 0.2) + + # Damped waves to simulate propagation loss + z1 = (np.cos(k * r1 - phase) / np.sqrt(r1)) + z2 = (np.cos(k * r2 - phase) / np.sqrt(r2)) + return z1 + z2 + +def calculate_intensity(x_line, d, lam): + """Calculate the time-averaged intensity at the screen (y = 15).""" + y_screen = 15.0 + r1 = np.sqrt((x_line + d/2)**2 + y_screen**2) + r2 = np.sqrt((x_line - d/2)**2 + y_screen**2) + k = 2 * np.pi / lam + # Intensity distribution combining interference envelope + I = 1/r1 + 1/r2 + 2 / np.sqrt(r1 * r2) * np.cos(k * (r1 - r2)) + return I + +# Initialize parameters +init_d = 4.0 +init_lam = 1.8 +x_screen = np.linspace(-10, 10, 300) + +# Setup Figure +fig = plt.figure(figsize=(12, 6.5), facecolor=MACARON['bg_light']) + +# [Left Subplot]: 3D Wave Interference Field +ax1 = fig.add_axes([0.05, 0.22, 0.48, 0.73], projection='3d', proj_type='ortho') +ax1.set_facecolor(MACARON['bg_light']) +ax1.view_init(elev=35, azim=-60) + +# [Right Subplot]: 2D Screen Intensity +ax2 = fig.add_axes([0.60, 0.26, 0.35, 0.62]) +ax2.set_facecolor(MACARON['bg_light']) +ax2.spines['top'].set_visible(False) +ax2.spines['right'].set_visible(False) +ax2.spines['left'].set_color(MACARON['line_light']) +ax2.spines['bottom'].set_color(MACARON['line_light']) + +# Set Labels +ax1.set_title("双缝干涉与衍射空间波面 ($z$ 轴代表波幅)", pad=10, fontsize=12) +ax2.set_title("接收屏上的光强/波强分布 $I(x)$", pad=15, fontsize=12) +ax2.set_xlabel("位置 $x$", color=MACARON['line_dark']) +ax2.set_ylabel("强度 $I$", color=MACARON['line_dark']) + +# Initial calculations +Z = calculate_waves(X, Y, init_d, init_lam) +I = calculate_intensity(x_screen, init_d, init_lam) + +# 3D Surface Plot +surf = ax1.plot_surface(X, Y, Z, cmap=macaron_cmap, rstride=1, cstride=1, + linewidth=0, antialiased=True, alpha=0.9, shade=True) +ax1.set_zlim(-2.5, 2.5) +ax1.set_xlim(-10, 10) +ax1.set_ylim(0, 15) +ax1.set_axis_off() # Keep it clean + +# Visual decoration: Sources indicators +source_dots, = ax1.plot([-init_d/2, init_d/2], [0, 0], [0, 0], 'o', + color=MACARON['line_dark'], ms=8, label="相干波源") + +# 2D Screen Plot +line2d, = ax2.plot(x_screen, I, color=MACARON['line_dark'], lw=2) +fill_area = ax2.fill_between(x_screen, 0, I, color=MACARON['wave_high'], alpha=0.4) +ax2.set_xlim(-10, 10) +ax2.set_ylim(0, 1.8) + +# UI Control Elements Layout +ax_slider_d = fig.add_axes([0.15, 0.08, 0.30, 0.025]) +ax_slider_lam = fig.add_axes([0.15, 0.03, 0.30, 0.025]) +ax_btn = fig.add_axes([0.80, 0.04, 0.10, 0.05]) + +slider_d = Slider(ax_slider_d, '波源间距 $d$', 1.5, 8.0, valinit=init_d, + color=MACARON['wave_high'], track_color=MACARON['wave_mid']) +slider_lam = Slider(ax_slider_lam, '波长 $\\lambda$', 0.8, 3.5, valinit=init_lam, + color=MACARON['wave_low'], track_color=MACARON['wave_mid']) + +# Style sliders text +slider_d.label.set_color(MACARON['line_dark']) +slider_d.valtext.set_color(MACARON['line_dark']) +slider_lam.label.set_color(MACARON['line_dark']) +slider_lam.valtext.set_color(MACARON['line_dark']) +ax_slider_d.set_facecolor(MACARON['bg_light']) +ax_slider_lam.set_facecolor(MACARON['bg_light']) + +# Style reset button +btn = Button(ax_btn, '重置参数', color=MACARON['wave_mid'], hovercolor=MACARON['wave_low']) +for spine in ax_btn.spines.values(): + spine.set_color(MACARON['line_light']) +btn.label.set_color(MACARON['line_dark']) + +# Update Callback +def update(val): + d = slider_d.val + lam = slider_lam.val + + # Recalculate 3D Wave Field + global surf, fill_area + surf.remove() + new_Z = calculate_waves(X, Y, d, lam) + surf = ax1.plot_surface(X, Y, new_Z, cmap=macaron_cmap, rstride=1, cstride=1, + linewidth=0, antialiased=True, alpha=0.9, shade=True) + + # Update sources positions + source_dots.set_data([-d/2, d/2], [0, 0]) + source_dots.set_3d_properties([0, 0], 'z') + + # Recalculate 2D screen intensity + new_I = calculate_intensity(x_screen, d, lam) + line2d.set_ydata(new_I) + + # Update fill area + fill_area.remove() + fill_area = ax2.fill_between(x_screen, 0, new_I, color=MACARON['wave_high'], alpha=0.4) + + # Rescale 2D Y limit dynamically if needed + ax2.set_ylim(0, max(new_I) * 1.2) + + fig.canvas.draw_idle() + +slider_d.on_changed(update) +slider_lam.on_changed(update) + +def reset(event): + slider_d.reset() + slider_lam.reset() +btn.on_clicked(reset) + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/note.md" new file mode 100644 index 0000000..3fae754 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\263\242\347\232\204\345\271\262\346\266\211\344\270\216\350\241\215\345\260\204/note.md" @@ -0,0 +1,4 @@ +波的干涉与衍射物理实验 +单场景 +马卡龙浅色系 +壮观 \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/design_cards/.placements.json" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/design_cards/.placements.json" new file mode 100644 index 0000000..fe51488 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/design_cards/.placements.json" @@ -0,0 +1 @@ +[] diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/main.py" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/main.py" new file mode 100644 index 0000000..070d900 --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/main.py" @@ -0,0 +1,209 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt +import numpy as np +from matplotlib.colors import LinearSegmentedColormap +from mpl_toolkits.mplot3d.art3d import Line3DCollection +from matplotlib.widgets import Slider, CheckButtons, Button + +# 基础环境配置 +mpl.rcParams['lines.antialiased'] = True +mpl.rcParams['patch.antialiased'] = True +mpl.rcParams["axes3d.mouserotationstyle"] = "azel" +plt.rcParams.update({ + "font.family": "SimHei", + "mathtext.fontset": "cm", + "axes.unicode_minus": False, + "font.size": 10 +}) + +# 马卡龙/莫兰迪配色方案 +MACARON_COLORS = ["#FFB7B2", "#FFDAC1", "#E2F0CB", "#B5EAD7", "#C7CEEA"] +BG_COLOR = "#FDFBF7" # 柔和暖白背景 +TEXT_COLOR = "#4A4A4A" # 深灰文字 +PROJ_COLOR = "#D1D5DB" # 投影线浅灰色 +EQUIL_COLOR_1 = "#FF9AA2" # 稳定点1颜色 +EQUIL_COLOR_2 = "#6C88C4" # 稳定点2颜色 + +# 创建自定义渐变色板 +macaron_cmap = LinearSegmentedColormap.from_list("macaron", MACARON_COLORS, N=256) + +# 2. 数学计算:四阶龙格库塔法(RK4)求解洛伦兹方程 +def solve_lorenz(rho, max_time=35, dt=0.01): + num_steps = int(max_time / dt) + xs = np.zeros(num_steps) + ys = np.zeros(num_steps) + zs = np.zeros(num_steps) + + # 初始状态微小偏移,引导出混沌轨道 + xs[0], ys[0], zs[0] = (1.0, 1.0, 1.0) + + sigma = 10.0 + beta = 8.0 / 3.0 + + for i in range(num_steps - 1): + x, y, z = xs[i], ys[i], zs[i] + + # RK4 步长计算 + k1_x = sigma * (y - x) + k1_y = x * (rho - z) - y + k1_z = x * y - beta * z + + x_h1, y_h1, z_h1 = x + 0.5 * dt * k1_x, y + 0.5 * dt * k1_y, z + 0.5 * dt * k1_z + k2_x = sigma * (y_h1 - x_h1) + k2_y = x_h1 * (rho - z_h1) - y_h1 + k2_z = x_h1 * y_h1 - beta * z_h1 + + x_h2, y_h2, z_h2 = x + 0.5 * dt * k2_x, y + 0.5 * dt * k2_y, z + 0.5 * dt * k2_z + k3_x = sigma * (y_h2 - x_h2) + k3_y = x_h2 * (rho - z_h2) - y_h2 + k3_z = x_h2 * y_h2 - beta * z_h2 + + x_e, y_e, z_e = x + dt * k3_x, y + dt * k3_y, z + dt * k3_z + k4_x = sigma * (y_e - x_e) + k4_y = x_e * (rho - z_e) - y_e + k4_z = x_e * y_e - beta * z_e + + xs[i+1] = x + (dt/6.0) * (k1_x + 2*k2_x + 2*k3_x + k4_x) + ys[i+1] = y + (dt/6.0) * (k1_y + 2*k2_y + 2*k3_y + k4_y) + zs[i+1] = z + (dt/6.0) * (k1_z + 2*k2_z + 2*k3_z + k4_z) + + return xs, ys, zs + +# 计算平衡点(中心点) +def get_equilibrium_points(rho): + beta = 8.0 / 3.0 + if rho <= 1: + return np.array([[0.0, 0.0, 0.0]]) + c = np.sqrt(beta * (rho - 1)) + return np.array([ + [c, c, rho - 1], + [-c, -c, rho - 1] + ]) + +# 3. 初始化画布与三维子图 +fig = plt.figure(figsize=(10, 8), facecolor=BG_COLOR) +ax = fig.add_axes([0.05, 0.18, 0.9, 0.78], projection='3d', proj_type='ortho') +ax.set_facecolor(BG_COLOR) + +# 设置轴线与网格隐藏,以体现极致简约的现代感 +ax.grid(False) +ax.xaxis.line.set_color((1.0, 1.0, 1.0, 0.0)) +ax.yaxis.line.set_color((1.0, 1.0, 1.0, 0.0)) +ax.zaxis.line.set_color((1.0, 1.0, 1.0, 0.0)) +ax.set_xticks([]) +ax.set_yticks([]) +ax.set_zticks([]) + +# 初始计算参数 +init_rho = 28.0 +init_time = 35.0 +xs, ys, zs = solve_lorenz(init_rho, init_time) + +# 4. 创建绘制对象 +# (a) 3D 主轨迹 (使用 Line3DCollection 实现色彩渐变) +points = np.array([xs, ys, zs]).T.reshape(-1, 1, 3) +segments = np.concatenate([points[:-1], points[1:]], axis=1) +lc = Line3DCollection(segments, cmap=macaron_cmap, norm=plt.Normalize(0, len(xs)), linewidth=1.2) +lc.set_array(np.arange(len(xs))) +ax.add_collection3d(lc) + +# (b) 底面二维投影面 (增强空间立体度与艺术感) +z_floor = -5.0 +proj_line, = ax.plot(xs, ys, np.full_like(zs, z_floor), color=PROJ_COLOR, alpha=0.3, linewidth=0.8) + +# (c) 平衡点散点 +eq_pts = get_equilibrium_points(init_rho) +eq_scatter = ax.scatter(eq_pts[:, 0], eq_pts[:, 1], eq_pts[:, 2], + c=[EQUIL_COLOR_1, EQUIL_COLOR_2], s=40, edgecolors=TEXT_COLOR, linewidths=0.8, zorder=10) + +# (d) 动态端点指针 +tip_dot = ax.scatter([xs[-1]], [ys[-1]], [zs[-1]], color="#FF6B6B", s=25, zorder=11) + +# 设置视口边界,确保形态变化时不闪烁 +ax.set_xlim(-25, 25) +ax.set_ylim(-25, 25) +ax.set_zlim(z_floor, 55) +ax.view_init(elev=20, azim=-45) + +# 5. 添加公式与说明文字 +ax.text2D(0.05, 0.92, "洛伦兹吸引子 (Lorenz Attractor)\n" + r"$\frac{\mathrm{d}x}{\mathrm{d}t}=\sigma(y-x), \quad \frac{\mathrm{d}y}{\mathrm{d}t}=x(\rho-z)-y, \quad \frac{\mathrm{d}z}{\mathrm{d}t}=xy-\beta z$", + transform=ax.transAxes, fontsize=12, color=TEXT_COLOR, ha='left', va='top', + bbox=dict(boxstyle="round,pad=0.5", facecolor='white', edgecolor='#E5E7EB', alpha=0.8)) + +# 6. UI 控制组件布局 +ax_rho = fig.add_axes([0.15, 0.10, 0.45, 0.022], facecolor='#EAEAEA') +ax_time = fig.add_axes([0.15, 0.05, 0.45, 0.022], facecolor='#EAEAEA') +ax_chk = fig.add_axes([0.68, 0.04, 0.14, 0.09], facecolor=BG_COLOR) +ax_btn = fig.add_axes([0.85, 0.05, 0.08, 0.06], facecolor=BG_COLOR) + +# 创建滑块与交互按钮 +slider_rho = Slider(ax_rho, r'控制参数 $\rho$', 10.0, 40.0, valinit=init_rho, color="#B5EAD7") +slider_time = Slider(ax_time, r'演化时长 $T$', 5.0, 50.0, valinit=init_time, color="#FFB7B2") + +slider_rho.label.set_color(TEXT_COLOR) +slider_time.label.set_color(TEXT_COLOR) + +chk_box = CheckButtons(ax_chk, ['显示投影', '显示平衡点'], [True, True]) +rects = getattr(chk_box, 'rectangles', [p for p in ax_chk.patches if type(p).__name__ == 'Rectangle']) +for r_patch in rects: + r_patch.set_edgecolor(TEXT_COLOR) + r_patch.set_linewidth(0.8) + +btn_reset = Button(ax_btn, '重置', color='white', hovercolor='#F5F5F5') +for spine in ax_btn.spines.values(): + spine.set_color('#D1D5DB') +btn_reset.label.set_color(TEXT_COLOR) + +# 7. 动态更新逻辑 +def update_plot(val): + rho = slider_rho.val + max_time = slider_time.val + + # 重新求解微分方程 + x_new, y_new, z_new = solve_lorenz(rho, max_time) + + # 更新三维主轨道曲线 + pts = np.array([x_new, y_new, z_new]).T.reshape(-1, 1, 3) + segs = np.concatenate([pts[:-1], pts[1:]], axis=1) + lc.set_segments(segs) + lc.set_array(np.arange(len(x_new))) + + # 更新动点位置 + tip_dot.set_offsets(np.array([[x_new[-1], y_new[-1]]])) + tip_dot.set_3d_properties([z_new[-1]], 'z') + + # 更新地面投影 + proj_line.set_data(x_new, y_new) + proj_line.set_3d_properties(np.full_like(z_new, z_floor), 'z') + + # 更新平衡点 + eq_pts_new = get_equilibrium_points(rho) + eq_scatter.set_offsets(eq_pts_new[:, :2]) + eq_scatter.set_3d_properties(eq_pts_new[:, 2], 'z') + + fig.canvas.draw_idle() + +slider_rho.on_changed(update_plot) +slider_time.on_changed(update_plot) + +# 显隐控制逻辑 +def toggle_visibility(label): + status = chk_box.get_status() + proj_line.set_visible(status[0]) + eq_scatter.set_visible(status[1]) + fig.canvas.draw_idle() + +chk_box.on_clicked(toggle_visibility) + +# 重置按钮逻辑 +def reset_inputs(event): + slider_rho.reset() + slider_time.reset() + if not chk_box.get_status()[0]: + chk_box.set_active(0) + if not chk_box.get_status()[1]: + chk_box.set_active(1) + +btn_reset.on_clicked(reset_inputs) + +plt.show() \ No newline at end of file diff --git "a/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/note.md" "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/note.md" new file mode 100644 index 0000000..275ea9f --- /dev/null +++ "b/Scripts/\345\267\245\344\275\234\345\214\272_01/\346\264\233\344\274\246\345\205\271\345\220\270\345\274\225\345\255\220/note.md" @@ -0,0 +1,4 @@ +洛伦兹力吸引子 +单场景 +马卡龙浅色系 +壮观 \ No newline at end of file diff --git "a/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/.plotkitycat-scenes.json" "b/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/.plotkitycat-scenes.json" new file mode 100644 index 0000000..49d6ed0 --- /dev/null +++ "b/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/.plotkitycat-scenes.json" @@ -0,0 +1,5 @@ +{ + "scenes": [ + "示例·棱锥的平行截面" + ] +} diff --git "a/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/\347\244\272\344\276\213\302\267\346\243\261\351\224\245\347\232\204\345\271\263\350\241\214\346\210\252\351\235\242/main.py" "b/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/\347\244\272\344\276\213\302\267\346\243\261\351\224\245\347\232\204\345\271\263\350\241\214\346\210\252\351\235\242/main.py" new file mode 100644 index 0000000..e69de29 diff --git "a/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/\347\244\272\344\276\213\302\267\346\243\261\351\224\245\347\232\204\345\271\263\350\241\214\346\210\252\351\235\242/note.md" "b/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/\347\244\272\344\276\213\302\267\346\243\261\351\224\245\347\232\204\345\271\263\350\241\214\346\210\252\351\235\242/note.md" new file mode 100644 index 0000000..2e6fae4 --- /dev/null +++ "b/Scripts/\346\226\260\346\211\213\345\274\225\345\257\274/\347\244\272\344\276\213\302\267\346\243\261\351\224\245\347\232\204\345\271\263\350\241\214\346\210\252\351\235\242/note.md" @@ -0,0 +1,27 @@ +# 棱锥的平行截面 + +绘制高中空间几何中的正四棱锥 **S-ABCD**: + +- 底面 ABCD 是正方形,顶点 S 位于底面中心正上方 +- 棱锥使用细线框,侧面保持半透明 +- 用一个平行于底面的平面截棱锥 +- 截面 EFGH 使用醒目但柔和的颜色填充 +- 标注 S、A、B、C、D 和截面顶点 + +添加一个滑块 **截面位置 t**,范围为 0.2 到 0.85。拖动时实时更新截面的位置和大小,并显示: + +**截面边长 / 底面边长 = t** + +**截面面积 / 底面面积 = t²** + +在图形下方放置三个紧凑的小按钮: + +- 「透视」:切换到默认三维视角 +- 「正视」:从正面观察棱锥和截面 +- 「重置」:恢复默认截面位置和视角 + +保留鼠标拖动旋转三维图形的能力。控件保持简洁,不要遮挡图形。 + +右边添加子图,展示截面的面积。 + +> 点击右下角「可视化」,让 AI 把这段笔记变成可交互的空间几何图。 \ No newline at end of file diff --git a/UPDATE_RELEASE.md b/UPDATE_RELEASE.md index 444636f..2ee0636 100644 --- a/UPDATE_RELEASE.md +++ b/UPDATE_RELEASE.md @@ -1,6 +1,9 @@ # PlotKityCat 更新发布说明 -这份文档只描述当前这套最小更新流程: +应用发布与在线更新流程。runtime 重建见 [RUNTIME_BUILD.md](D:/projects/plotkitycat/RUNTIME_BUILD.md)。 +本地开发环境准备见 [DEVELOPMENT.md](D:/projects/plotkitycat/DEVELOPMENT.md)。 + +当前发布模型: - 更新客户端只更新 `exe` - runtime 走整包发布,不走在线更新 @@ -9,11 +12,20 @@ - `stable/manifest.json` - `releases/PlotKityCat-版本号-windows-amd64.exe` -关于 runtime: +更新安装安全模型: + +- 下载阶段校验 HTTPS、文件大小与 SHA-256 +- 安装前再次校验下载文件,并在目标目录生成 Staged EXE +- Installer 完成 Ready Handshake 后,应用通过 Wails Graceful Shutdown 退出 +- EXE 使用 Windows `MoveFileEx` Atomic Replace +- 新进程在 Startup Health Window 内退出时,Installer 自动 Rollback 并重启旧版本 +- `config/updates/state.json` 使用 Atomic File Write,失败后保留重试状态 -- `resources/runtime/runtime.zip` 默认不提交到 Git -- 它应作为 release asset 或其他外部分发制品单独保存 -- 从零重建 runtime 的流程见 [RUNTIME_BUILD.md](D:/projects/plotkitycat/RUNTIME_BUILD.md) +runtime 约定: + +- `resources/runtime/runtime.7z` 作为 release asset 外部分发 +- 当前使用的资产名是 `runtime.7z` +- 从零重建流程见 [RUNTIME_BUILD.md](D:/projects/plotkitycat/RUNTIME_BUILD.md) ## 1. 先改版本号 @@ -25,18 +37,16 @@ ```json { - "appVersion": "0.0.1.9" + "appVersion": "0.0.3.1" } ``` -说明: +相关脚本如果不手动传 `-Version`,都会默认读取这里的 `appVersion`: - `tools/build-versioned-app.ps1` - `tools/prepare-update-release.ps1` - `tools/package-release.ps1` -这三个脚本如果不手动传 `-Version`,都会默认读取这里的 `appVersion`。 - ## 2. 构建应用 exe 在仓库根目录运行: @@ -48,14 +58,17 @@ powershell -ExecutionPolicy Bypass -File .\tools\build-versioned-app.ps1 或者显式指定版本: ```powershell -powershell -ExecutionPolicy Bypass -File .\tools\build-versioned-app.ps1 -Version 0.0.1.9 +powershell -ExecutionPolicy Bypass -File .\tools\build-versioned-app.ps1 -Version 0.0.3.1 ``` 产物: - [build/bin/PlotKityCat.exe](/D:/projects/PlotKityCat/build/bin/PlotKityCat.exe) +- `build/bin/build-metadata.json` + +后续发布脚本会核对 `build-metadata.json` 与目标版本;版本不一致时直接终止,防止旧 EXE 被包装成新版本。 -## 3. 生成给更新服务器用的 exe + manifest +## 3. 生成在线更新产物 运行: @@ -66,7 +79,7 @@ powershell -ExecutionPolicy Bypass -File .\tools\prepare-update-release.ps1 或者: ```powershell -powershell -ExecutionPolicy Bypass -File .\tools\prepare-update-release.ps1 -Version 0.0.1.9 +powershell -ExecutionPolicy Bypass -File .\tools\prepare-update-release.ps1 -Version 0.0.3.1 ``` 产物目录: @@ -75,15 +88,16 @@ powershell -ExecutionPolicy Bypass -File .\tools\prepare-update-release.ps1 -Ver 对应版本目录里会生成: -- `PlotKityCat-0.0.1.9-windows-amd64.exe` +- `PlotKityCat-0.0.3.1-windows-amd64.exe` - `manifest.json` 说明: -- `manifest.json` 里已经自动写好下载地址和 sha256 +- `manifest.json` 会自动写入下载地址和 sha256 +- `manifest.json` 会写入 EXE 字节数,客户端下载时同时校验 - 默认下载地址前缀是 `https://update.5051001.xyz/plotkitycat/releases` -## 4. 生成给用户下载的完整 zip +## 4. 生成完整下载包 运行: @@ -94,21 +108,30 @@ powershell -ExecutionPolicy Bypass -File .\tools\package-release.ps1 或者: ```powershell -powershell -ExecutionPolicy Bypass -File .\tools\package-release.ps1 -Version 0.0.1.9 +powershell -ExecutionPolicy Bypass -File .\tools\package-release.ps1 -Version 0.0.3.1 ``` 产物: -- 便携目录:`build/release/PlotKityCat-v0.0.1.9` -- 压缩包:`build/release/PlotKityCat-v0.0.1.9.zip` +- 便携目录:`build/release/PlotKityCat-v0.0.3.1` +- 压缩包:`build/release/PlotKityCat-v0.0.3.1.zip` 说明: -- 这个 zip 里会包含: - - `PlotKityCat.exe` - - `resources/runtime/runtime.zip` - - `Scripts/` -- 因此发布完整包前,必须先在本地准备好 `resources/runtime/runtime.zip` +- 这个 zip 会包含 `PlotKityCat.exe`、`resources/runtime/runtime.7z`、`resources/runtime/7zip/` 和 `Scripts/` +- 这个 zip 还会包含 `resources/screeningzoom/zoomit.exe` +- 因此发布完整包前,必须先在本地准备好 `resources/runtime/runtime.7z` +- 并且必须准备好 `zoomit.exe`;推荐固定放在 `resources/screeningzoom/zoomit.exe` +- 当前推荐先按 [RUNTIME_BUILD.md](/D:/projects/plotkitycat/RUNTIME_BUILD.md:1) 用裁剪开关重建 runtime,再执行完整打包 +- 在线更新仍只替换 EXE;新增或调整 `Scripts/`、Runtime、ScreeningZoom 时,必须同步发布完整 zip + +`package-release.ps1` 对 `zoomit.exe` 的查找顺序固定为: + +1. `resources/screeningzoom/zoomit.exe` +2. `thirdparty/screeningzoom/build/Release/zoomit.exe` +3. `thirdparty/screeningzoom/build/zoomit.exe` + +如果三处都没有,脚本会直接失败,不再生成缺失放映工具的发布包。 ## 5. 上传到更新服务器 @@ -124,13 +147,13 @@ powershell -ExecutionPolicy Bypass -File .\tools\package-release.ps1 -Version 0. 先上传版本 exe: ```powershell -scp .\build\update\0.0.1.9\PlotKityCat-0.0.1.9-windows-amd64.exe root@149.28.135.102:/var/www/update.5051001.xyz/plotkitycat/releases/ +scp .\build\update\0.0.3.1\PlotKityCat-0.0.3.1-windows-amd64.exe root@149.28.135.102:/var/www/update.5051001.xyz/plotkitycat/releases/ ``` 再上传 manifest: ```powershell -scp .\build\update\0.0.1.9\manifest.json root@149.28.135.102:/var/www/update.5051001.xyz/plotkitycat/stable/manifest.json +scp .\build\update\0.0.3.1\manifest.json root@149.28.135.102:/var/www/update.5051001.xyz/plotkitycat/stable/manifest.json ``` 注意: @@ -149,42 +172,52 @@ curl.exe -I https://update.5051001.xyz/plotkitycat/stable/manifest.json 再验证 exe: ```powershell -curl.exe -I https://update.5051001.xyz/plotkitycat/releases/PlotKityCat-0.0.1.9-windows-amd64.exe +curl.exe -I https://update.5051001.xyz/plotkitycat/releases/PlotKityCat-0.0.3.1-windows-amd64.exe ``` 如果都返回 `200 OK`,说明更新源可用。 ## 7. 最短发布流程 -以后你如果只想记住最短流程,就按这个顺序: +仅记录最短发布顺序: 1. 改 [version.json](/D:/projects/PlotKityCat/version.json) 的 `appVersion` -2. 跑 `.\tools\build-versioned-app.ps1` -3. 跑 `.\tools\prepare-update-release.ps1` -4. 跑 `.\tools\package-release.ps1` +2. 执行 `.\tools\build-versioned-app.ps1` +3. 执行 `.\tools\prepare-update-release.ps1` +4. 执行 `.\tools\package-release.ps1` 5. 上传 `build/update/版本号/` 里的 `exe` 6. 把 `build/update/版本号/manifest.json` 覆盖到服务器 `stable/manifest.json` -## 8. 当前固定约定 +### Updater Bootstrap 发布 + +Updater 修复会从安装了新 Updater 的版本开始生效。旧版本升级到首个修复版本时,安装动作仍由旧 Updater 执行。 + +安全发布顺序: + +1. 首个修复版本优先发布完整 zip +2. 确认用户运行的 EXE 已包含新 Updater +3. 后续版本再验证 Check → Download → Graceful Restart → Health Check → Cleanup +4. 验证成功后更新 `stable/manifest.json` + +## 8. 固定约定 -- 更新 manifest 地址: - - `https://update.5051001.xyz/plotkitycat/stable/manifest.json` -- 更新 exe 文件名格式: - - `PlotKityCat-版本号-windows-amd64.exe` -- 完整发布包目录格式: - - `build/release/PlotKityCat-v版本号` -- 完整发布包 zip 格式: - - `build/release/PlotKityCat-v版本号.zip` +- 更新 manifest 地址:`https://update.5051001.xyz/plotkitycat/stable/manifest.json` +- 更新 exe 文件名格式:`PlotKityCat-版本号-windows-amd64.exe` +- 完整发布包目录格式:`build/release/PlotKityCat-v版本号` +- 完整发布包 zip 格式:`build/release/PlotKityCat-v版本号.zip` +- runtime release 页面格式:`https://github.com/Wing900/PlotKityCat/releases/tag/v版本号` +- runtime 下载地址格式:`https://github.com/Wing900/PlotKityCat/releases/download/v版本号/runtime.7z` +- 当前示例:`https://github.com/Wing900/PlotKityCat/releases/download/v0.0.3.1/runtime.7z` -## 9. 如果只想发完整包,不想更新在线更新 +## 9. 只发完整包,不更新在线更新 只需要: 1. 改 `version.json` -2. 跑 `.\tools\build-versioned-app.ps1` -3. 跑 `.\tools\package-release.ps1` +2. 执行 `.\tools\build-versioned-app.ps1` +3. 执行 `.\tools\package-release.ps1` -这样你会得到完整 zip,但不会更新服务器上的在线更新入口。 +这样会得到完整 zip,但不会更新服务器上的在线更新入口。 ## 10. 常见问题 @@ -202,9 +235,9 @@ curl.exe -I https://update.5051001.xyz/plotkitycat/releases/PlotKityCat-0.0.1.9- 因为 manifest 一更新,旧版本客户端就可能立刻看到新版本;如果这时 exe 还没上传完,下载会失败。 -### Q4. 哪些目录不该进 git? +### Q4. 哪些目录不需纳入 Git? -这些产物目录本来就已经忽略: +以下产物目录已在 `.gitignore` 中排除: - `build/bin/` - `build/release/` diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9c87772..eae46b0 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,14 +14,48 @@ "@codemirror/language": "6.12.3", "@codemirror/state": "6.6.0", "@codemirror/view": "6.43.0", + "@rive-app/canvas": "^2.38.5", + "driver.js": "^1.8.0", "markdown-it": "^14.1.1", "markdown-it-mathjax3": "^5.2.0", "vue": "^3.5.13" }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", + "@vitest/coverage-v8": "^2.1.9", + "@vue/test-utils": "^2.4.11", + "jsdom": "^25.0.1", "typescript": "^5.8.3", - "vite": "^6.3.5" + "vite": "^6.3.5", + "vitest": "^2.1.9" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" } }, "node_modules/@babel/helper-string-parser": { @@ -70,6 +104,13 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@codemirror/autocomplete": { "version": "6.20.2", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.2.tgz", @@ -142,6 +183,121 @@ "w3c-keyname": "^2.2.4" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -584,12 +740,72 @@ "node": ">=18" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/@lezer/common": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", @@ -631,6 +847,30 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rive-app/canvas": { + "version": "2.38.5", + "resolved": "https://registry.npmjs.org/@rive-app/canvas/-/canvas-2.38.5.tgz", + "integrity": "sha512-S3W6w5D9m2JWD3IKmDYfOKppyVf197QSL4OyuIBiYOvwY552Gzu5ct+6vEevLCAXKhqiY2YuZIEMTXeUGOC5xg==", + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.3", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", @@ -1152,6 +1392,125 @@ "vue": "^3.2.25" } }, + "node_modules/@vitest/coverage-v8": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.9.tgz", + "integrity": "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.7", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.12", + "magicast": "^0.3.5", + "std-env": "^3.8.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "2.1.9", + "vitest": "2.1.9" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vue/compiler-core": { "version": "3.5.34", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.34.tgz", @@ -1252,285 +1611,1332 @@ "integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==", "license": "MIT" }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" + "node_modules/@vue/test-utils": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.11.tgz", + "integrity": "sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^3.0.0" + }, + "peerDependencies": { + "@vue/compiler-dom": "3.x", + "@vue/server-renderer": "3.x", + "vue": "3.x" + }, + "peerDependenciesMeta": { + "@vue/server-renderer": { + "optional": true + } + } }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "license": "MIT" + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "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/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } }, - "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", + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12" + "node": ">=12" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=18" + "node": ">=12" }, - "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" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "node": ">=12" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=8" } }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, "license": "MIT", "dependencies": { - "uc.micro": "^2.0.0" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "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==", + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" + "color-name": "~1.1.4" }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/markdown-it-mathjax3": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/markdown-it-mathjax3/-/markdown-it-mathjax3-5.2.0.tgz", - "integrity": "sha512-R+XAy5/7vSGuhG9Z0/cJm6zKxOzStcScfSKVwoarh4nBra+v1KClvbALr/xFTEe9iQhwfQM4SJnO68LXL+btMA==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "license": "MIT", "dependencies": { - "@se-oss/deasync": "^1.0.1", - "mathxyjax3": "^0.8.3" + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/markdown-it/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": ">=14" } }, - "node_modules/mathxyjax3": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/mathxyjax3/-/mathxyjax3-0.8.3.tgz", - "integrity": "sha512-eXjFaiyQsTdVOeTFoFaFJ/r1FITpB1f9c5MW4FETfcoVV/+xa5SD9pS05AwugzL/gNuDtWXrTOSmoD2e0Du+UA==", - "license": "MIT" + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "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" - } - ], + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">= 8" } }, - "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==", + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dev": true, "license": "MIT", - "peer": true, + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "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/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=18" } }, - "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", - "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" - } - ], + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "ms": "^2.1.3" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/rollup": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", - "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/driver.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/driver.js/-/driver.js-1.8.0.tgz", + "integrity": "sha512-+8/IO7h1v14IzWh2GP60N7T3PFZweXwdn5e5POuxRSBoCYUojsBxzqawPeXh3YZIibRy7EehYNEyxe7slwwtdg==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/editorconfig": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz", + "integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "^9.0.1", + "semver": "^7.5.3" }, "bin": { - "rollup": "dist/bin/rollup" + "editorconfig": "bin/editorconfig" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.3", - "@rollup/rollup-android-arm64": "4.60.3", - "@rollup/rollup-darwin-arm64": "4.60.3", - "@rollup/rollup-darwin-x64": "4.60.3", - "@rollup/rollup-freebsd-arm64": "4.60.3", - "@rollup/rollup-freebsd-x64": "4.60.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "node": ">=14" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "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/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/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/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "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/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz", + "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "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/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-it": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", + "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-mathjax3": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/markdown-it-mathjax3/-/markdown-it-mathjax3-5.2.0.tgz", + "integrity": "sha512-R+XAy5/7vSGuhG9Z0/cJm6zKxOzStcScfSKVwoarh4nBra+v1KClvbALr/xFTEe9iQhwfQM4SJnO68LXL+btMA==", + "license": "MIT", + "dependencies": { + "@se-oss/deasync": "^1.0.1", + "mathxyjax3": "^0.8.3" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathxyjax3": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/mathxyjax3/-/mathxyjax3-0.8.3.tgz", + "integrity": "sha512-eXjFaiyQsTdVOeTFoFaFJ/r1FITpB1f9c5MW4FETfcoVV/+xa5SD9pS05AwugzL/gNuDtWXrTOSmoD2e0Du+UA==", + "license": "MIT" + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "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/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.24", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz", + "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==", + "dev": true, + "license": "MIT" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "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/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", "@rollup/rollup-linux-arm-musleabihf": "4.60.3", "@rollup/rollup-linux-arm64-gnu": "4.60.3", "@rollup/rollup-linux-arm64-musl": "4.60.3", @@ -1552,91 +2958,1572 @@ "fsevents": "~2.3.2" } }, - "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", + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.2.tgz", + "integrity": "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^10.2.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "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/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "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/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite-node/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.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", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/style-mod": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", - "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=12" } }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=16" + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "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", - "peer": true, + "node_modules/vitest/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=14.17" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "license": "MIT" + "node_modules/vitest/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/vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", - "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "node_modules/vitest/node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "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" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -1645,25 +4532,19 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "jiti": { - "optional": true - }, "less": { "optional": true }, @@ -1684,12 +4565,6 @@ }, "terser": { "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true } } }, @@ -1698,7 +4573,6 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.34.tgz", "integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==", "license": "MIT", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.34", "@vue/compiler-sfc": "3.5.34", @@ -1715,11 +4589,249 @@ } } }, + "node_modules/vue-component-type-helpers": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-3.3.7.tgz", + "integrity": "sha512-Skkhw9agYSgsWqv7bxSOGJZa9SaiJbZVGdXuFWnrzKaQYHnw9qbjD630rw6RyMqDbp54nfLCLw5SZA55if7JLg==", + "dev": true, + "license": "MIT" + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" } } } diff --git a/frontend/package.json b/frontend/package.json index 9b62556..8bb31b1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,10 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "test": "vitest", + "test:run": "vitest run", + "test:coverage": "vitest run --coverage" }, "dependencies": { "@codemirror/autocomplete": "6.20.2", @@ -15,13 +18,19 @@ "@codemirror/language": "6.12.3", "@codemirror/state": "6.6.0", "@codemirror/view": "6.43.0", + "@rive-app/canvas": "^2.38.5", + "driver.js": "^1.8.0", "markdown-it": "^14.1.1", "markdown-it-mathjax3": "^5.2.0", "vue": "^3.5.13" }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.1", + "@vitest/coverage-v8": "^2.1.9", + "@vue/test-utils": "^2.4.11", + "jsdom": "^25.0.1", "typescript": "^5.8.3", - "vite": "^6.3.5" + "vite": "^6.3.5", + "vitest": "^2.1.9" } } diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index 4dc9eb3..f17312a 100644 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -6125ef81ac1910be6da7753d41e748ae \ No newline at end of file +ce470ae0ef5ef3392c863e680de02191 \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue index d4ccee1..d8f6ed1 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,36 +1,172 @@ diff --git a/frontend/src/assets/fonts/ChillRoundFBold.otf b/frontend/src/assets/fonts/ChillRoundFBold.otf new file mode 100644 index 0000000..b391f60 Binary files /dev/null and b/frontend/src/assets/fonts/ChillRoundFBold.otf differ diff --git a/frontend/src/assets/fonts/ChillRoundFRegular.otf b/frontend/src/assets/fonts/ChillRoundFRegular.otf new file mode 100644 index 0000000..b3cf459 Binary files /dev/null and b/frontend/src/assets/fonts/ChillRoundFRegular.otf differ diff --git a/frontend/src/assets/fonts/Fredoka-Variable.ttf b/frontend/src/assets/fonts/Fredoka-Variable.ttf new file mode 100644 index 0000000..349b6ec Binary files /dev/null and b/frontend/src/assets/fonts/Fredoka-Variable.ttf differ diff --git a/frontend/src/assets/fonts/LICENSE-ChillRoundF.txt b/frontend/src/assets/fonts/LICENSE-ChillRoundF.txt new file mode 100644 index 0000000..2085722 --- /dev/null +++ b/frontend/src/assets/fonts/LICENSE-ChillRoundF.txt @@ -0,0 +1,93 @@ +© 2023 ChillType, with Reserved Font Name 'ChillRoundF' 'ChillRoundM'. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/frontend/src/assets/fonts/LICENSE-Fredoka.txt b/frontend/src/assets/fonts/LICENSE-Fredoka.txt new file mode 100644 index 0000000..791a98d --- /dev/null +++ b/frontend/src/assets/fonts/LICENSE-Fredoka.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Fredoka Project Authors (https://github.com/hafontia/Fredoka-One) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/frontend/src/assets/loading/logo-loading-black.svg b/frontend/src/assets/loading/logo-loading-black.svg new file mode 100644 index 0000000..a49af9f --- /dev/null +++ b/frontend/src/assets/loading/logo-loading-black.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/loading/logo-loading-cyan.svg b/frontend/src/assets/loading/logo-loading-cyan.svg new file mode 100644 index 0000000..702f1de --- /dev/null +++ b/frontend/src/assets/loading/logo-loading-cyan.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/loading/logo-loading-moon.svg b/frontend/src/assets/loading/logo-loading-moon.svg new file mode 100644 index 0000000..aee1610 --- /dev/null +++ b/frontend/src/assets/loading/logo-loading-moon.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/loading/logo-loading-warm.svg b/frontend/src/assets/loading/logo-loading-warm.svg new file mode 100644 index 0000000..658438b --- /dev/null +++ b/frontend/src/assets/loading/logo-loading-warm.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/rive/plotkitycat.riv b/frontend/src/assets/rive/plotkitycat.riv new file mode 100644 index 0000000..5428092 Binary files /dev/null and b/frontend/src/assets/rive/plotkitycat.riv differ diff --git a/frontend/src/components/EnvironmentIndicator.vue b/frontend/src/components/EnvironmentIndicator.vue deleted file mode 100644 index c7190eb..0000000 --- a/frontend/src/components/EnvironmentIndicator.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/frontend/src/components/RuntimeLoadingScreen.vue b/frontend/src/components/RuntimeLoadingScreen.vue index 895af20..34a5348 100644 --- a/frontend/src/components/RuntimeLoadingScreen.vue +++ b/frontend/src/components/RuntimeLoadingScreen.vue @@ -1,25 +1,141 @@