diff --git a/.gitignore b/.gitignore index 50930bdd3..f4ec5c581 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ scripts/* scripts/regression/* !scripts/regression/validate_sdf_cli_truth.ts !scripts/regression/run_shadow_hand_hover_regression.mjs +!scripts/regression/validate_unitree_ros_usd_export_benchmark.mjs !scripts/mujoco/ scripts/mujoco/* !scripts/mujoco/mjcf_compare.ts @@ -22,6 +23,7 @@ scripts/mujoco/* !scripts/rewrite_dts_aliases.mjs !scripts/omx_tmux_grid.sh !scripts/omx_tmux_grid.test.mjs +!scripts/start_dev_server.mjs !scripts/versioning/ scripts/versioning/* !scripts/versioning/bump.mjs @@ -34,6 +36,7 @@ scripts/versioning/* output/ tmp/ .playwright-mcp/ +log/COLCON_IGNORE log/latest log/latest_* log/list_*/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..4b2742dfd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,843 @@ +# URDF-Studio Agent Guide + +本文件是仓库内 AI Coding Agent 的统一执行规范,基于仓库当前结构(核对日期:2026-04-06)整理。 + +## 1. 目标与范围 + +- 项目:URDF Studio(机器人设计、装配、可视化与导出工作台) +- 技术栈:React 19.2 + TypeScript 5.8 + Three.js/R3F + Vite 6.2 + Tailwind CSS 4.1 + Zustand 5 +- 核心能力: + - 单模式 `Editor` 编辑(统一承载拓扑、几何、碰撞、测量与硬件配置) + - 多 URDF 组装、桥接关节与工作区文件管理 + - `URDF` / `MJCF` / `SDF` / `USD` / `Xacro` / `ZIP` / `.usp` 导入导出 + - AI 生成、AI 审阅、PDF/CSV 报告 + - 可复用 `react-robot-canvas` 画布封装与对外发布 + +适用任务: + +- 代码实现与重构 +- UI/交互改造 +- 3D/R3F/Three.js/URDF/USD 相关功能扩展 +- Workspace / import-export / hydration / roundtrip 流程开发 +- AI 审阅、提示词上下文与报告导出相关开发 + +## 2. Prompt 文档来源(Source of Truth) + +建议按“单主文档 + 按需补充”读取: + +- 主 Source of Truth(默认先读): + - `docs/prompts/CLAUDE.md`:项目架构、目录职责、依赖规则、Viewer/样式约束 +- 轻量补充入口(仅在任务相关时读取): + - `docs/prompts/urdf-viewer.md` + - `docs/prompts/URDF_STUDIO_STYLE_GUIDE.md` + - `docs/prompts/overview.md` +- 非 prompts 补充文档(边界/发布/runtime 审计任务时读取): + - `docs/architecture-boundaries.md` + - `docs/robot-canvas-lib.md` + - `docs/runtime-fallback-audit.md` +- 若文档描述与当前 `src/` 真实结构冲突,以仓库现状为准,并优先回补 `CLAUDE.md` + +AI 审阅标准输入(以仓库真实路径为准): + +- `src/features/ai-assistant/config/urdf_inspect_standard_en.md` +- `src/features/ai-assistant/config/urdf_inspect_stantard_zh.md` + +说明: + +- 中文文件名当前仍是 `stantard`(仓库现状),不要擅自改名,除非任务明确要求修复命名。 + +## 2.5 本机 ROS 2 环境(2026-03-27 已核实) + +- 本机已安装 ROS 2 Humble,`ros2` 实际路径为 `/opt/ros/humble/bin/ros2`。 +- 当前非交互 shell 默认可能未注入 ROS 环境,直接执行 `ros2` 可能出现 `command not found`。 +- 运行 ROS 2 / RViz / URDF 检查相关命令前,优先先执行: + +```bash +source /opt/ros/humble/setup.bash +``` + +- 用户的 `/home/xyk/.bashrc` 当前已包含: + +```bash +source /opt/ros/humble/setup.bash +``` + +- 若 agent 需要核对 ROS 侧真值(如 `ros2`、`rviz2`、URDF 可视化或相关 CLI),不要先假设系统未安装 ROS,应先 source 上述环境后再检查。 + +## 2.6 Skill-first 替代策略(降低 MCP / prompt token 开销) + +默认原则: + +- 若需求本质上是“工作流指导、最佳实践、排障框架、测试套路、设计约束”,优先使用 skill,而不是在 prompt 里显式堆一串 MCP/tool 名称。 +- skill 负责压缩“怎么做”的上下文;只有在确实需要执行外部能力时,才继续调用对应 MCP/tool。 +- 不要把 skill 当成 capability replacement。skill 能替代的是提示词和决策开销,不是浏览器点击、远程 API、Figma 读取、`.pen` 编辑这类真实执行能力本身。 + +本仓库优先替代映射: + +- 浏览器验证 / 页面联调 / 截图: + - 优先 `webapp-testing`、`playwright`、`browser-automation` + - 营销或文档截图优先 `screenshots` + - 仅当需要真实浏览器交互、DOM 快照、网络面板或 DevTools 级检查时,再使用 Playwright / Chrome DevTools MCP +- 3D / R3F / Three.js / WebGL: + - 优先 `threejs-skills` + - 不要在 prompt 中重复展开 Three.js 基础套路 +- URDF Studio UI 改造: + - 优先 `urdf-studio-style` + - 通用前端视觉与交互再补 `frontend-design` 或 `ui-ux-pro-max` +- 调试 / 问题定位: + - 优先 `systematic-debugging`、`debugger` + - 不要一开始就堆浏览器 MCP、trace MCP、console MCP;先按调试 workflow 收敛问题 +- 测试 / QA: + - 优先 `testing-qa` + - 细分场景再补 `javascript-testing-patterns`、`e2e-testing-patterns`、`webapp-testing` +- 库文档 / 最新框架资料: + - 优先 `context7-auto-research` + - OpenAI 相关优先 `openai-docs` + - 仅当 skill 无法覆盖时,再直接使用 Context7 / Web 搜索类工具 +- 代码审阅 / 风险扫描: + - 优先 `requesting-code-review`、`find-bugs`、`code-reviewer` +- 泛搜索 / 调研: + - 优先 `search-specialist` + - 深度外部调研再补 `exa-search` 或 `deep-research` + +使用约束: + +- 同一任务优先选择 1 个主 skill;只有主 skill 明显不足时,再补 1 到 2 个辅助 skill。 +- 不要为了“保险”同时声明多个重叠 skill,例如浏览器任务同时堆 `playwright`、`playwright-skill`、`browser-automation`、`webapp-testing`。 +- 若任务已经能被 repo 内现成脚本、测试、build 命令完成,优先本地命令;不要为了形式统一改走 MCP。 +- 若任务要求真实外部状态读取或修改,例如浏览器点击、Figma 节点读取、在线搜索、设计文件写入、MCP 服务侧资源查询,则 skill 不能单独替代该能力。 + +## 3. 当前结构快照(2026-04-06) + +顶层目录(核心): + +- `src/`:业务源码 +- `docs/`:Agent 上下文、架构边界、runtime 审计与对外库说明 +- `packages/react-robot-canvas/`:对外发布包工作区 +- `public/`:静态资源(字体、logo、Monaco、USD bindings 等) +- `scripts/`:辅助脚本;高频子树包括 `regression/`、`versioning/`、`mujoco/`、`codex_key_router/` +- `log/`:本地运行日志与排障输出 +- `.tmp/`:脚本/构建使用的临时 scratch 目录 +- `output/`:导出结果与用户可见验证产物 +- `tmp/`:浏览器截图、trace、临时调试与中间产物 +- `test/`:外部工程镜像、浏览器回归样本与大型 fixtures +- `dist/`:构建产物 +- `.worktrees/`:本地 git worktree 隔离工作区 + +`src/` 一级模块: + +- `app/`:应用编排层;负责 App shell、viewer 组合、导入导出流程、workspace/source sync、USD hydration/roundtrip、document loading 与 worker handoff 协调;当前重点子树为 `components/unified-viewer/*`、`components/header/*`、`components/settings/*`、`hooks/file-export/*` +- `features/`:业务功能层 + - `ai-assistant/` + - `assembly/` + - `code-editor/` + - `editor/` + - `file-io/` + - `hardware-config/` + - `property-editor/` + - `robot-tree/` + - `urdf-viewer/` +- `store/`:Zustand 状态层 +- `shared/`:共享组件、3D 基础设施、hooks、i18n、数据、调试桥接、workers、通用工具 +- `core/`:纯逻辑、解析器、robot core、mesh loaders、parse workers、runtime diagnostics +- `lib/`:面向外部复用的 `RobotCanvas` 封装、类型与样式入口 +- `styles/`:全局样式与语义 token +- `types/`:跨模块类型定义 + +补充说明: + +- `src/app/components/unified-viewer/*` 是统一 viewer 的当前热区,承载 mode module loading、scene root、overlay、raycast interactivity 与 joints panel 适配。 +- `src/app/hooks/file-export/*` 是应用级导出 workflow 的辅助子树;`useFileExport.ts` 作为入口编排这些 helper。 +- `src/app/components/header/*`、`src/app/components/settings/*` 已独立成子树,Header/Settings 不再全部堆在单文件里。 +- `src/features/urdf-viewer/runtime/*` 现已细分为 `embed/`、`hydra/`、`viewer/`、`types/`、`vendor/`,不要只把它理解成单一 runtime 目录。 +- `shared/data/` 已存在,承载 inspection criteria、motor library 等共享静态数据。 +- `shared/data/*` 是共享静态数据 canonical source;`features/hardware-config/data/motorLibrary.ts` 等同名文件当前仅作 re-export / 兼容层使用。 +- `shared/debug/` 已存在,承载 regression/debug bridge,不要把调试桥接逻辑散落到业务组件里。 +- `shared/workers/closedLoopMotionPreview.worker.ts` 当前承载共享闭环运动预览 worker;需要跨 viewer 复用的 worker 优先收口到 `shared/workers/`。 +- `core/loaders/workers/*` 当前承载 `collada` / `obj` / `stl` 解析 worker;mesh 解析不要重新在 feature 层复制一套 worker 管线。 +- `app/components/` 现已细分出 `header/*`、`settings/*`、`unified-viewer/*` 等子树;新增 App 级 UI 逻辑优先先落到对应子目录。 +- `app/hooks/file-export/*` 当前承载导出进度、assembly history、project export、USD export 辅助逻辑;不要再把这些 helper 回塞进 `useFileExport.ts` 单文件。 +- `features/robot-tree/components/` 当前已拆出 `tree-editor/*` 与 `tree-node/*` 子树;树结构相关 UI 优先按这两个方向继续拆分。 +- `features/property-editor/utils/geometry-conversion/*` 已作为几何转换细分子树存在;几何转换逻辑不要再全部堆回单一 util 文件。 +- `shared/components/3d/workspace/*` 已作为共享画布宿主子树存在;应用层 `WorkspaceCanvas` 主要负责组合,而底层 WebGL 清理与错误边界更多在共享层维护。 +- `src/lib/` 当前已细分 `components/` 与 `hooks/`,对外封装不再只有单一入口文件。 +- 单元测试当前主要采用源码邻近放置(`src/**/*.test.*`);`test/` 不再是唯一测试入口。 +- `test/` 当前重点样本集包括 `test/unitree_model`、`test/gazebo_models`、`test/awesome_robot_descriptions_repos`、`test/usd-viewer`。 +- 当前已存在多个 worker 子树:`src/app/workers`、`src/features/code-editor/workers`、`src/features/file-io/workers`、`src/features/property-editor/workers`、`src/features/urdf-viewer/workers`、`src/core/loaders/workers`、`src/shared/workers`。 + +## 4. 架构红线(必须遵守) + +应用运行时代码必须保持“只向下依赖”的单向结构: + +`app -> features -> store -> shared -> core -> types` + +按层约束理解为: + +- `app` 可以编排 `features/store/shared/core/types`,但不能把业务细节反向塞回下层。 +- `features` 可以依赖 `store/shared/core/types`,禁止依赖 `app`。 +- `store` 与 `shared` 不应新增对 `features` 的运行时依赖。 +- `core` 保持纯函数,不引入 React/UI/Feature 依赖。 +- `types` 只提供类型与常量,不回指上层。 +- 使用 `@/` 路径别名指向 `src/`。 + +`src/lib/` 与 `packages/react-robot-canvas/` 约束: + +- `src/lib/` 视为对外复用封装层,只收稳定、通用、与应用壳无关的能力。 +- 应用内部不要把 `src/lib/` 当业务逻辑 source of truth。 +- 若能力仍强依赖 `robotStore`、workspace、app overlays 或特定业务流程,不要急于抽进 `src/lib/`。 + +当前存量例外(运行时代码,仅记录,禁止扩散): + +- `src/shared/components/Panel/JointControlItem.tsx` 依赖 `@/store/robotStore` +- `src/shared/hooks/useEffectiveTheme.ts` 依赖 `@/store/uiStore` +- `src/features/ai-assistant/utils/pdfExport.ts` 依赖 `@/features/file-io/components/InspectionReportTemplate` + +当前测试期例外(仅测试,不作为运行时先例): + +- `src/features/file-io/utils/usdFloatingRoundtrip.test.ts` 依赖 `urdf-viewer` 的 runtime/utils 做 roundtrip 验证 +- `src/features/file-io/utils/usdGo2Roundtrip.test.ts` 依赖 `urdf-viewer` 的 runtime/utils 做 roundtrip 验证 + +## 4.5 调试优先:默认少兜底 + +本仓库默认采用“debuggability first”原则:兜底不是默认美德,很多 silent fallback 会掩盖真实问题、污染状态并拉高排障成本。 + +必须遵循: + +- 默认优先暴露真实错误,不要为了“看起来还能跑”就吞错、改写异常或偷偷切到备用路径。 +- 禁止新增 `catch -> 返回空值/默认值/旧缓存/伪成功状态` 这一类 silent fallback,除非任务明确要求保活且能证明收益大于调试损失。 +- 禁止在导入、导出、hydration、roundtrip、解析、viewer 初始化这类 source-of-truth 链路里做不透明兜底;这些链路一旦异常,优先报出原始错误和上下文。 +- 导入准备、robot import、USD stage preparation、hydration、prepared export、archive/roundtrip 这类 worker bridge / off-main-thread 链路,默认必须 **fail fast**;不要因为 worker 不可用、worker 初始化失败或 postMessage 失败,就在同一调用路径里悄悄改走主线程实现。 +- 禁止用“自动重试 + 自动降级 + 自动切换备用实现”掩盖根因,尤其是格式解析、资源加载、3D runtime 初始化与 store 同步流程。 +- 若确实必须保留窄兜底,必须同时满足: + - 保留原始错误信息、栈与触发条件,不能吞掉; + - 能被用户或开发者明确观察到,例如 console/error state/debug panel 中可见; + - 不得悄悄改写 source of truth,不得制造“表面成功、数据已偏”的假象; + - 需要在注释或实现附近说明为何必须兜底,以及主路径失败时实际降级到什么。 + +实现倾向: + +- 优先选择 fail fast、显式 error state、显式禁用某按钮/面板,也不要返回一个看似正常但不可 debug 的假结果。 +- 如果要保活 UI,优先把失败隔离在边界处,并把错误显式透传到调用方或调试通道,而不是在底层 util 里悄悄吃掉。 +- 调试期发现历史遗留 silent fallback 时,可以顺手收紧,但前提是不破坏本次任务边界。 + +错误处理补充约束: + +- 不要静默吞掉错误(空 catch)。 +- 错误信息应包含足够上下文,至少覆盖触发阶段、关键输入和模块来源,便于快速定位。 +- 可恢复错误(如网络超时)优先采用有限重试;默认不做自动降级。确需降级时,必须满足上文“窄兜底”全部条件并明确说明原因。 +- 不可恢复错误应尽早失败并上抛,由上层边界统一处理与展示。 + +内存 / 生命周期补充约束: + +- 每次新增或修改 `ResizeObserver`、全局事件监听、RAF、`setTimeout` / `setInterval`、worker listener、`ImageBitmap`、object URL、THREE 材质/几何体/纹理、OffscreenCanvas/runtime owner 时,必须同时检查并实现对称 cleanup。 +- shared worker / singleton runtime 若为了复用而常驻,必须明确其所有者和释放边界;不能以“下次可能复用”为理由保留已经失效的 scene graph、driver、observer、message listener 或 pending request。 +- 新增 shared worker / singleton runtime 时,代码评审里必须能直接指出对应的 `dispose*` / `reset*` / owner teardown 调用点;如果说不清谁负责释放,就不要引入该常驻对象。 +- 临时调试缓存、prepared snapshot、context map 必须有上限、淘汰策略或显式 dispose/reset 路径;不要默认无限增长。 +- 做 memory / fallback 审计时,优先检查:worker bridge 的 `pendingRequests` / `workerUnavailable` 分支、`addEventListener/removeEventListener` 是否成对、`ResizeObserver.disconnect()`、`URL.revokeObjectURL()`、`ImageBitmap.close()`、以及 THREE 资源释放是否覆盖材质/几何体/纹理/renderer。 + +## 4.6 设计哲学补充:符合 Linux 哲学与 Linus taste + +适用范围: + +- 所有新增代码、重构、API 设计、状态流调整、worker bridge、viewer/runtime 适配层 + +优先级: + +- 这是一级工程约束,不是“风格建议”。当“模式统一”“抽象好看”“先包一层再说”和 Linux 哲学 / Linus taste 冲突时,默认前者让路。 + +默认工程取向: + +- 优先简单直接的数据流与控制流,不为“理论优雅”引入额外抽象层。 +- 优先解决真实问题,不为未来也许会出现的场景预埋复杂框架。 +- 优先把复杂度消灭在设计里,而不是把复杂度包进 helper、manager、factory、coordinator 名字里。 +- 若一个设计需要靠大量注释解释“为什么这样绕”,通常说明设计本身就不够好。 + +必须遵循: + +- 小而清晰的接口优先。函数、hook、store action、worker message shape 都应尽量只表达一件事。 +- 优先组合现有稳定模块,不轻易新增“万能层”“统一抽象层”“Base\*”或过度泛化的中间封装。 +- 数据结构优先于分支堆砌。能通过更好的数据建模消掉 `if/else` 和特殊情况,就不要继续堆条件。 +- 能删除就先删除;能合并特殊情况就先合并;能把例外变成正常数据形态,就不要再保留分支补丁。 +- 命名必须直白,优先用真实语义描述所有权、生命周期和失败路径,避免 `misc`、`manager`、`helper`、`temp` 这类弱语义命名。 +- 禁止把坏状态悄悄修平。出现异常状态时,应优先暴露不变量被破坏的位置,而不是在下游补丁式兼容。 +- 新逻辑默认先问“能不能删掉特殊情况”,再问“要不要新增分支”。 +- 新抽象必须证明自己减少了调用点的整体复杂度;若只是把复杂度移动到别处,则不要抽象。 +- 公共层不得为迁就单一业务场景而污染接口;宁可让特例留在边界层,也不要把整个共享层做脏。 +- 性能优化应以真实热点为目标,避免引入牺牲可读性的投机性缓存、记忆化和多态分发。 +- 错误处理要保留锋利边界。不要因为想让调用方“省心”就吞掉上下文、来源和失败条件。 + +Linus taste 落地检查: + +- 这段代码能不能通过更直接的数据结构或更少的状态把一半逻辑删掉? +- 这个改动是否减少了特殊情况,而不是重新包装特殊情况? +- 这个接口是否能让调用方一眼看懂,而不需要追三层抽象? +- 这个状态/生命周期的 owner 是否唯一且明确? +- 这段逻辑是否在解决真实需求,而不是为了架构好看? +- 如果未来要删掉这段代码,边界是否清楚、代价是否可控? + +明确不鼓励: + +- 为了“模式统一”引入仓库当前并不需要的架构层。 +- 过度 OO、过度继承、过度配置化、过度泛型化。 +- 把复杂交互拆成大量彼此弱关联的小文件,导致阅读路径碎片化。 +- 用 silent fallback、隐式同步、魔法默认值维持表面整洁。 +- 为避免修改旧代码而额外包一层适配器,结果使主路径更难理解。 + +## 5. 单模式开发语义 + +- `Editor`:统一承载运动链拓扑、几何体/材质/纹理、碰撞与测量、电机与硬件参数配置 + +实现新功能前,必须先判断属于 `Editor` 下的哪类子能力,避免跨子系统逻辑耦合。 + +快速映射: + +- 统一公开入口落在 `features/editor` +- 具体实现位于 `features/urdf-viewer`(包含原拓扑/硬件与几何/碰撞/测量能力) +- 跨子域共享交互优先落在 `app` 编排层或 `shared/components/3d` + +## 6. App 编排层(当前重点) + +关键入口: + +- `src/app/App.tsx`:根组件,装配 Providers、懒加载模态框、全局导入导出入口、回归调试桥接 +- `src/app/AppLayout.tsx`:应用壳、Header、TreeEditor、PropertyEditor、UnifiedViewer、workspace/source 同步主编排 +- `src/app/components/UnifiedViewer.tsx`:组合 `Editor` 两个子域场景(拓扑/硬件 + 几何/碰撞/测量),统一 selection/hover/preview/tool mode/resource scope +- `src/app/components/WorkspaceCanvas.tsx`:应用层对共享画布入口的 re-export;底层 `WorkspaceCanvas` runtime 在 `src/shared/components/3d/workspace/*` +- `src/app/components/AppLayoutOverlays.tsx` 与 `src/app/utils/overlayLoaders.ts`:懒加载业务浮层(如 bridge create、collision optimization) +- `src/app/components/ConnectedDocumentLoadingOverlay.tsx` / `src/app/components/DocumentLoadingOverlay.tsx` / `src/app/components/ImportPreparationOverlay.tsx`:导入准备、文档加载与 worker 进度反馈 +- `src/app/components/SnapshotDialog.tsx`:统一快照导出与预览弹层 +- `src/app/components/unified-viewer/*`:统一 viewer 的 scene root、overlay、derived state、mode module loader 与 joints panel 适配 +- `src/app/components/header/*`:Header actions/menus/overflow/toolbox 等子结构;`ToolboxMenu` 为纯渲染器(接收 `items: ToolboxItem[]`),`ToolboxItem` 类型收口在 `header/types.ts` +- `src/app/components/settings/*`:设置面板子页与 about pane + +当前 `app/hooks/*` 职责: + +- `useAppShellState.ts` / `useAppEffects.ts` / `useAppLayoutEffects.ts` / `useAppState.ts`:App shell、mode/panel、副作用与 layout 编排 +- `useViewerOrchestration.ts`:selection / hover / pulse / focus / transform pending 协调 +- `useFileImport.ts`:App 级导入流程;组合 `store + parsers + feature file-io` +- `useFileExport.ts`:App 级导出流程;组合当前 viewer 状态、assembly history、USD roundtrip、project export、archive assets +- `hooks/file-export/*`:导出 workflow 的 helper 子树,当前主要包含 `src/app/hooks/file-export/assemblyHistory.ts`、`src/app/hooks/file-export/progress.ts`、`src/app/hooks/file-export/projectExport.ts`、`src/app/hooks/file-export/usdExport.ts` +- `useWorkspaceSourceSync.ts` / `useWorkspaceMutations.ts` / `useLibraryFileActions.ts`:workspace、source code、assembly、library 文件行为编排 +- `src/app/hooks/useWorkspaceModeTransitions.ts` / `src/app/hooks/useWorkspaceOverlayActions.ts`:workspace 视图切换、浮层动作与 UI 呈现收口 +- `src/app/hooks/usePreparedUsdViewerAssets.ts` / `src/app/hooks/useAnimatedWorkspaceViewerRobotData.ts` / `src/app/hooks/useSourceCodeEditorWarmup.ts`:viewer 资产预热、workspace viewer 动画数据与代码编辑器预热 +- `src/app/hooks/useImportInputBinding.ts`:App 级文件输入绑定与导入入口整合 +- `src/app/hooks/useEditableSourcePatches.ts` / `src/app/hooks/useUnsavedChangesPrompt.ts`:编辑源码 patch 生命周期与离开保护 +- `useCollisionOptimizationWorkflow.ts`:碰撞优化 UI 流程与状态协调 +- `usePendingHistoryCoordinator.ts`:pending history 与 viewer/export 生命周期协同 +- `useToolItems.tsx`:工具箱工具注册表;定义有哪些工具、每个工具怎么打开,返回 `items` 与 `openTool(key)` 统一路由。新增内部工具时**唯一需要改的文件** +- `robotImportWorkerBridge.ts` / `importPreparationWorkerBridge.ts`:App 层 worker bridge,连接导入准备与 robot import worker + +当前 `app/utils/*` 重点: + +- USD/roundtrip/hydration:`usdExportContext.ts`、`usdHydrationPersistence.ts`、`usdStageHydration.ts`、`liveUsdRoundtripExport.ts`、`usdRoundtripExportArchive.ts` +- 导出辅助:`exportArchiveAssets.ts`、`usdBinaryArchive.ts`、`urdfSourceExportUtils.ts`、`currentUsdExportMode.ts` +- 编辑器与显示:`sourceCodeDisplay.ts`、`sourceCodeEditorLoader.ts` +- 历史与缓存:`pendingHistory.ts`、`pendingUsdCache.ts` +- 导入准备 / 文档加载:`documentLoadFlow.ts`、`contentChangeAppMode.ts`、`importPreparation.ts`、`importPreparationTransfer.ts`、`importPackageAssetReferences.ts`、`contextualPreResolvedImports.ts` +- workspace/source:`workspaceGeneratedSourceState.ts`、`workspaceSourceSyncUtils.ts`、`workspaceViewerAnimation.ts`、`workspaceViewerPresentation.ts` +- Unified viewer 切换与 viewport handoff:`unifiedViewerForcedSessionState.ts`、`unifiedViewerHandoffReadyState.ts`、`unifiedViewerLoadReleaseState.ts`、`unifiedViewerOptionsRestore.ts`、`unifiedViewerResourceScopes.ts`、`unifiedViewerSceneMode.ts`、`unifiedViewerSceneProps.ts`、`unifiedViewerViewportState.ts`、`viewerViewportHandoff.ts` +- Worker payload / transfer:`robotImportWorkerPayload.ts`、`importPreparationTransfer.ts`、`usdBinaryArchiveWorkerTransfer.ts` +- App workers:`app/workers/importPreparation.worker.ts`、`app/workers/robotImport.worker.ts`、`app/workers/usdBinaryArchive.worker.ts` + +约束: + +- 只要逻辑横跨多个 store、多个 feature、viewer 当前状态或 hydration/export 生命周期,优先放在 `app`。 +- 单一 feature 内闭环的逻辑不要硬塞进 `app`。 +- `features/file-io` 负责底层文件能力,`app/hooks/useFileImport.ts` / `useFileExport.ts` 才是当前应用工作流 source of truth。 +- `app` 当前会直接 deep import 若干 feature 内部 `utils/*`;新增长期编排能力时,优先通过 feature 的 `index.ts` 或 facade 暴露稳定入口,避免 `app` 继续绑死内部文件布局。 + +## 7. Editor 统一 Viewer 子域要求(`editor` 入口 + `urdf-viewer` 实现) + +本节即该子域的规范入口(不再维护旧的双入口描述)。 + +3D / Three.js / R3F / WebGL 相关任务补充要求: + +- 若任务涉及 3D 场景、Three.js、React Three Fiber、WebGL 或 `Editor` 两个 viewer 子域能力,agent 必须优先使用 `threejs-skills` skill。 +- 在满足本仓库架构约束的前提下,优先复用该 skill 中关于场景组织、材质、动画循环、交互和资源释放的最佳实践。 + +当前结构: + +- `src/features/editor/index.ts` +- `src/features/urdf-viewer/components/*` +- `src/features/urdf-viewer/hooks/*` +- `src/features/urdf-viewer/utils/*` +- `src/features/urdf-viewer/runtime/*` + +关键约束: + +- 扩展功能优先放入 hooks 或新增组件,避免回到独立壳组件形态。 +- 保持 `RobotNode <-> JointNode` 交替递归渲染模式。 +- TransformControls 与拖拽链路依赖的引用注册必须完整可追踪。 +- 材质必须通过 `materials.ts` / `urdfMaterials.ts` 或共享工厂复用,不要在高频路径直接 `new` 材质。 +- 使用 `RobotState` 与现有共享类型,避免 `any`。 +- 与闭环、碰撞拖拽相关的能力优先复用: + - `useViewerController` + - `useMouseInteraction` + - `useHoverDetection` + - `useVisualizationEffects` + +## 8. Editor 几何/碰撞/测量子域要求(`urdf-viewer` 目录) + +对应文档:`docs/prompts/urdf-viewer.md` + +当前结构: + +- React 层: + - `src/features/urdf-viewer/components/*` + - `src/features/urdf-viewer/hooks/*` + - `src/features/urdf-viewer/types.ts` +- 运行时与嵌入层: + - `src/features/urdf-viewer/runtime/embed/*` + - `src/features/urdf-viewer/runtime/hydra/*` + - `src/features/urdf-viewer/runtime/types/*` + - `src/features/urdf-viewer/runtime/vendor/*` + - `src/features/urdf-viewer/runtime/viewer/*` + - `src/features/urdf-viewer/runtime/UPSTREAM.md` +- 工具与适配层: + - `src/features/urdf-viewer/utils/*` +- Worker 层: + - `src/features/urdf-viewer/workers/*` + +关键边界: + +- `runtime/*` 是 vendored usd-viewer runtime,当前来源见 `src/features/urdf-viewer/runtime/UPSTREAM.md`。 +- URDF Studio 应该把 runtime 输出适配到自己的 `ViewerRobotDataResolution` / `RobotData`,不要在 `src/core/parsers/usd/*` 重复实现 viewer runtime 职责。 +- `public/usd/bindings/*` 必须保持可在浏览器运行时 fetch,不要迁入源码模块。 + +关键文件与职责: + +- `UsdWasmStage.tsx`:WASM stage 嵌入入口 +- `UsdOffscreenStage.tsx`:offscreen canvas + worker 模式下的 USD viewer 宿主 +- `src/features/urdf-viewer/components/ViewerCanvas.tsx`:viewer 画布层与共享 canvas 适配 +- `src/features/urdf-viewer/components/ViewerToolbar.tsx`:viewer 顶部工具条 +- `src/features/urdf-viewer/components/ViewerLoadingHud.tsx`:viewer/stage loading 状态 HUD 与反馈层 +- `src/features/urdf-viewer/utils/viewerRobotData.ts`:统一 viewer 层消费的数据形态 +- `src/features/urdf-viewer/utils/viewerResourceScope.ts`:围绕 source file / assets / robot links 构建稳定资源域 +- `src/features/urdf-viewer/utils/usdExportBundle.ts`:USD viewer 场景快照与导出缓存协调 +- `src/features/urdf-viewer/utils/usdRuntimeRobotHydration.ts`:runtime -> RobotData hydration +- `src/features/urdf-viewer/utils/usdSceneRobotResolution.ts`:场景级 robot resolution +- `src/features/urdf-viewer/utils/usdViewerRobotAdapter.ts`:viewer runtime / snapshot 到应用数据的适配 +- `src/features/urdf-viewer/utils/usdOffscreenViewerProtocol.ts`:主线程与 offscreen worker 的协议与消息形态 +- `src/features/urdf-viewer/utils/usdOffscreenViewerWorkerClient.ts`:主线程对 offscreen worker 的请求封装 +- `src/features/urdf-viewer/utils/usdWorkerRendererSupport.ts` / `src/features/urdf-viewer/utils/usdWorkerOrbit.ts`:worker 渲染支持与轨道控制协同 +- `src/features/urdf-viewer/utils/usdStageOpenPreparationWorkerBridge.ts` / `src/features/urdf-viewer/utils/usdPreparedExportCacheWorkerBridge.ts`:prepared-open / prepared-export 链路的 worker bridge +- `src/features/urdf-viewer/utils/runtimeSceneMetadata.ts`:runtime scene metadata 的标准化读模型 +- `src/features/urdf-viewer/utils/visualizationFactories.ts`:辅助可视化对象创建 +- `src/features/urdf-viewer/utils/dispose.ts`:THREE 资源清理 + +关键约束: + +- 优先复用 `utils/materials.ts`、`utils/urdfMaterials.ts`、`utils/dispose.ts`,不要散落临时材质与几何体。 +- Props 与共享类型统一收口到 `types.ts`。 +- 可视化对象新增优先通过 `visualizationFactories.ts`。 +- 卸载、切换、reload、hydration rollback 时必须确认 THREE 资源释放。 +- `JointsPanel` 仍位于 `src/shared/components/Panel/JointsPanel.tsx`,共享逻辑不要回灌到 viewer 私有层。 + +### 8.1 USD worker / metadata 链路约束(2026-03-30) + +适用范围: + +- `src/features/urdf-viewer/runtime/hydra/render-delegate/*` +- `src/features/urdf-viewer/workers/*` +- `src/features/urdf-viewer/utils/usd*` +- `src/app/hooks/useFileImport.ts` / `src/app/hooks/useFileExport.ts` 中会消费 worker 结果的 USD 工作流 + +必须遵循: + +- 涉及 USD stage preparation、runtime metadata extraction、robot hydration、prepared export cache、roundtrip archive 的修复,默认优先放在 `worker/runtime` 链路里完成,不要为了“先跑通”把修复偷偷搬到主线程 adapter 或 debug bridge。 +- `runtime/hydra/render-delegate/*` 产出的 metadata snapshot 是该链路的 source of truth;若 snapshot 缺字段,应修 worker/runtime 生成逻辑,而不是在上层 UI、store 或 regression bridge 做补丁式回填。 +- 禁止新增“worker 结果缺失 -> 主线程重建 metadata -> 静默继续”的 fallback。worker 真失败就应显式暴露,或直接把根因修在 worker。 +- 对 folded fixed link、collision-only semantic child link、synthetic parent-child pair 这类推断,只能基于 stage/truth 中的明确证据推进;不要做纯命名猜测式补 link。 +- 若 stage 原始数据本身不包含目标语义(例如 fixture 里没有对应 child link 证据),应如实暴露这一事实,不要在 UI 层伪造“看起来正确”的 metadata。 + +验证要求: + +- 改动上述链路时,除定向单测外,必须额外跑 `test/unitree_model` 的整套 USD 浏览器批量验证,确认所有主要 Unitree 样本仍满足: + - `loaded = true` + - `stageReady = true` + - `stagePreparationMode = "worker"` + - `metadataSource` 指向 worker/runtime 结果(当前应为 `usd-stage-cpp` 或同类 worker source) +- 浏览器批量验证产物默认写入 `tmp/regression/`,并在最终回复中明确说明结果文件路径。 +- 若 `test/unitree_model` fixture 缺少外部 mesh 资源,允许出现可解释的 mesh lookup error;但这不能作为跳过 worker metadata 验证、跳过 hydration 验证或引入 fallback 的理由。 + +### 8.2 USD offscreen / runtime 生命周期约束(2026-03-30) + +适用范围: + +- `src/features/urdf-viewer/components/UsdOffscreenStage.tsx` +- `src/features/urdf-viewer/workers/usdOffscreenViewer.worker.ts` +- `src/features/urdf-viewer/runtime/hydra/render-delegate/*` +- `src/shared/utils/three/dispose.ts` + +必须遵循: + +- worker/offscreen renderer 的主线程宿主只负责 handoff、尺寸同步与错误透传;不要在主线程重新构建 runtime truth 或默默补 runtime 缺失状态。 +- teardown 必须完整释放 observer、DOM/worker 事件监听、RAF/timer、OffscreenCanvas 关联 runtime、scene graph 与 driver 引用。 +- 若 runtime 注册了全局 handler / registry / active owner,新增能力时必须同时提供对称的 unregister/reset;禁止只注册不解绑。 +- worker 侧若创建了 `ImageBitmap`、object URL、临时 geometry/material/texture,销毁路径必须显式 close/revoke/dispose,不能假设 GC 或 `Texture.dispose()` 会兜底。 +- 任何“reload 后继续复用旧 delegate / 旧 render interface / 旧 stage metadata”的做法都必须有明确生命周期边界,禁止通过全局单例把旧实例挂死。 + +## 9. File I/O、Workspace 与导出链路 + +当前职责拆分: + +- `src/features/file-io/`:格式检测、BOM、project import/export、archive/asset registry、USD/SDF export、ExportDialog/ExportProgressDialog、snapshot/pdf hooks、导入/导出 worker bridge +- `src/app/hooks/useFileImport.ts`:应用级导入工作流 +- `src/app/hooks/useFileExport.ts`:应用级导出工作流 +- `src/app/hooks/file-export/*`:应用导出流程的子模块 helper;当前主要包括 `assemblyHistory.ts`、`progress.ts`、`projectExport.ts`、`usdExport.ts` +- `src/features/robot-tree/`:structure/workspace 文件树、树编辑器、上下文菜单、布局;组件层已拆分为 `tree-editor/*` 与 `tree-node/*` +- `src/features/assembly/`:桥接组件创建与组装入口 +- `src/features/property-editor/`:属性编辑、几何编辑、碰撞优化、`geometry-conversion/*` 与 mesh worker + +当前工作流事实: + +- `features/file-io/hooks/useFileExport.ts` 已移除,应用导出 source of truth 在 `app/hooks/useFileExport.ts`。 +- 应用导入 source of truth 在 `src/app/hooks/useFileImport.ts`;不要在 `features/file-io` 内恢复旧导入 hook。 +- 新增导出辅助逻辑时,优先补到 `app/hooks/file-export/*`,而不是把 `useFileExport.ts` 重新做成大而全单文件。 +- `.usp` project import/export、USD prepared export cache、live USD roundtrip archive 已进入主工作流。 +- `projectArchive.worker.ts`、`usdExport.worker.ts`、`usdBinaryArchive.worker.ts` 已进入主导出链路;涉及大型归档或序列化任务时,优先走现有 worker/transfer 路径。 +- `projectImport.worker.ts` 已进入 project import 链路;项目归档导入相关问题优先在 worker/bridge 修,不要把补丁塞回 UI。 +- `DisconnectedWorkspaceUrdfExportDialog.tsx` 是 workspace 断联 URDF 导出的特例入口;相关逻辑不要重新塞回通用导出弹层。 +- `ExportProgressDialog.tsx` / `ExportProgressView.tsx` 是当前长时导出反馈的统一 UI,不要在业务层重新发明一套导出进度弹层。 +- `output/` 可包含用户可见导出与回归结果;新的临时浏览器验证产物仍默认写入 `tmp/`。 + +多 URDF 组装相关改动,需重点检查: + +- 命名空间前缀冲突规避 +- `BridgeJoint` 连接合法性 +- 合并导出行为一致性 +- workspace 与 structure 视图切换时的 source file / selected file 同步 + +## 10. Shared / Core / Lib 约束 + +`shared/`: + +- `shared/components/3d/*` 是双 viewer 共享的 3D 基础设施 source of truth。 +- 当前已分化出 `scene/*`、`renderers/*`、`helpers/*`、`unified-transform-controls/*` 等子树;涉及画布基础设施时优先先定位到对应子目录,而不是直接在 viewer 组件层搜索。 +- `shared/components/3d/workspace/*` 已承载共享 `WorkspaceCanvas` 宿主、renderer cleanup 与 WebGL 能力检查;App 层画布入口主要做业务编排。 +- 优先复用: + - `ReferenceGrid` + - `GroundShadowPlane` + - `SceneLighting` + - `WorkspaceOrbitControls` + - `UnifiedTransformControls` + - `SnapshotManager` + - `TransformConfirmOverlay` +- `MeshAssetNode.tsx`、`MeshRenderers.tsx` 与 `shared/components/3d/renderers/*` 属于共享 mesh 呈现层;涉及 primitive/object 生命周期时优先在这里集中治理。 +- `GLTFRendererImpl.tsx` 已存在于共享渲染层;不要在 feature 内重新拼 glTF 渲染入口。 +- 通用 THREE 释放优先使用 `shared/utils/three/dispose.ts` 或 viewer 已有 `dispose.ts`。 +- `shared/workers/closedLoopMotionPreview.worker.ts` 是共享 worker,不要把同类预览逻辑复制回 feature 私有 worker。 +- `shared/debug/regressionBridge.ts` 仅做调试/回归桥接,不要让业务层依赖调试 API 才能工作。 +- DAE / OBJ / STL mesh 渲染优先复用 `shared/components/3d/renderers/*`,其底层 mesh/collada 处理依赖 `core/loaders/*`;不要在 feature 组件里重新拼一套 loader + renderer 管线。 + +`core/`: + +- 当前包含 `parsers/`、`loaders/`、`loaders/workers/`、`robot/`、`stl-compressor/`、`utils/`。 +- 维持纯逻辑与可测试性,不引入 React/UI/DOM。 +- Mesh、MJCF、URDF、USD、Xacro 解析器与 robot algebra 统一留在 `core/`。 +- `core/utils/runtimeDiagnostics.ts`、`core/utils/ensureWorkerXmlDomApis.ts` 等运行时辅助也归 `core`,不要把这类纯工具上移到 feature。 + +`lib/`: + +- 当前主要对外暴露 `RobotCanvas`、`lib/types.ts`、`lib/styles.css` 与 `lib/hooks/useControllableState.ts`。 +- 只有确认“与应用壳无关、可稳定发布”的能力才允许抽入 `lib/`。 +- `packages/react-robot-canvas/dist/*` 当前作为包发布产物保留在仓库内;除非任务明确针对发包结果,禁止手改这些文件,统一通过包内 build / `scripts/postbuild.mjs` / prepack 流程生成。 + +## 11. 状态管理与文件职责提示 + +Zustand 关键 Store: + +- `robotStore`:模型 CRUD、Undo/Redo、派生计算、closed loop constraints +- `uiStore`:mode/theme/lang/sidebar/panels/view options 等(含持久化) +- `selectionStore`:选中、悬停、pulse、focus +- `assetsStore`:mesh/texture/robot files/motor library/USD scene snapshot/prepared export cache +- `assemblyStore`:多 URDF 组装、组件管理、BridgeJoint、组装历史 +- `assemblySelectionStore`:workspace 中的组件 / bridge / source file 选区作用域 +- `collisionTransformStore`:碰撞 gizmo 的瞬时 pending transform +- `jointInteractionPreviewStore`:跨 viewer 的关节交互预览与瞬时状态 + +约束: + +- 共享 UI 组件不要直接操纵多个 store;跨 store 协调优先放 `app/hooks/*`。 +- 新增持久化字段前,先确认是否属于 `uiStore` / `assetsStore` 的长期状态,而不是一次性交互缓存。 +- USD hydration / roundtrip 中间态优先落在 `assetsStore` 缓存或 `app/utils/*`,不要在组件局部散落。 + +## 12. UI 风格与可访问性要求 + +对应文档:`docs/prompts/URDF_STUDIO_STYLE_GUIDE.md` + +必须遵循: + +- 使用语义色 token,不散落硬编码色值 +- 组件在 `light + dark + prefers-contrast: more` 下都需可读 +- 暗色层次使用 `base/surface/elevated`,避免纯黑硬切 +- 状态表达不只依赖颜色,补充图标/文本/形态差异 +- Focus 态可见,建议统一 `ring-system-blue/30` + +蓝色强约束: + +- `#0088FF` 仅用于 Slider/进度线/细线型高亮(`slider-accent`) +- `#0088FF` 禁止用于主按钮实底、小字号正文链接、大面积背景 +- 主按钮底色使用 `system-blue-solid`,文本/图标强调用 `system-blue` + +面板文案约束: + +- 常驻工具面板默认使用短标签、短标题、短状态文案;不要为了“解释清楚”塞整段说明。 +- 像测量、吸附、显示开关这类高频操作,优先直接提供可选项,不额外重复解释“该选项是什么”。 +- 只有首次使用门槛高、流程较长或存在明显误操作成本的区域(如 toolbox、批量优化、复杂导入导出流程)才保留简短 helper copy。 +- 若一个面板已经能通过标题、字段名、占位文案和按钮标签表达清楚,就删除冗余说明文本。 + +## 13. 代码变更工作流(建议) + +1. 定位任务所属模式与模块边界。 +2. 明确改动是在 `app` 编排层、单一 `feature`、还是 `shared/core` 通用层。 +3. 检查依赖方向是否符合架构红线,避免新增例外。 +4. 优先复用现有 hooks/utils/components,不重复造轮子。 +5. 保持类型完整性,避免 `any`。 +6. 涉及 3D/USD/mesh 时,检查材质缓存、资源释放、hydration/export 生命周期。 +7. 做最小必要改动并验证。 + +单文件与模块化策略: + +- 默认优先模块化拆分,不把新能力堆到单个大文件。 +- 允许单文件:仅限小改动(文案、样式微调、局部 bug 修复)且不引入新职责。 +- 必须拆分的场景: + - 同时引入“状态 + 视图 + 业务逻辑” + - 新增可复用逻辑(优先抽为 hook/utils) + - 文件已明显过大且继续修改会降低可维护性 +- 对 `Editor` 两个 viewer 子域 / `AppLayout` / `UnifiedViewer` 的改动,优先新增 hooks/components,避免继续增厚主组件。 + +当前明确热点文件(新增逻辑优先抽离,而不是继续增厚): + +- `src/features/property-editor/utils/geometryConversion.ts` +- `src/features/file-io/utils/usdExport.ts` +- `src/features/urdf-viewer/components/UsdWasmStage.tsx` +- `src/features/urdf-viewer/utils/usdExportBundle.ts` +- `src/app/hooks/useFileExport.ts` +- `src/app/AppLayout.tsx` + +## 14. 浏览器/MCP 截图与收尾清理 + +- 启动浏览器时默认使用无头模式(`headless: true` / `--headless`),除非用户明确要求打开可见浏览器窗口。 +- 若任务中使用了 Chrome DevTools、Playwright、浏览器自动化或其他 MCP 工具,浏览器验证产物默认统一写入仓库内 `tmp/`,可按需放在 `tmp/screenshots/`、`tmp/playwright/`、`tmp/chrome-devtools/` 等子目录。 +- 禁止将浏览器截图或其他临时验证产物直接写到仓库根目录。 +- `output/` 仅用于用户可见导出结果、回归归档或明确要保留的产物;临时截图/trace 不要默认写入 `output/`。 +- 浏览器截图前,默认先关闭会遮挡主体画面的侧栏、浮层和调试面板,尤其是 `Editor` 的选项面板。 +- 除非任务明确要求保留 UI 状态,截图应优先提供无遮挡 clean shot;如需展示面板,建议额外补一张无遮挡截图。 +- 完成验证后必须清理残留上下文,关闭不再使用的浏览器标签页、DevTools 面板、Playwright 页面、隔离 context 与临时调试会话。 +- 若测试过程中启动了额外的浏览器实例、调试端口、后台线程、本地预览服务或临时辅助进程,结束前应主动关闭,除非用户明确要求保留。 +- 最终回复中,若保留了任何必须继续运行的进程,需要明确说明用途与访问地址;否则默认应清理干净。 + +## 15. 验收清单 + +- [ ] Light / Dark / 高对比模式下可读性通过 +- [ ] 无新增分散硬编码颜色 +- [ ] 3D 资源无明显泄漏(材质/几何体/纹理释放) +- [ ] worker/offscreen 生命周期完整释放(listener / observer / timer / worker / global handler / ImageBitmap) +- [ ] 新增的 observer / listener / timer / object URL / ImageBitmap / pending request map 均有对称 cleanup 或容量上限 +- [ ] USD hydration / roundtrip / export 未破坏当前 source-of-truth 流程 +- [ ] 浏览器验证产物已放入 `tmp/`,未新增根目录截图/trace +- [ ] 浏览器测试结束后无残留 Chrome DevTools / Playwright 会话、线程或临时进程 +- [ ] 变更符合模块职责,没有破坏依赖方向 +- [ ] 新增依赖未扩大现有架构例外 +- [ ] 未新增 silent fallback / 吞错式兜底,异常路径仍可定位和 debug +- [ ] import/export/hydration 等 source-of-truth worker bridge 在失败时显式报错,没有静默主线程降级 +- [ ] 若改动 USD worker / metadata 链路,已完成 `test/unitree_model` 全量浏览器验证且结果落盘到 `tmp/regression/` +- [ ] 若改动运行时代码,已完成对应测试或构建验证 + +推荐命令: + +```bash +npm run dev +npm run lint +npm run typecheck +npm run test +npm run build +npm run verify:fast +npm run verify:full + +# 快速查看当前模块结构 +find src -maxdepth 3 -type d | sort + +# 查看 app / features 主入口 +find src/app src/features -maxdepth 2 -type f | sort | sed -n '1,240p' + +# 大仓库搜索时默认排除 vendor / 产物 / 临时目录 +rg -n "pattern" src docs scripts packages/react-robot-canvas \ + -g '!test/**' -g '!.tmp/**' -g '!tmp/**' -g '!dist/**' -g '!node_modules/**' + +# 检查潜在反向依赖(重点关注 core/shared/store 对 features 的引用) +rg -n "from ['\"]@/features/" src/core src/shared src/store + +# 检查 feature 间直接耦合(控制存量,不新增) +rg -n "from ['\"]@/features/" src/features + +# 检查 shared 对 store 的依赖(控制存量,不新增) +rg -n "from ['\"]@/store/" src/shared + +# 检查硬编码色值 +rg -n "#[0-9A-Fa-f]{3,8}" src + +# 检查 #0088FF 使用范围 +rg -n "#0088FF|#0088ff" src | rg -v "Slider.tsx|styles/index.css" + +# 仓库级快速验证 +npm run check +npm run test:unit:app-hooks + +# USD worker / metadata 链路回归 +node --test \ + src/features/urdf-viewer/runtime/hydra/render-delegate/robot-metadata-stage-fallback.test.js \ + src/features/urdf-viewer/runtime/hydra/render-delegate/folded-fixed-link-truth.test.js + +npx tsx --test \ + src/features/urdf-viewer/utils/usdViewerRobotAdapter.test.ts \ + src/features/urdf-viewer/utils/usdRuntimeRobotHydration.test.ts + +# Unitree roundtrip / archive 样本验证 +npx tsx scripts/regression/validate_unitree_model_roundtrip_archive.ts + +# 现成 fixture 回归入口 +npm run test:fixtures:imports +npm run test:fixtures:myosuite-imports +npm run test:fixtures:unitree-usd +npm run test:fixtures:unitree-ros-urdfs +npm run test:fixtures:unitree-ros-usda + +# 打包对外库(仅在改到 src/lib 或 packages/react-robot-canvas 时执行) +npm run build:package:react-robot-canvas + +# 额外工具 +bash scripts/inspect_urdf.sh path/to/robot.urdf --compact +npm run codex:retry +npm run codex:key-router:deploy:dry +``` + +说明: + +- 根 `package.json` 当前已提供统一的 `npm run lint`、`npm run test`、`npm run build`、`npm run check`、`npm run verify:fast`、`npm run verify:fixtures`、`npm run verify:full` 脚本;默认优先用这些入口,再按模块补跑定向测试。 + +## 16. 常用 test 样本索引(给 agent 直接复用) + +当任务需要真实模型做导入、viewer、hydration、roundtrip、导出或浏览器回归验证时,优先复用 `test/` 里现成样本,不要临时去外网再找模型。 + +### 16.1 USD / worker / roundtrip 主样本 + +- `test/unitree_model/Go2/usd/go2.usd` + - Unitree 四足基准样本;适合 USD stage open、worker metadata、hydration、viewer smoke test。 +- `test/unitree_model/Go2W/usd/go2w.usd` + - Go2 轮足变体;适合验证变体资产、命名差异与 roundtrip 稳定性。 +- `test/unitree_model/B2/usd/b2.usd` + - 更大体量四足样本;适合检查 stage truth、folded fixed link 与复杂 link/joint 结构。 +- `test/unitree_model/H1-2/h1_2/h1_2.usd` + - Humanoid USD 样本;适合验证双足/人形链路与 viewer hydration。 +- `test/unitree_model/H1-2/h1_2_handless/h1_2_handless.usd` + - H1-2 handless 变体;适合检查 asset/配置差异下的 runtime 行为。 +- `test/unitree_model/B2/usd/b2.viewer_roundtrip.usd` +- `test/unitree_model/Go2/usd/go2.viewer_roundtrip.usd` +- `test/unitree_model/Go2W/usd/go2w.viewer_roundtrip.usd` + - 以上 roundtrip 产物可用于导出后 diff、回归对照与 viewer roundtrip 验证。 + +默认规则: + +- 只要改动 USD worker、runtime metadata、hydration、prepared export cache、roundtrip archive,默认先用 `test/unitree_model`。 +- 不要只拿单个 Go2 过一遍就宣称链路稳定;这类改动默认至少覆盖 `Go2 + B2 + H1-2`。 + +### 16.2 SDF / Gazebo 资产链路样本 + +- `test/gazebo_models/camera/model.sdf` + - 轻量 smoke 样本;适合快速验证 SDF 导入是否整体可用。 +- `test/gazebo_models/cordless_drill/model.sdf` + - 小型对象样本;同时包含 `DAE + STL + texture`,适合 mesh/材质路径回归。 +- `test/gazebo_models/bus_stop/model.sdf` + - 复合场景样本;包含多 mesh、贴图与混合格式,适合资源解析和路径解析回归。 +- `test/gazebo_models/apartment/model.sdf` + - 大场景样本;适合验证大体量静态环境、纹理与 viewer 性能/稳定性。 +- `test/gazebo_models/camera/model-1_2.sdf` +- `test/gazebo_models/camera/model-1_3.sdf` +- `test/gazebo_models/camera/model-1_4.sdf` +- `test/gazebo_models/cordless_drill/model-1_2.sdf` +- `test/gazebo_models/cordless_drill/model-1_3.sdf` +- `test/gazebo_models/cordless_drill/model-1_4.sdf` + - 版本化 SDF 文件可用于验证不同 schema/version 兼容性。 + +### 16.3 URDF / 外部仓库镜像样本 + +- `test/awesome_robot_descriptions_repos/anymal_c_simple_description/urdf/anymal.urdf` + - 纹理与 `DAE` 较完整的四足 URDF;适合常规 URDF viewer/import 回归。 +- `test/awesome_robot_descriptions_repos/mini_cheetah_urdf/urdf/mini_cheetah.urdf` + - 混合 `OBJ/STL` 资产链路;适合 mesh loader 与相对路径解析回归。 +- `test/awesome_robot_descriptions_repos/cassie_description/urdf/cassie_v4.urdf` + - 双足/人形 URDF;适合复杂关节层级和碰撞/惯性链路检查。 +- `test/awesome_robot_descriptions_repos/fanuc_m710ic_description/urdf/m710ic70.urdf` + - 工业机械臂;适合关节轴、层级清晰度与属性编辑面板回归。 +- `test/awesome_robot_descriptions_repos/models/franka_description/urdf/panda_arm_hand.urdf` + - `gltf + ktx2 + png/bin` 资产链路;适合现代 mesh 资源解析与纹理引用验证。 +- `test/awesome_robot_descriptions_repos/onshape-to-robot-examples/quadruped_urdf/robot.urdf` + - 结构较直接的 Onshape 导出样本;适合快速排查导入器对简化 URDF 的兼容性。 + +### 16.4 MJCF / MuJoCo 样本 + +- `test/awesome_robot_descriptions_repos/mujoco_menagerie/unitree_go2/go2.xml` + - 标准 MuJoCo menagerie 样本;适合 MJCF 导入与 Unitree 对照验证。 +- `test/awesome_robot_descriptions_repos/mujoco_menagerie/unitree_go2/scene.xml` + - 带场景包装的 MJCF;适合检查 scene 级引用与资源装配。 + +### 16.5 样本选择建议 + +- 快速 smoke: + - `test/gazebo_models/camera/model.sdf` + - `test/awesome_robot_descriptions_repos/fanuc_m710ic_description/urdf/m710ic70.urdf` + - `test/unitree_model/Go2/usd/go2.usd` +- 资源加载/路径解析回归: + - `test/gazebo_models/bus_stop/model.sdf` + - `test/awesome_robot_descriptions_repos/models/franka_description/urdf/panda_arm_hand.urdf` + - `test/awesome_robot_descriptions_repos/mini_cheetah_urdf/urdf/mini_cheetah.urdf` +- 复杂层级/人形链路: + - `test/unitree_model/H1-2/h1_2/h1_2.usd` + - `test/awesome_robot_descriptions_repos/cassie_description/urdf/cassie_v4.urdf` +- USD worker / metadata / roundtrip: + - 整套 `test/unitree_model` + +使用要求: + +- 在任务描述、脚本参数、回归记录里直接写具体文件路径,不要只写“跑一下 test 里的模型”。 +- 若任务只影响某一种格式,优先选对应格式样本,不要默认把所有大样本都跑一遍。 +- 若新增了长期稳定、可重复复用的高价值样本,可顺手补充到本节,而不是只留在临时聊天记录里。 + +## 17. Prompt 编写建议(给 AI 指令使用者) + +- 具体化:明确 `Link` / `Joint` 名称、源文件名、当前模式 +- 结构化:描述期望父子连接关系、workspace 还是 structure 视图、是否涉及 merged assembly +- 导出/回归:说明目标格式(`URDF` / `MJCF` / `USD` / `.usp`)、是否要保留 roundtrip 能力 +- 物理约束:涉及电机替换时给出力矩、减速比、阻尼/摩擦范围 + +以上可显著提升 AI 输出的可执行性与一致性。 + +## 18. 近期结构变化记录(2026-03 / 2026-04-06) + +- App 编排层已明显增厚:导入导出、USD hydration、workspace/source sync、overlay lazy loading 主要在 `src/app/*` 完成。 +- `src/app/*` 新增了 document loading / import preparation / robot import / usd binary archive 等 worker bridge 与 transfer utils;导入和大文件导出不再只靠主线程串行处理。 +- `src/app/components/unified-viewer/*` 与 `src/app/hooks/file-export/*` 已拆成稳定子树;`UnifiedViewer.tsx` / `useFileExport.ts` 作为入口编排,细分逻辑优先放到这些子模块。 +- `src/app/components/WorkspaceCanvas.tsx` 当前只是应用层导出入口;共享画布 runtime、WebGL 检查、renderer cleanup 与 error boundary 已沉到 `src/shared/components/3d/workspace/*`。 +- `UnifiedViewer` 周边已新增一批 forced session、handoff ready、load release、resource scope、viewport handoff 工具;涉及 viewer 切换状态时优先复用这些工具,而不是在组件里散写本地状态机。 +- `features/file-io/hooks/useFileExport.ts` 已移除,当前应用导出工作流 source of truth 为 `src/app/hooks/useFileExport.ts`。 +- `features/file-io` 目前仅保留 `usePdfExport.tsx`、`useSnapshot.ts` 等局部 hooks;应用导入工作流 source of truth 为 `src/app/hooks/useFileImport.ts`。 +- `features/file-io/*` 目前已接入 `projectArchive.worker.ts`、`projectImport.worker.ts`、`usdExport.worker.ts` 与 `ExportProgressDialog.tsx`,project import/export 与长时导出默认应走 worker + progress UI。 +- `robot-tree` 已拆成 `tree-editor/*` 与 `tree-node/*` 子树;结构树 UI 与文件浏览器 UI 不再继续堆到单个组件里。 +- `property-editor` 已形成 `components/* + hooks/* + utils/geometry-conversion/* + workers/*` 结构;几何转换和 mesh analysis 优先在 util/worker 链路解决。 +- `urdf-viewer` 已形成“React 层 + vendored runtime + adapter/utils + workers”结构,并新增 `UsdOffscreenStage.tsx`、`usdOffscreenViewer.worker.ts` 与 offscreen 协议/渲染支持工具。 +- `urdf-viewer/runtime/*` 现已细分出 `types/*` 与 `vendor/*` 子树;涉及上游 vendored runtime 的协议类型与兼容性适配优先落这里。 +- `urdf-viewer` 已新增一批 USD roundtrip/hydration/resolution/prepared-open/prepared-export 工具,相关 source-of-truth 默认收口在 runtime/worker 链路。 +- `shared/data/` 与 `shared/debug/` 已加入仓库,分别承载共享静态数据和回归调试桥接。 +- `shared/workers/*` 与 `core/loaders/workers/*` 已进入常规结构,worker 并行能力不再只存在于 feature/app 层。 +- 根 `package.json` 已提供统一的 `lint` / `test` / `check` / `verify:*` 入口;文档与 agent workflow 不应再假设“只能按模块手拼命令”。 +- collision overlay material / render order 已下沉到 `src/shared/utils/three/collisionOverlayMaterial.ts`,`core` 不应再直接依赖 `features/urdf-viewer/utils/materials`。 +- `src/lib/*` 与 `packages/react-robot-canvas/*` 已进入稳定工作区,用于通用画布封装与发布。 +- `src/lib/` 当前已细分 `components/*` 与 `hooks/*`;对外复用逻辑不再只靠单一 `index.ts` 暴露。 +- `packages/react-robot-canvas/` 当前附带 `dist/` 发布产物,默认由包构建脚本与 `scripts/postbuild.mjs` 维护。 +- `docs/` 现已补充 `architecture-boundaries.md`、`robot-canvas-lib.md`、`runtime-fallback-audit.md`,做边界说明、对外库说明与 runtime fallback 审计参考。 +- `test/` 中包含外部工程镜像和大型样本,修改前先确认是否真的是本次任务范围。 +- 2026-03-30:USD runtime metadata 的 folded collision semantic child link 修复已明确收口到 `runtime/hydra/render-delegate/*` 的 worker/runtime 链路,不应再把同类问题修到主线程 adapter、UI store 或 regression bridge。 +- 2026-03-30:`test/unitree_model` 目前是 USD worker/stage metadata 链路的主浏览器回归样本集;后续改动该链路时,默认要补跑整套 Unitree 验证,而不是只跑单个 Go2 fixture。 +- 2026-04-11:ToolboxMenu 引入工具注册表模式。`useToolItems` hook(`app/hooks/useToolItems.tsx`)作为工具定义与路由的单一入口,新增内部工具只需改该文件 + i18n。`ToolboxMenu` 改为纯渲染器,接收 `items: ToolboxItem[]`。`AppExposedActions` 新增 `openTool(key)` 统一路由。已有 `openAIInspection` / `openAIConversation` / `openIkTool` / `openCollisionOptimizer` 字段全部保留(向后兼容)。 diff --git a/RELEASING.md b/RELEASING.md deleted file mode 100644 index 126f58350..000000000 --- a/RELEASING.md +++ /dev/null @@ -1,66 +0,0 @@ -# Releasing - -This repository manages two independent semantic versions: - -- `urdf-studio`: the private app workspace version used for builds and the About dialog -- `@urdf-studio/react-robot-canvas`: the published package workspace version - -## Inspect Current Versions - -```bash -npm run version:show -``` - -## Bump Versions - -Examples: - -```bash -# Bump the app version by one minor release -npm run version:bump -- --app minor - -# Bump the published package by one patch release -npm run version:bump -- --package patch - -# Set exact versions explicitly -npm run version:bump -- --app 2.1.0 --package 0.2.0 -``` - -The bump script updates: - -- root `package.json` -- root `package-lock.json` for app version changes -- `packages/react-robot-canvas/package.json` -- version references in [`README.md`](./README.md) and [`README_CN.md`](./README_CN.md) - -## Changelog Policy - -- Keep [`CHANGELOG.md`](./CHANGELOG.md) in Keep a Changelog format. -- Update the `Unreleased` section while work is in progress. -- When cutting a release, move the relevant notes into a dated version section. - -## Verification - -For app-only releases: - -```bash -npm run build -``` - -For package releases: - -```bash -npm run build:package:react-robot-canvas -npm run pack:package:react-robot-canvas -``` - -If both changed, run both verification paths before tagging or publishing. - -## Publishing The Package Workspace - -After bumping the package version and validating the build: - -```bash -cd packages/react-robot-canvas -npm publish -``` diff --git a/docs/architecture-boundaries.md b/docs/architecture-boundaries.md index 7ad156674..549ee988d 100644 --- a/docs/architecture-boundaries.md +++ b/docs/architecture-boundaries.md @@ -19,8 +19,8 @@ Rules for new code: ### Feature Public APIs -- `visualizer` exposes controller + scene/panel surface through `src/features/visualizer/index.ts` -- `urdf-viewer` exposes controller + scene/panel surface through `src/features/urdf-viewer/index.ts` +- `editor`(统一 Editor 公开入口)exposes controller + scene/panel surface through `src/features/editor/index.ts` +- `urdf-viewer`(Editor 实现子目录)retains runtime/components/utils internals through `src/features/urdf-viewer/index.ts` - `file-io` exposes import/export entry points through `src/features/file-io/index.ts` ### Canonical Data Sources diff --git a/docs/prompts/CLAUDE.md b/docs/prompts/CLAUDE.md index f4194a8ca..dcf594993 100644 --- a/docs/prompts/CLAUDE.md +++ b/docs/prompts/CLAUDE.md @@ -1,17 +1,17 @@ # URDF-Studio 开发指南 -> 单一主 Prompt 上下文。`overview.md`、`URDF_STUDIO_STYLE_GUIDE.md`、`urdf-viewer.md`、`visualizer.md` 只保留轻量入口,避免重复加载同一批信息。 +> 单一主 Prompt 上下文。`overview.md`、`URDF_STUDIO_STYLE_GUIDE.md`、`urdf-viewer.md` 只保留轻量入口,避免重复加载同一批信息。 ## 1. 最小读取策略 建议按以下顺序读取,控制 token: -1. 先读仓库根目录 `AGENTS.md`。 +1. 先读仓库根目录 `CLAUDE.md`。 2. 再读本文件。 3. 仅在任务相关时再看对应轻量入口: - UI / 主题 / 可访问性:看本文件第 6 节 - - `Visualizer`(`Editor` 下的拓扑 / 硬件能力):看本文件第 7 节 - - `URDF Viewer`(`Editor` 下的几何 / 碰撞 / 测量能力):看本文件第 8 节 + - `Editor 拓扑/硬件子域`:看本文件第 7 节 + - `Editor 几何/碰撞/测量子域`:看本文件第 8 节 - 架构边界 / 对外库 / runtime fallback 审计:看 `docs/architecture-boundaries.md`、`docs/robot-canvas-lib.md`、`docs/runtime-fallback-audit.md` 4. AI 审阅标准直接读取: - `src/features/ai-assistant/config/urdf_inspect_standard_en.md` @@ -22,7 +22,7 @@ - 当前在 `Editor` 中操作的是拓扑、几何/碰撞、还是硬件相关能力 - 涉及电机时的力矩 / 传动 / 阻尼约束 -若本文件描述与当前 `src/` 真实结构冲突,以仓库现状和 `AGENTS.md` 为准。 +若本文件描述与当前 `src/` 真实结构冲突,以仓库现状和 `CLAUDE.md` 为准。 ## 1.5 Skill-first 路由(减少 prompt / MCP token) @@ -187,11 +187,11 @@ app -> features -> store -> shared -> core -> types ## 4. 单模式 `Editor` -| 子能力 | 主模块 | 典型任务 | -| ------------------ | ------------- | ---------------------------------------------- | -| 拓扑编辑 | `Visualizer` | Link / Joint 增删、拓扑编辑、关节参数 | -| 几何 / 碰撞 / 测量 | `URDF Viewer` | Visual / Collision、网格、材质、纹理、碰撞变换 | -| 硬件配置 | `Visualizer` | 电机型号、传动比、阻尼、摩擦 | +| 子能力 | 主模块 | 典型任务 | +| ------------------ | ----------------- | ---------------------------------------------- | +| 拓扑编辑 | `Editor 拓扑子域` | Link / Joint 增删、拓扑编辑、关节参数 | +| 几何 / 碰撞 / 测量 | `Editor 几何子域` | Visual / Collision、网格、材质、纹理、碰撞变换 | +| 硬件配置 | `Editor 硬件子域` | 电机型号、传动比、阻尼、摩擦 | 新增功能前,先判断属于 `Editor` 下哪类子能力,避免跨子系统逻辑缠绕。 @@ -206,7 +206,7 @@ app -> features -> store -> shared -> core -> types - `src/app/App.tsx`:根组件,装配 Providers、懒加载模态框、全局导入导出入口、debug bridge - `src/app/AppLayout.tsx`:应用壳、Header、TreeEditor、PropertyEditor、UnifiedViewer 主编排 -- `src/app/components/UnifiedViewer.tsx`:统一组合 `Visualizer` / `URDF Viewer` +- `src/app/components/UnifiedViewer.tsx`:统一 `Editor` viewer 组合入口 - `src/app/components/WorkspaceCanvas.tsx`:应用层对共享画布入口的 re-export;底层 runtime 在 `src/shared/components/3d/workspace/*` - `src/app/components/AppLayoutOverlays.tsx`:延迟加载桥接创建、碰撞优化等浮层 - `src/app/components/ConnectedDocumentLoadingOverlay.tsx` / `src/app/components/DocumentLoadingOverlay.tsx` / `src/app/components/ImportPreparationOverlay.tsx`:导入准备与文档加载反馈 @@ -320,25 +320,15 @@ app -> features -> store -> shared -> core -> types - Hover / Active / Focus 行为一致且可感知 - 不新增分散硬编码色值 -## 7. Visualizer(Editor 子能力:拓扑 / 硬件) +## 7. Editor 统一 Viewer 子域(已合并) ### 当前结构 -- `src/features/visualizer/components/Visualizer.tsx` -- `src/features/visualizer/components/VisualizerScene.tsx` -- `src/features/visualizer/components/VisualizerPanels.tsx` -- `src/features/visualizer/components/VisualizerCanvas.tsx` -- `src/features/visualizer/components/VisualizerHoverController.tsx` -- `src/features/visualizer/components/nodes/*` -- `src/features/visualizer/components/controls/*` -- `src/features/visualizer/components/constraints/*` -- `src/features/visualizer/hooks/*` -- `src/features/visualizer/hooks/useVisualizerState.ts` -- `src/features/visualizer/hooks/useCollisionMeshPrewarm.ts` -- `src/features/visualizer/utils/mergedVisualizerSceneMode.ts` -- `src/features/visualizer/utils/mergedVisualizerLayout.ts` -- `src/features/visualizer/utils/materialCache.ts` -- 共享面板在 `src/shared/components/Panel/*` +- 公开入口:`src/features/editor/index.ts` +- 实现目录:`src/features/urdf-viewer/components/*`、`src/features/urdf-viewer/hooks/*`、`src/features/urdf-viewer/utils/*` +- 统一场景宿主:`src/app/components/unified-viewer/*` +- 共享 3D 能力:`src/shared/components/3d/*` +- 共享面板:`src/shared/components/Panel/*` ### 核心模式 @@ -348,22 +338,21 @@ RobotNode (Link) -> JointNode (Joint) -> RobotNode (child Link) 关键 hooks / 能力: -- `useJointPivots` -- `useCollisionRefs` -- `useClosedLoopDragSync` -- `useTransformControls` -- `useVisualizerController` +- `useViewerController` +- `useMouseInteraction` +- `useHoverDetection` +- `useVisualizationEffects` ### 实现约束 -- 新能力优先放入 hooks 或新组件,不继续增厚 `Visualizer.tsx` -- 材质必须通过 `materialCache` 复用,不在高频路径直接 `new` 材质 +- 新能力优先放入 hooks 或新组件,不要恢复双壳并存 +- 材质必须通过 `materials.ts` / `urdfMaterials.ts` 复用,不在高频路径直接 `new` 材质 - 统一使用 `RobotState` 等共享类型,避免 `any` - TransformControls 依赖的引用注册必须完整、可追踪 - 保持 `RobotNode <-> JointNode` 交替递归模式 - 涉及 THREE 资源时注意释放,避免材质 / 几何体泄漏 -## 8. URDF Viewer(Editor 子能力:几何 / 碰撞 / 测量) +## 8. Editor 几何/碰撞/测量子域 ### 当前结构 @@ -392,7 +381,7 @@ RobotNode (Link) -> JointNode (Joint) -> RobotNode (child Link) ### 核心文件 -- `src/features/urdf-viewer/components/URDFViewerCanvas.tsx` +- `src/features/urdf-viewer/components/ViewerCanvas.tsx` - `src/features/urdf-viewer/components/ViewerToolbar.tsx` - `src/features/urdf-viewer/components/ViewerLoadingHud.tsx` - `src/features/urdf-viewer/components/UsdWasmStage.tsx` diff --git a/docs/prompts/overview.md b/docs/prompts/overview.md index a1e196afd..6d656641f 100644 --- a/docs/prompts/overview.md +++ b/docs/prompts/overview.md @@ -8,7 +8,6 @@ 2. 再读 `docs/prompts/CLAUDE.md` 3. 只在需要文件锚点时再看: - `docs/prompts/URDF_STUDIO_STYLE_GUIDE.md` - - `docs/prompts/visualizer.md` - `docs/prompts/urdf-viewer.md` 4. 需要边界或对外库说明时再看: - `docs/architecture-boundaries.md` diff --git a/docs/prompts/urdf-viewer.md b/docs/prompts/urdf-viewer.md index 755c00591..cb0ee870f 100644 --- a/docs/prompts/urdf-viewer.md +++ b/docs/prompts/urdf-viewer.md @@ -1,13 +1,14 @@ -# URDF Viewer 模块入口 +# Editor 几何/碰撞/测量子域入口 本文件改为轻量入口,完整上下文已并入 `docs/prompts/CLAUDE.md` 第 8 节。 +说明:`urdf-viewer` 是 `Editor` 内部子域目录名,不是独立运行模式。 ## 快速定位 -- 主组件:`src/features/urdf-viewer/components/URDFViewer.tsx` -- 场景编排:`src/features/urdf-viewer/components/URDFViewerScene.tsx` -- 面板编排:`src/features/urdf-viewer/components/URDFViewerPanels.tsx` -- 画布层:`src/features/urdf-viewer/components/URDFViewerCanvas.tsx` +- 主入口:`src/app/components/unified-viewer/ViewerSceneConnector.tsx` +- 场景编排:`src/features/urdf-viewer/components/ViewerScene.tsx` +- 面板编排:`src/features/urdf-viewer/components/ViewerPanels.tsx` +- 画布层:`src/features/urdf-viewer/components/ViewerCanvas.tsx` - 工具条:`src/features/urdf-viewer/components/ViewerToolbar.tsx` - Loading HUD:`src/features/urdf-viewer/components/ViewerLoadingHud.tsx` - USD 嵌入入口:`src/features/urdf-viewer/components/UsdWasmStage.tsx` diff --git a/docs/prompts/visualizer.md b/docs/prompts/visualizer.md deleted file mode 100644 index da4bb0c09..000000000 --- a/docs/prompts/visualizer.md +++ /dev/null @@ -1,29 +0,0 @@ -# Visualizer 模块入口 - -本文件改为轻量入口,完整上下文已并入 `docs/prompts/CLAUDE.md` 第 7 节。 - -## 快速定位 - -- 主组件:`src/features/visualizer/components/Visualizer.tsx` -- 场景编排:`src/features/visualizer/components/VisualizerScene.tsx` -- 面板编排:`src/features/visualizer/components/VisualizerPanels.tsx` -- 画布:`src/features/visualizer/components/VisualizerCanvas.tsx` -- 悬停控制:`src/features/visualizer/components/VisualizerHoverController.tsx` -- 约束覆盖:`src/features/visualizer/components/constraints/*` -- 节点递归:`src/features/visualizer/components/nodes/*` -- 控制器:`src/features/visualizer/components/controls/*` -- Hooks:`src/features/visualizer/hooks/*` -- 状态与预热:`src/features/visualizer/hooks/useVisualizerState.ts`、`src/features/visualizer/hooks/useCollisionMeshPrewarm.ts` -- 场景模式/布局:`src/features/visualizer/utils/mergedVisualizerSceneMode.ts`、`src/features/visualizer/utils/mergedVisualizerLayout.ts` -- 交互辅助:`src/features/visualizer/utils/hoverPicking.ts`、`src/features/visualizer/utils/geometryHover.ts` -- 材质缓存:`src/features/visualizer/utils/materialCache.ts` - -## 关键约束 - -- 新功能优先拆到 hooks / 新组件,不继续堆进 `Visualizer.tsx` -- `src/app/components/unified-viewer/*` 负责统一 viewer 宿主与 mode 切换;不要把 app 级 overlay / preview / forced session 状态回灌到 visualizer feature 内部 -- 保持 `RobotNode <-> JointNode` 交替递归结构 -- 引用注册必须完整,避免 TransformControls 失联 -- 材质通过缓存复用,避免高频路径直接创建 -- 类型优先用 `RobotState`,避免 `any` -- 闭环与碰撞拖拽优先复用现有 hooks:`useClosedLoopDragSync`、`useJointPivots`、`useCollisionRefs`、`useTransformControls` diff --git a/docs/robot-canvas-lib.md b/docs/robot-canvas-lib.md index ba7c5f0df..eafa5886f 100644 --- a/docs/robot-canvas-lib.md +++ b/docs/robot-canvas-lib.md @@ -40,7 +40,7 @@ import { RobotCanvas } from '@/lib'; onJointAnglesChange={(jointAngles) => { console.log(jointAngles); }} -/> +/>; ``` ## 已完成 @@ -66,7 +66,7 @@ import { RobotCanvas } from '@/lib'; - `assets` 目前仍是 `Record`,后续应升级为 `assetResolver` - 类型声明现在已切到 `tsc` 自动生成,并对产物里的 `@/` 别名做发布前重写 -- `Visualizer` 仍属于应用内编辑壳,尚未抽成独立可发布子包 +- `editor` 子域能力仍属于应用内壳,尚未抽成独立可发布子包 ## 当前发布方式 diff --git a/handoff.html b/handoff.html new file mode 100644 index 000000000..f41cb1205 --- /dev/null +++ b/handoff.html @@ -0,0 +1,13 @@ + + + + + + URDF Studio Import Handoff + + + +
+ + + diff --git a/log/COLCON_IGNORE b/log/COLCON_IGNORE deleted file mode 100644 index e69de29bb..000000000 diff --git a/metadata.json b/metadata.json deleted file mode 100644 index 518709f79..000000000 --- a/metadata.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "URDF Studio - Visual Robot Designer", - "description": "A visual, top-down URDF design tool. Start with stick-figure kinematics and refine into detailed designs. Features rapid prototyping.", - "requestFramePermissions": [] -} diff --git a/package-lock.json b/package-lock.json index f0732e655..9aab878e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "jsdom": "^28.0.0", "jspdf": "^4.0.0", "jszip": "^3.10.1", + "libarchive.js": "^2.0.2", "linkedom": "^0.18.12", "lucide-react": "^0.555.0", "monaco-editor": "^0.55.1", @@ -31,9 +32,11 @@ "react": "^19.2.0", "react-dom": "^19.2.0", "react-draggable": "^4.5.0", + "react-markdown": "^10.1.0", "react-resizable": "^3.1.3", "react-simple-code-editor": "^0.14.1", "react-syntax-highlighter": "^16.1.0", + "remark-gfm": "^4.0.1", "three": "^0.181.2", "zustand": "^5.0.10" }, @@ -67,14 +70,10 @@ }, "node_modules/@acemir/cssom": { "version": "0.9.31", - "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", - "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", "license": "MIT" }, "node_modules/@asamuzakjp/css-color": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", - "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.4", @@ -86,8 +85,6 @@ }, "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { "version": "11.2.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", - "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -95,8 +92,6 @@ }, "node_modules/@asamuzakjp/dom-selector": { "version": "6.7.7", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.7.tgz", - "integrity": "sha512-8CO/UQ4tzDd7ula+/CVimJIVWez99UJlbMyIgk8xOnhAVPKLnBZmUFYVgugS441v2ZqUq5EnSh6B0Ua0liSFAA==", "license": "MIT", "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", @@ -108,8 +103,6 @@ }, "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { "version": "11.2.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", - "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -117,14 +110,10 @@ }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", "license": "MIT" }, "node_modules/@babel/code-frame": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { @@ -138,8 +127,6 @@ }, "node_modules/@babel/compat-data": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "engines": { @@ -148,8 +135,6 @@ }, "node_modules/@babel/core": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", "peer": true, @@ -180,8 +165,6 @@ }, "node_modules/@babel/generator": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz", - "integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -197,8 +180,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { @@ -214,8 +195,6 @@ }, "node_modules/@babel/helper-globals": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", "engines": { @@ -224,8 +203,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { @@ -238,8 +215,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { @@ -256,8 +231,6 @@ }, "node_modules/@babel/helper-plugin-utils": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -266,8 +239,6 @@ }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -276,8 +247,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -286,8 +255,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -296,8 +263,6 @@ }, "node_modules/@babel/helpers": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { @@ -310,8 +275,6 @@ }, "node_modules/@babel/parser": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, "license": "MIT", "dependencies": { @@ -326,8 +289,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", "dependencies": { @@ -342,8 +303,6 @@ }, "node_modules/@babel/plugin-transform-react-jsx-source": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", "dependencies": { @@ -358,8 +317,6 @@ }, "node_modules/@babel/runtime": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -367,8 +324,6 @@ }, "node_modules/@babel/template": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { @@ -382,8 +337,6 @@ }, "node_modules/@babel/traverse": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "dependencies": { @@ -401,8 +354,6 @@ }, "node_modules/@babel/types": { "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { @@ -415,8 +366,6 @@ }, "node_modules/@cacheable/memory": { "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.8.tgz", - "integrity": "sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==", "dev": true, "license": "MIT", "dependencies": { @@ -428,8 +377,6 @@ }, "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", - "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -445,8 +392,6 @@ }, "node_modules/@cacheable/memory/node_modules/keyv": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", - "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", "peer": true, @@ -456,8 +401,6 @@ }, "node_modules/@cacheable/utils": { "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.1.tgz", - "integrity": "sha512-eiFgzCbIneyMlLOmNG4g9xzF7Hv3Mga4LjxjcSC/ues6VYq2+gUbQI8JqNuw/ZM8tJIeIaBGpswAsqV2V7ApgA==", "dev": true, "license": "MIT", "dependencies": { @@ -467,8 +410,6 @@ }, "node_modules/@cacheable/utils/node_modules/keyv": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", - "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", "dependencies": { @@ -477,8 +418,6 @@ }, "node_modules/@commitlint/cli": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.0.tgz", - "integrity": "sha512-yNkyN/tuKTJS3wdVfsZ2tXDM4G4Gi7z+jW54Cki8N8tZqwKBltbIvUUrSbT4hz1bhW/h0CdR+5sCSpXD+wMKaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -499,8 +438,6 @@ }, "node_modules/@commitlint/config-conventional": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.0.tgz", - "integrity": "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==", "dev": true, "license": "MIT", "dependencies": { @@ -513,8 +450,6 @@ }, "node_modules/@commitlint/config-validator": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-20.5.0.tgz", - "integrity": "sha512-T/Uh6iJUzyx7j35GmHWdIiGRQB+ouZDk0pwAaYq4SXgB54KZhFdJ0vYmxiW6AMYICTIWuyMxDBl1jK74oFp/Gw==", "dev": true, "license": "MIT", "dependencies": { @@ -527,8 +462,6 @@ }, "node_modules/@commitlint/ensure": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.0.tgz", - "integrity": "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==", "dev": true, "license": "MIT", "dependencies": { @@ -545,8 +478,6 @@ }, "node_modules/@commitlint/execute-rule": { "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-20.0.0.tgz", - "integrity": "sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==", "dev": true, "license": "MIT", "engines": { @@ -555,8 +486,6 @@ }, "node_modules/@commitlint/format": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-20.5.0.tgz", - "integrity": "sha512-TI9EwFU/qZWSK7a5qyXMpKPPv3qta7FO4tKW+Wt2al7sgMbLWTsAcDpX1cU8k16TRdsiiet9aOw0zpvRXNJu7Q==", "dev": true, "license": "MIT", "dependencies": { @@ -569,8 +498,6 @@ }, "node_modules/@commitlint/is-ignored": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-20.5.0.tgz", - "integrity": "sha512-JWLarAsurHJhPozbuAH6GbP4p/hdOCoqS9zJMfqwswne+/GPs5V0+rrsfOkP68Y8PSLphwtFXV0EzJ+GTXTTGg==", "dev": true, "license": "MIT", "dependencies": { @@ -583,8 +510,6 @@ }, "node_modules/@commitlint/is-ignored/node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -596,8 +521,6 @@ }, "node_modules/@commitlint/lint": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.0.tgz", - "integrity": "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==", "dev": true, "license": "MIT", "dependencies": { @@ -612,8 +535,6 @@ }, "node_modules/@commitlint/load": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.0.tgz", - "integrity": "sha512-sLhhYTL/KxeOTZjjabKDhwidGZan84XKK1+XFkwDYL/4883kIajcz/dZFAhBJmZPtL8+nBx6bnkzA95YxPeDPw==", "dev": true, "license": "MIT", "dependencies": { @@ -633,8 +554,6 @@ }, "node_modules/@commitlint/message": { "version": "20.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-20.4.3.tgz", - "integrity": "sha512-6akwCYrzcrFcTYz9GyUaWlhisY4lmQ3KvrnabmhoeAV8nRH4dXJAh4+EUQ3uArtxxKQkvxJS78hNX2EU3USgxQ==", "dev": true, "license": "MIT", "engines": { @@ -643,8 +562,6 @@ }, "node_modules/@commitlint/parse": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-20.5.0.tgz", - "integrity": "sha512-SeKWHBMk7YOTnnEWUhx+d1a9vHsjjuo6Uo1xRfPNfeY4bdYFasCH1dDpAv13Lyn+dDPOels+jP6D2GRZqzc5fA==", "dev": true, "license": "MIT", "dependencies": { @@ -658,8 +575,6 @@ }, "node_modules/@commitlint/read": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-20.5.0.tgz", - "integrity": "sha512-JDEIJ2+GnWpK8QqwfmW7O42h0aycJEWNqcdkJnyzLD11nf9dW2dWLTVEa8Wtlo4IZFGLPATjR5neA5QlOvIH1w==", "dev": true, "license": "MIT", "dependencies": { @@ -675,8 +590,6 @@ }, "node_modules/@commitlint/resolve-extends": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.0.tgz", - "integrity": "sha512-3SHPWUW2v0tyspCTcfSsYml0gses92l6TlogwzvM2cbxDgmhSRc+fldDjvGkCXJrjSM87BBaWYTPWwwyASZRrg==", "dev": true, "license": "MIT", "dependencies": { @@ -693,8 +606,6 @@ }, "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", "engines": { @@ -703,8 +614,6 @@ }, "node_modules/@commitlint/rules": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.0.tgz", - "integrity": "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -719,8 +628,6 @@ }, "node_modules/@commitlint/to-lines": { "version": "20.0.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-20.0.0.tgz", - "integrity": "sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==", "dev": true, "license": "MIT", "engines": { @@ -729,8 +636,6 @@ }, "node_modules/@commitlint/top-level": { "version": "20.4.3", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-20.4.3.tgz", - "integrity": "sha512-qD9xfP6dFg5jQ3NMrOhG0/w5y3bBUsVGyJvXxdWEwBm8hyx4WOk3kKXw28T5czBYvyeCVJgJJ6aoJZUWDpaacQ==", "dev": true, "license": "MIT", "dependencies": { @@ -742,8 +647,6 @@ }, "node_modules/@commitlint/types": { "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-20.5.0.tgz", - "integrity": "sha512-ZJoS8oSq2CAZEpc/YI9SulLrdiIyXeHb/OGqGrkUP6Q7YV+0ouNAa7GjqRdXeQPncHQIDz/jbCTlHScvYvO/gA==", "dev": true, "license": "MIT", "dependencies": { @@ -756,8 +659,6 @@ }, "node_modules/@conventional-changelog/git-client": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.6.0.tgz", - "integrity": "sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==", "dev": true, "license": "MIT", "dependencies": { @@ -783,8 +684,6 @@ }, "node_modules/@conventional-changelog/git-client/node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -796,8 +695,6 @@ }, "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==", "funding": [ { "type": "github", @@ -815,8 +712,6 @@ }, "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==", "funding": [ { "type": "github", @@ -838,8 +733,6 @@ }, "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==", "funding": [ { "type": "github", @@ -865,8 +758,6 @@ }, "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==", "funding": [ { "type": "github", @@ -888,8 +779,6 @@ }, "node_modules/@csstools/css-syntax-patches-for-csstree": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz", - "integrity": "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==", "funding": [ { "type": "github", @@ -912,8 +801,6 @@ }, "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==", "funding": [ { "type": "github", @@ -932,8 +819,6 @@ }, "node_modules/@csstools/selector-resolve-nested": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-4.0.0.tgz", - "integrity": "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==", "dev": true, "funding": [ { @@ -955,8 +840,6 @@ }, "node_modules/@csstools/selector-specificity": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-6.0.0.tgz", - "integrity": "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==", "dev": true, "funding": [ { @@ -978,14 +861,10 @@ }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", - "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", "license": "Apache-2.0" }, "node_modules/@electron/get": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1278,8 +1157,6 @@ }, "node_modules/@esbuild/linux-x64": { "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -1448,8 +1325,6 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1467,8 +1342,6 @@ }, "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1480,8 +1353,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -1490,8 +1361,6 @@ }, "node_modules/@eslint/config-array": { "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1505,8 +1374,6 @@ }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1518,8 +1385,6 @@ }, "node_modules/@eslint/core": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1531,8 +1396,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { @@ -1555,8 +1418,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/ajv": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -1572,8 +1433,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { @@ -1585,15 +1444,11 @@ }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -1605,8 +1460,6 @@ }, "node_modules/@eslint/js": { "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { @@ -1618,8 +1471,6 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1628,8 +1479,6 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1642,8 +1491,6 @@ }, "node_modules/@exodus/bytes": { "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.11.0.tgz", - "integrity": "sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==", "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -1659,8 +1506,6 @@ }, "node_modules/@fontsource/inter": { "version": "5.2.8", - "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz", - "integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==", "license": "OFL-1.1", "funding": { "url": "https://github.com/sponsors/ayuhito" @@ -1668,8 +1513,6 @@ }, "node_modules/@humanfs/core": { "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1678,8 +1521,6 @@ }, "node_modules/@humanfs/node": { "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1692,8 +1533,6 @@ }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1706,8 +1545,6 @@ }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1720,8 +1557,6 @@ }, "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": { @@ -1731,8 +1566,6 @@ }, "node_modules/@jridgewell/remapping": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1742,8 +1575,6 @@ }, "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": { @@ -1752,15 +1583,11 @@ }, "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==", "dev": true, "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": { @@ -1770,21 +1597,15 @@ }, "node_modules/@keyv/serialize": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", "dev": true, "license": "MIT" }, "node_modules/@mediapipe/tasks-vision": { "version": "0.10.17", - "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.17.tgz", - "integrity": "sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==", "license": "Apache-2.0" }, "node_modules/@monaco-editor/loader": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", - "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", "license": "MIT", "dependencies": { "state-local": "^1.0.6" @@ -1792,8 +1613,6 @@ }, "node_modules/@monaco-editor/react": { "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", - "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", "license": "MIT", "dependencies": { "@monaco-editor/loader": "^1.5.0" @@ -1806,8 +1625,6 @@ }, "node_modules/@monogrid/gainmap-js": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@monogrid/gainmap-js/-/gainmap-js-3.4.0.tgz", - "integrity": "sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==", "license": "MIT", "dependencies": { "promise-worker-transferable": "^1.0.4" @@ -1818,8 +1635,6 @@ }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { @@ -1832,8 +1647,6 @@ }, "node_modules/@nodelib/fs.stat": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", "engines": { @@ -1842,8 +1655,6 @@ }, "node_modules/@nodelib/fs.walk": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { @@ -1856,8 +1667,6 @@ }, "node_modules/@puppeteer/browsers": { "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.11.2.tgz", - "integrity": "sha512-GBY0+2lI9fDrjgb5dFL9+enKXqyOPok9PXg/69NVkjW3bikbK9RQrNrI3qccQXmDNN7ln4j/yL89Qgvj/tfqrw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1878,8 +1687,6 @@ }, "node_modules/@puppeteer/browsers/node_modules/semver": { "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { @@ -1891,8 +1698,6 @@ }, "node_modules/@react-three/drei": { "version": "10.7.7", - "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz", - "integrity": "sha512-ff+J5iloR0k4tC++QtD/j9u3w5fzfgFAWDtAGQah9pF2B1YgOq/5JxqY0/aVoQG5r3xSZz0cv5tk2YuBob4xEQ==", "license": "MIT", "peer": true, "dependencies": { @@ -1932,8 +1737,6 @@ }, "node_modules/@react-three/fiber": { "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.5.0.tgz", - "integrity": "sha512-FiUzfYW4wB1+PpmsE47UM+mCads7j2+giRBltfwH7SNhah95rqJs3ltEs9V3pP8rYdS0QlNne+9Aj8dS/SiaIA==", "license": "MIT", "peer": true, "dependencies": { @@ -1981,8 +1784,6 @@ }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", - "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", "dev": true, "license": "MIT" }, @@ -2226,8 +2027,6 @@ }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", - "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", "cpu": [ "x64" ], @@ -2240,8 +2039,6 @@ }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", "cpu": [ "x64" ], @@ -2338,8 +2135,6 @@ }, "node_modules/@simple-libs/child-process-utils": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", - "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", "dev": true, "license": "MIT", "dependencies": { @@ -2354,8 +2149,6 @@ }, "node_modules/@simple-libs/stream-utils": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", - "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", "dev": true, "license": "MIT", "engines": { @@ -2367,8 +2160,6 @@ }, "node_modules/@sindresorhus/is": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", "dev": true, "license": "MIT", "engines": { @@ -2380,8 +2171,6 @@ }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", - "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", "dev": true, "license": "MIT", "engines": { @@ -2393,8 +2182,6 @@ }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", "dev": true, "license": "MIT", "dependencies": { @@ -2406,8 +2193,6 @@ }, "node_modules/@tailwindcss/node": { "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", - "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2422,8 +2207,6 @@ }, "node_modules/@tailwindcss/oxide": { "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", - "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", "dev": true, "license": "MIT", "engines": { @@ -2565,8 +2348,6 @@ }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", - "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", "cpu": [ "x64" ], @@ -2582,8 +2363,6 @@ }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", - "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", "cpu": [ "x64" ], @@ -2723,8 +2502,6 @@ }, "node_modules/@tailwindcss/vite": { "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", - "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", "dev": true, "license": "MIT", "dependencies": { @@ -2738,21 +2515,15 @@ }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "dev": true, "license": "MIT" }, "node_modules/@tweenjs/tween.js": { "version": "23.1.3", - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "license": "MIT", "dependencies": { @@ -2765,8 +2536,6 @@ }, "node_modules/@types/babel__generator": { "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -2775,8 +2544,6 @@ }, "node_modules/@types/babel__template": { "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "license": "MIT", "dependencies": { @@ -2786,8 +2553,6 @@ }, "node_modules/@types/babel__traverse": { "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2796,8 +2561,6 @@ }, "node_modules/@types/cacheable-request": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", "dev": true, "license": "MIT", "dependencies": { @@ -2807,23 +2570,34 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/draco3d": { "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz", - "integrity": "sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==", "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/hast": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -2831,15 +2605,11 @@ }, "node_modules/@types/http-cache-semantics": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", "dev": true, "license": "MIT" }, "node_modules/@types/jsdom": { "version": "27.0.0", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-27.0.0.tgz", - "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -2849,25 +2619,34 @@ }, "node_modules/@types/json-schema": { "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.19.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.8.tgz", - "integrity": "sha512-ebO/Yl+EAvVe8DnMfi+iaAyIqYdK0q/q0y0rw82INWEKJOBe6b/P3YWE8NW7oOlF/nXFNrHwhARrN/hdgDkraA==", "license": "MIT", "peer": true, "dependencies": { @@ -2876,8 +2655,6 @@ }, "node_modules/@types/node-fetch": { "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -2886,33 +2663,23 @@ }, "node_modules/@types/offscreencanvas": { "version": "2019.7.3", - "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", - "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==", "license": "MIT" }, "node_modules/@types/pako": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", - "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", "license": "MIT" }, "node_modules/@types/prismjs": { "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", "license": "MIT" }, "node_modules/@types/raf": { "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", - "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", "license": "MIT", "optional": true }, "node_modules/@types/react": { "version": "19.2.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", - "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", "license": "MIT", "peer": true, "dependencies": { @@ -2921,8 +2688,6 @@ }, "node_modules/@types/react-dom": { "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2931,8 +2696,6 @@ }, "node_modules/@types/react-reconciler": { "version": "0.28.9", - "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", - "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", "license": "MIT", "peerDependencies": { "@types/react": "*" @@ -2940,8 +2703,6 @@ }, "node_modules/@types/react-resizable": { "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/react-resizable/-/react-resizable-3.0.8.tgz", - "integrity": "sha512-Pcvt2eGA7KNXldt1hkhVhAgZ8hK41m0mp89mFgQi7LAAEZiaLgm4fHJ5zbJZ/4m2LVaAyYrrRRv1LHDcrGQanA==", "dev": true, "license": "MIT", "dependencies": { @@ -2950,8 +2711,6 @@ }, "node_modules/@types/react-syntax-highlighter": { "version": "15.5.13", - "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", - "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", "license": "MIT", "dependencies": { "@types/react": "*" @@ -2959,8 +2718,6 @@ }, "node_modules/@types/responselike": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "license": "MIT", "dependencies": { @@ -2969,14 +2726,10 @@ }, "node_modules/@types/stats.js": { "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", - "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", "license": "MIT" }, "node_modules/@types/three": { "version": "0.182.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", - "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", "license": "MIT", "peer": true, "dependencies": { @@ -2991,33 +2744,23 @@ }, "node_modules/@types/tough-cookie": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "license": "MIT" }, "node_modules/@types/trusted-types": { "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", "license": "MIT", "optional": true }, "node_modules/@types/unist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, "node_modules/@types/webxr": { "version": "0.5.24", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", - "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", "license": "MIT" }, "node_modules/@types/yauzl": { "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, "license": "MIT", "optional": true, @@ -3027,8 +2770,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", - "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", "dev": true, "license": "MIT", "dependencies": { @@ -3056,8 +2797,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -3066,8 +2805,6 @@ }, "node_modules/@typescript-eslint/parser": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", - "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", "peer": true, @@ -3092,8 +2829,6 @@ }, "node_modules/@typescript-eslint/project-service": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", - "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", "dev": true, "license": "MIT", "dependencies": { @@ -3114,8 +2849,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", - "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3132,8 +2865,6 @@ }, "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", - "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", "dev": true, "license": "MIT", "engines": { @@ -3149,8 +2880,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", - "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", "dev": true, "license": "MIT", "dependencies": { @@ -3174,8 +2903,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", - "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", "dev": true, "license": "MIT", "engines": { @@ -3188,8 +2915,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", - "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", "dev": true, "license": "MIT", "dependencies": { @@ -3216,8 +2941,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/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": { @@ -3226,8 +2949,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3239,8 +2960,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/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": { @@ -3255,8 +2974,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -3268,8 +2985,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", - "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", "dev": true, "license": "MIT", "dependencies": { @@ -3292,8 +3007,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", - "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3310,8 +3023,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3321,20 +3032,22 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, "node_modules/@urdf-studio/react-robot-canvas": { "resolved": "packages/react-robot-canvas", "link": true }, "node_modules/@use-gesture/core": { "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", - "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", "license": "MIT" }, "node_modules/@use-gesture/react": { "version": "10.3.1", - "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", - "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", "license": "MIT", "dependencies": { "@use-gesture/core": "10.3.1" @@ -3345,8 +3058,6 @@ }, "node_modules/@vitejs/plugin-react": { "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.3.tgz", - "integrity": "sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==", "dev": true, "license": "MIT", "dependencies": { @@ -3366,14 +3077,10 @@ }, "node_modules/@webgpu/types": { "version": "0.1.69", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", - "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", "license": "BSD-3-Clause" }, "node_modules/abort-controller": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -3384,8 +3091,6 @@ }, "node_modules/acorn": { "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "peer": true, @@ -3398,8 +3103,6 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3408,8 +3111,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 14" @@ -3417,8 +3118,6 @@ }, "node_modules/agentkeepalive": { "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", "license": "MIT", "dependencies": { "humanize-ms": "^1.2.1" @@ -3429,8 +3128,6 @@ }, "node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { @@ -3446,8 +3143,6 @@ }, "node_modules/ansi-align": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", "dev": true, "license": "ISC", "dependencies": { @@ -3456,8 +3151,6 @@ }, "node_modules/ansi-align/node_modules/ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "license": "MIT", "engines": { @@ -3466,8 +3159,6 @@ }, "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "license": "MIT", "engines": { @@ -3476,8 +3167,6 @@ }, "node_modules/ansi-align/node_modules/string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "license": "MIT", "dependencies": { @@ -3490,8 +3179,6 @@ }, "node_modules/ansi-align/node_modules/strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "license": "MIT", "dependencies": { @@ -3503,8 +3190,6 @@ }, "node_modules/ansi-escapes": { "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", - "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", "dev": true, "license": "MIT", "dependencies": { @@ -3519,8 +3204,6 @@ }, "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": { @@ -3529,8 +3212,6 @@ }, "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": { @@ -3545,22 +3226,16 @@ }, "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==", "dev": true, "license": "Python-2.0" }, "node_modules/array-ify": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true, "license": "MIT" }, "node_modules/ast-types": { "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "dev": true, "license": "MIT", "dependencies": { @@ -3572,8 +3247,6 @@ }, "node_modules/astral-regex": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, "license": "MIT", "engines": { @@ -3582,14 +3255,10 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/autoprefixer": { "version": "10.4.24", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", - "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", "dev": true, "funding": [ { @@ -3625,8 +3294,6 @@ }, "node_modules/b4a": { "version": "1.7.3", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", - "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -3638,17 +3305,23 @@ } } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "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/bare-events": { "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -3662,8 +3335,6 @@ }, "node_modules/bare-fs": { "version": "4.5.3", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.3.tgz", - "integrity": "sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -3688,8 +3359,6 @@ }, "node_modules/bare-os": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -3699,8 +3368,6 @@ }, "node_modules/bare-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -3710,8 +3377,6 @@ }, "node_modules/bare-stream": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", - "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -3733,8 +3398,6 @@ }, "node_modules/bare-url": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", - "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -3744,8 +3407,6 @@ }, "node_modules/base64-arraybuffer": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -3753,8 +3414,6 @@ }, "node_modules/base64-js": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", @@ -3773,8 +3432,6 @@ }, "node_modules/baseline-browser-mapping": { "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3783,8 +3440,6 @@ }, "node_modules/basic-ftp": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", - "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", "dev": true, "license": "MIT", "engines": { @@ -3793,8 +3448,6 @@ }, "node_modules/bidi-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" @@ -3802,23 +3455,16 @@ }, "node_modules/boolbase": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC" }, "node_modules/boolean": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, "license": "MIT", "optional": true }, "node_modules/boxen": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "dev": true, "license": "MIT", "dependencies": { @@ -3836,8 +3482,6 @@ }, "node_modules/boxen/node_modules/ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "license": "MIT", "engines": { @@ -3846,8 +3490,6 @@ }, "node_modules/boxen/node_modules/is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "license": "MIT", "engines": { @@ -3856,8 +3498,6 @@ }, "node_modules/boxen/node_modules/string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "license": "MIT", "dependencies": { @@ -3870,8 +3510,6 @@ }, "node_modules/boxen/node_modules/strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "license": "MIT", "dependencies": { @@ -3883,8 +3521,6 @@ }, "node_modules/brace-expansion": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -3894,8 +3530,6 @@ }, "node_modules/braces": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { @@ -3907,8 +3541,6 @@ }, "node_modules/browserslist": { "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -3942,8 +3574,6 @@ }, "node_modules/buffer": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -3966,8 +3596,6 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", "engines": { @@ -3976,8 +3604,6 @@ }, "node_modules/cacheable": { "version": "2.3.4", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.4.tgz", - "integrity": "sha512-djgxybDbw9fL/ZWMI3+CE8ZilNxcwFkVtDc1gJ+IlOSSWkSMPQabhV/XCHTQ6pwwN6aivXPZ43omTooZiX06Ew==", "dev": true, "license": "MIT", "dependencies": { @@ -3990,8 +3616,6 @@ }, "node_modules/cacheable-lookup": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, "license": "MIT", "engines": { @@ -4000,8 +3624,6 @@ }, "node_modules/cacheable-request": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, "license": "MIT", "dependencies": { @@ -4019,8 +3641,6 @@ }, "node_modules/cacheable/node_modules/keyv": { "version": "5.6.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", - "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", "dependencies": { @@ -4029,8 +3649,6 @@ }, "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4042,8 +3660,6 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { @@ -4052,8 +3668,6 @@ }, "node_modules/camelcase": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", "dev": true, "license": "MIT", "engines": { @@ -4062,8 +3676,6 @@ }, "node_modules/camera-controls": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.2.tgz", - "integrity": "sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==", "license": "MIT", "engines": { "node": ">=22.0.0", @@ -4075,8 +3687,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001767", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", - "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", "dev": true, "funding": [ { @@ -4096,8 +3706,6 @@ }, "node_modules/canvg": { "version": "3.0.11", - "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", - "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", "license": "MIT", "optional": true, "dependencies": { @@ -4116,8 +3724,6 @@ }, "node_modules/capture-stack-trace": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", - "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", "dev": true, "license": "MIT", "engines": { @@ -4127,10 +3733,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4144,8 +3758,6 @@ }, "node_modules/chalk/node_modules/ansi-styles": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", "dependencies": { @@ -4157,8 +3769,6 @@ }, "node_modules/chalk/node_modules/color-convert": { "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "license": "MIT", "dependencies": { @@ -4167,15 +3777,11 @@ }, "node_modules/chalk/node_modules/color-name": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, "license": "MIT" }, "node_modules/chalk/node_modules/escape-string-regexp": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { @@ -4184,8 +3790,16 @@ }, "node_modules/character-entities": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { "type": "github", @@ -4194,8 +3808,6 @@ }, "node_modules/character-entities-legacy": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", "funding": { "type": "github", @@ -4204,8 +3816,6 @@ }, "node_modules/character-reference-invalid": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "license": "MIT", "funding": { "type": "github", @@ -4214,8 +3824,6 @@ }, "node_modules/chromium-bidi": { "version": "13.0.1", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-13.0.1.tgz", - "integrity": "sha512-c+RLxH0Vg2x2syS9wPw378oJgiJNXtYXUvnVAldUlt5uaHekn0CCU7gPksNgHjrH1qFhmjVXQj4esvuthuC7OQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4228,15 +3836,11 @@ }, "node_modules/ci-info": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", "dev": true, "license": "MIT" }, "node_modules/cli-boxes": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", "dev": true, "license": "MIT", "engines": { @@ -4245,8 +3849,6 @@ }, "node_modules/cli-cursor": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { @@ -4261,8 +3863,6 @@ }, "node_modules/cli-truncate": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", - "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", "dev": true, "license": "MIT", "dependencies": { @@ -4278,8 +3878,6 @@ }, "node_modules/cli-truncate/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": { @@ -4291,8 +3889,6 @@ }, "node_modules/cli-truncate/node_modules/string-width": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", - "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "dev": true, "license": "MIT", "dependencies": { @@ -4308,8 +3904,6 @@ }, "node_modules/cli-truncate/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": { @@ -4324,8 +3918,6 @@ }, "node_modules/cliui": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -4339,8 +3931,6 @@ }, "node_modules/clone-response": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "license": "MIT", "dependencies": { @@ -4352,8 +3942,6 @@ }, "node_modules/clsx": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", "engines": { "node": ">=6" @@ -4361,8 +3949,6 @@ }, "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": { @@ -4374,29 +3960,21 @@ }, "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/colord": { "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true, "license": "MIT" }, "node_modules/colorette": { "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "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==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -4405,10 +3983,14 @@ "node": ">= 0.8" } }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, "node_modules/comma-separated-tokens": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { "type": "github", @@ -4417,8 +3999,6 @@ }, "node_modules/commander": { "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "dev": true, "license": "MIT", "engines": { @@ -4427,8 +4007,6 @@ }, "node_modules/compare-func": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", "dev": true, "license": "MIT", "dependencies": { @@ -4438,8 +4016,6 @@ }, "node_modules/compare-func/node_modules/dot-prop": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4451,8 +4027,6 @@ }, "node_modules/compare-func/node_modules/is-obj": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, "license": "MIT", "engines": { @@ -4461,15 +4035,11 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/configstore": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", - "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4486,8 +4056,6 @@ }, "node_modules/conventional-changelog-angular": { "version": "8.3.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.1.tgz", - "integrity": "sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==", "dev": true, "license": "ISC", "dependencies": { @@ -4499,8 +4067,6 @@ }, "node_modules/conventional-changelog-conventionalcommits": { "version": "9.3.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-9.3.1.tgz", - "integrity": "sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==", "dev": true, "license": "ISC", "dependencies": { @@ -4512,8 +4078,6 @@ }, "node_modules/conventional-commits-parser": { "version": "6.4.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.4.0.tgz", - "integrity": "sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==", "dev": true, "license": "MIT", "dependencies": { @@ -4529,15 +4093,11 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/core-js": { "version": "3.48.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", - "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4548,14 +4108,10 @@ }, "node_modules/core-util-is": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, "node_modules/cosmiconfig": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", - "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", "dev": true, "license": "MIT", "peer": true, @@ -4582,8 +4138,6 @@ }, "node_modules/cosmiconfig-typescript-loader": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.2.0.tgz", - "integrity": "sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4600,8 +4154,6 @@ }, "node_modules/create-error-class": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", "dev": true, "license": "MIT", "dependencies": { @@ -4613,8 +4165,6 @@ }, "node_modules/cross-env": { "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", "license": "MIT", "dependencies": { "cross-spawn": "^7.0.1" @@ -4631,8 +4181,6 @@ }, "node_modules/cross-spawn": { "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4645,8 +4193,6 @@ }, "node_modules/crypto-random-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", "dev": true, "license": "MIT", "engines": { @@ -4655,8 +4201,6 @@ }, "node_modules/css-functions-list": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", - "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", "dev": true, "license": "MIT", "engines": { @@ -4665,8 +4209,6 @@ }, "node_modules/css-line-break": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", - "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "license": "MIT", "dependencies": { "utrie": "^1.0.2" @@ -4674,8 +4216,6 @@ }, "node_modules/css-select": { "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", @@ -4690,8 +4230,6 @@ }, "node_modules/css-tree": { "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", - "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "license": "MIT", "dependencies": { "mdn-data": "2.27.1", @@ -4703,8 +4241,6 @@ }, "node_modules/css-what": { "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "license": "BSD-2-Clause", "engines": { "node": ">= 6" @@ -4715,8 +4251,6 @@ }, "node_modules/cssesc": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", "bin": { @@ -4728,14 +4262,10 @@ }, "node_modules/cssom": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", "license": "MIT" }, "node_modules/cssstyle": { "version": "5.3.7", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", - "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^4.1.1", @@ -4749,8 +4279,6 @@ }, "node_modules/cssstyle/node_modules/lru-cache": { "version": "11.2.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", - "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -4758,14 +4286,10 @@ }, "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-uri-to-buffer": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "dev": true, "license": "MIT", "engines": { @@ -4774,8 +4298,6 @@ }, "node_modules/data-urls": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", - "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", "license": "MIT", "dependencies": { "whatwg-mimetype": "^5.0.0", @@ -4787,8 +4309,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4804,14 +4324,10 @@ }, "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==", "license": "MIT" }, "node_modules/decode-named-character-reference": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", "license": "MIT", "dependencies": { "character-entities": "^2.0.0" @@ -4823,8 +4339,6 @@ }, "node_modules/decompress-response": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4839,8 +4353,6 @@ }, "node_modules/decompress-response/node_modules/mimic-response": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, "license": "MIT", "engines": { @@ -4852,8 +4364,6 @@ }, "node_modules/deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "license": "MIT", "engines": { @@ -4862,15 +4372,11 @@ }, "node_modules/deep-is": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/default-gateway": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4882,8 +4388,6 @@ }, "node_modules/defer-to-connect": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, "license": "MIT", "engines": { @@ -4892,8 +4396,6 @@ }, "node_modules/define-data-property": { "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "license": "MIT", "optional": true, @@ -4911,8 +4413,6 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "license": "MIT", "optional": true, @@ -4930,8 +4430,6 @@ }, "node_modules/degenerator": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4945,17 +4443,22 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-gpu": { "version": "5.0.70", - "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.70.tgz", - "integrity": "sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==", "license": "MIT", "dependencies": { "webgl-constants": "^1.1.1" @@ -4963,8 +4466,6 @@ }, "node_modules/detect-libc": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4973,24 +4474,31 @@ }, "node_modules/detect-node": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, "license": "MIT", "optional": true }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1551306", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1551306.tgz", - "integrity": "sha512-CFx8QdSim8iIv+2ZcEOclBKTQY6BI1IEDa7Tm9YkwAXzEWFndTEzpTo5jAUhSnq24IC7xaDw0wvGcm96+Y3PEg==", "dev": true, "license": "BSD-3-Clause", "peer": true }, "node_modules/dom-serializer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", @@ -5003,8 +4511,6 @@ }, "node_modules/dom-serializer/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" @@ -5015,8 +4521,6 @@ }, "node_modules/domelementtype": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "funding": [ { "type": "github", @@ -5027,8 +4531,6 @@ }, "node_modules/domhandler": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" @@ -5042,8 +4544,6 @@ }, "node_modules/dompurify": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", - "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", "license": "(MPL-2.0 OR Apache-2.0)", "optional": true, "optionalDependencies": { @@ -5052,8 +4552,6 @@ }, "node_modules/domutils": { "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", @@ -5066,8 +4564,6 @@ }, "node_modules/dot-prop": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", - "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5079,14 +4575,10 @@ }, "node_modules/draco3d": { "version": "1.5.7", - "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.7.tgz", - "integrity": "sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==", "license": "Apache-2.0" }, "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==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5099,15 +4591,11 @@ }, "node_modules/duplexer3": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", "dev": true, "license": "BSD-3-Clause" }, "node_modules/electron": { "version": "23.3.13", - "resolved": "https://registry.npmjs.org/electron/-/electron-23.3.13.tgz", - "integrity": "sha512-BaXtHEb+KYKLouUXlUVDa/lj9pj4F5kiE0kwFdJV84Y2EU7euIDgPthfKtchhr5MVHmjtavRMIV/zAwEiSQ9rQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -5125,29 +4613,21 @@ }, "node_modules/electron-to-chromium": { "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "dev": true, "license": "ISC" }, "node_modules/electron/node_modules/@types/node": { "version": "16.18.126", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", - "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", "dev": true, "license": "MIT" }, "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/end-of-stream": { "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "dev": true, "license": "MIT", "dependencies": { @@ -5156,8 +4636,6 @@ }, "node_modules/enhanced-resolve": { "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "license": "MIT", "dependencies": { @@ -5170,8 +4648,6 @@ }, "node_modules/entities": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -5182,8 +4658,6 @@ }, "node_modules/env-paths": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { @@ -5192,8 +4666,6 @@ }, "node_modules/environment": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, "license": "MIT", "engines": { @@ -5205,8 +4677,6 @@ }, "node_modules/error-ex": { "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5215,8 +4685,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5224,8 +4692,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -5233,8 +4699,6 @@ }, "node_modules/es-object-atoms": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5245,8 +4709,6 @@ }, "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5260,16 +4722,12 @@ }, "node_modules/es6-error": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, "license": "MIT", "optional": true }, "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", @@ -5310,8 +4768,6 @@ }, "node_modules/escalade": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -5320,8 +4776,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -5333,8 +4787,6 @@ }, "node_modules/escodegen": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5355,8 +4807,6 @@ }, "node_modules/eslint": { "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", "peer": true, @@ -5416,8 +4866,6 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5433,8 +4881,6 @@ }, "node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5446,8 +4892,6 @@ }, "node_modules/eslint/node_modules/ajv": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -5463,8 +4907,6 @@ }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -5480,8 +4922,6 @@ }, "node_modules/eslint/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": { @@ -5490,15 +4930,11 @@ }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/eslint/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": { @@ -5510,8 +4946,6 @@ }, "node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5528,8 +4962,6 @@ }, "node_modules/esprima": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "license": "BSD-2-Clause", "bin": { @@ -5542,8 +4974,6 @@ }, "node_modules/esquery": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5555,8 +4985,6 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5568,18 +4996,24 @@ }, "node_modules/estraverse": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -5588,8 +5022,6 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "engines": { "node": ">=6" @@ -5597,15 +5029,11 @@ }, "node_modules/eventemitter3": { "version": "5.0.4", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", - "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "dev": true, "license": "MIT" }, "node_modules/events-universal": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", - "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5614,8 +5042,6 @@ }, "node_modules/execa": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", "dependencies": { @@ -5638,8 +5064,6 @@ }, "node_modules/execa/node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", "engines": { @@ -5649,10 +5073,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5672,22 +5100,16 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, "license": "MIT" }, "node_modules/fast-fifo": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true, "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -5703,8 +5125,6 @@ }, "node_modules/fast-glob/node_modules/glob-parent": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -5716,22 +5136,16 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fast-png": { "version": "6.4.0", - "resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz", - "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", "license": "MIT", "dependencies": { "@types/pako": "^2.0.3", @@ -5741,8 +5155,6 @@ }, "node_modules/fast-uri": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { @@ -5758,8 +5170,6 @@ }, "node_modules/fastest-levenshtein": { "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, "license": "MIT", "engines": { @@ -5768,8 +5178,6 @@ }, "node_modules/fastq": { "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { @@ -5778,8 +5186,6 @@ }, "node_modules/fault": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", "license": "MIT", "dependencies": { "format": "^0.2.0" @@ -5791,8 +5197,6 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "license": "MIT", "dependencies": { @@ -5801,8 +5205,6 @@ }, "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": { @@ -5819,14 +5221,10 @@ }, "node_modules/fflate": { "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "license": "MIT" }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5838,8 +5236,6 @@ }, "node_modules/fill-range": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { @@ -5851,8 +5247,6 @@ }, "node_modules/find-up": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -5868,8 +5262,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -5882,15 +5274,11 @@ }, "node_modules/flatted": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/form-data": { "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -5905,22 +5293,16 @@ }, "node_modules/form-data-encoder": { "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", "license": "MIT" }, "node_modules/format": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", "engines": { "node": ">=0.4.x" } }, "node_modules/formdata-node": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", "license": "MIT", "dependencies": { "node-domexception": "1.0.0", @@ -5932,8 +5314,6 @@ }, "node_modules/fraction.js": { "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, "license": "MIT", "engines": { @@ -5946,8 +5326,6 @@ }, "node_modules/fs-extra": { "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { @@ -5976,8 +5354,6 @@ }, "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==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5985,8 +5361,6 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "license": "MIT", "engines": { @@ -5995,8 +5369,6 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { @@ -6005,8 +5377,6 @@ }, "node_modules/get-east-asian-width": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", - "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "dev": true, "license": "MIT", "engines": { @@ -6018,8 +5388,6 @@ }, "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==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -6042,8 +5410,6 @@ }, "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==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -6055,8 +5421,6 @@ }, "node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "license": "MIT", "dependencies": { @@ -6071,8 +5435,6 @@ }, "node_modules/get-tsconfig": { "version": "4.13.7", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", - "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", "dev": true, "license": "MIT", "optional": true, @@ -6085,8 +5447,6 @@ }, "node_modules/get-uri": { "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "dev": true, "license": "MIT", "dependencies": { @@ -6100,8 +5460,6 @@ }, "node_modules/git-raw-commits": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", - "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6117,8 +5475,6 @@ }, "node_modules/glob-parent": { "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { @@ -6130,8 +5486,6 @@ }, "node_modules/global-agent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -6149,8 +5503,6 @@ }, "node_modules/global-agent/node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "optional": true, @@ -6163,8 +5515,6 @@ }, "node_modules/global-directory": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", - "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6179,8 +5529,6 @@ }, "node_modules/global-directory/node_modules/ini": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, "license": "ISC", "engines": { @@ -6189,8 +5537,6 @@ }, "node_modules/global-dirs": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", "dev": true, "license": "MIT", "dependencies": { @@ -6202,8 +5548,6 @@ }, "node_modules/global-modules": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", "dev": true, "license": "MIT", "dependencies": { @@ -6215,8 +5559,6 @@ }, "node_modules/global-prefix": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "dev": true, "license": "MIT", "dependencies": { @@ -6230,8 +5572,6 @@ }, "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { @@ -6243,8 +5583,6 @@ }, "node_modules/globals": { "version": "17.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", - "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", "dev": true, "license": "MIT", "engines": { @@ -6256,8 +5594,6 @@ }, "node_modules/globalthis": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "license": "MIT", "optional": true, @@ -6274,8 +5610,6 @@ }, "node_modules/globby": { "version": "16.2.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz", - "integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6295,8 +5629,6 @@ }, "node_modules/globby/node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -6305,8 +5637,6 @@ }, "node_modules/globby/node_modules/is-path-inside": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", "dev": true, "license": "MIT", "engines": { @@ -6318,21 +5648,15 @@ }, "node_modules/globjoin": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true, "license": "MIT" }, "node_modules/glsl-noise": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", - "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==", "license": "MIT" }, "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -6343,8 +5667,6 @@ }, "node_modules/got": { "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, "license": "MIT", "dependencies": { @@ -6369,15 +5691,11 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true, "license": "ISC" }, "node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { @@ -6386,8 +5704,6 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "license": "MIT", "optional": true, @@ -6400,8 +5716,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -6412,8 +5726,6 @@ }, "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==", "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -6427,8 +5739,6 @@ }, "node_modules/hashery": { "version": "1.5.1", - "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", - "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6440,8 +5750,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -6452,8 +5760,46 @@ }, "node_modules/hast-util-parse-selector": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -6465,8 +5811,6 @@ }, "node_modules/hastscript": { "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -6482,8 +5826,6 @@ }, "node_modules/highlight.js": { "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "license": "BSD-3-Clause", "engines": { "node": "*" @@ -6491,27 +5833,19 @@ }, "node_modules/highlightjs-vue": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", "license": "CC0-1.0" }, "node_modules/hls.js": { "version": "1.6.15", - "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz", - "integrity": "sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==", "license": "Apache-2.0" }, "node_modules/hookified": { "version": "1.15.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", - "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", "dev": true, "license": "MIT" }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", - "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.6.0" @@ -6522,14 +5856,10 @@ }, "node_modules/html-escaper": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", "license": "MIT" }, "node_modules/html-tags": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-5.1.0.tgz", - "integrity": "sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==", "dev": true, "license": "MIT", "engines": { @@ -6539,10 +5869,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/html2canvas": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", - "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "license": "MIT", "dependencies": { "css-line-break": "^2.1.0", @@ -6554,8 +5892,6 @@ }, "node_modules/htmlparser2": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", - "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -6573,8 +5909,6 @@ }, "node_modules/htmlparser2/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" @@ -6585,15 +5919,11 @@ }, "node_modules/http-cache-semantics": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "dev": true, "license": "BSD-2-Clause" }, "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==", "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -6605,8 +5935,6 @@ }, "node_modules/http2-wrapper": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, "license": "MIT", "dependencies": { @@ -6619,8 +5947,6 @@ }, "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==", "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -6632,8 +5958,6 @@ }, "node_modules/human-signals": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -6642,8 +5966,6 @@ }, "node_modules/humanize-ms": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "license": "MIT", "dependencies": { "ms": "^2.0.0" @@ -6651,8 +5973,6 @@ }, "node_modules/husky": { "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { @@ -6667,8 +5987,6 @@ }, "node_modules/ieee754": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -6687,8 +6005,6 @@ }, "node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -6697,14 +6013,10 @@ }, "node_modules/immediate": { "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "license": "MIT" }, "node_modules/immer": { "version": "11.1.3", - "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz", - "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==", "license": "MIT", "peer": true, "funding": { @@ -6714,8 +6026,6 @@ }, "node_modules/import-fresh": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6731,8 +6041,6 @@ }, "node_modules/import-lazy": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", "dev": true, "license": "MIT", "engines": { @@ -6741,8 +6049,6 @@ }, "node_modules/import-meta-resolve": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", "dev": true, "license": "MIT", "funding": { @@ -6752,8 +6058,6 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { @@ -6762,21 +6066,21 @@ }, "node_modules/inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "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/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, "node_modules/internal-ip": { "version": "6.2.0", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-6.2.0.tgz", - "integrity": "sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==", "dev": true, "license": "MIT", "dependencies": { @@ -6794,14 +6098,10 @@ }, "node_modules/iobuffer": { "version": "5.4.0", - "resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz", - "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", "license": "MIT" }, "node_modules/ip-address": { "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", "engines": { @@ -6810,8 +6110,6 @@ }, "node_modules/ip-regex": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", "dev": true, "license": "MIT", "engines": { @@ -6820,8 +6118,6 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "license": "MIT", "engines": { @@ -6830,8 +6126,6 @@ }, "node_modules/is-alphabetical": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "license": "MIT", "funding": { "type": "github", @@ -6840,8 +6134,6 @@ }, "node_modules/is-alphanumerical": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", "license": "MIT", "dependencies": { "is-alphabetical": "^2.0.0", @@ -6854,15 +6146,11 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, "license": "MIT" }, "node_modules/is-ci": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "dev": true, "license": "MIT", "dependencies": { @@ -6874,8 +6162,6 @@ }, "node_modules/is-decimal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", "license": "MIT", "funding": { "type": "github", @@ -6884,8 +6170,6 @@ }, "node_modules/is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -6894,8 +6178,6 @@ }, "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": { @@ -6904,8 +6186,6 @@ }, "node_modules/is-glob": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -6917,8 +6197,6 @@ }, "node_modules/is-hexadecimal": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", "license": "MIT", "funding": { "type": "github", @@ -6927,8 +6205,6 @@ }, "node_modules/is-installed-globally": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", "dev": true, "license": "MIT", "dependencies": { @@ -6941,8 +6217,6 @@ }, "node_modules/is-ip": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", - "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6954,8 +6228,6 @@ }, "node_modules/is-npm": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", "dev": true, "license": "MIT", "engines": { @@ -6964,8 +6236,6 @@ }, "node_modules/is-number": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { @@ -6974,8 +6244,6 @@ }, "node_modules/is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", "dev": true, "license": "MIT", "engines": { @@ -6984,8 +6252,6 @@ }, "node_modules/is-path-inside": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", "dev": true, "license": "MIT", "dependencies": { @@ -6997,9 +6263,6 @@ }, "node_modules/is-plain-obj": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -7010,8 +6273,6 @@ }, "node_modules/is-plain-object": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, "license": "MIT", "engines": { @@ -7020,20 +6281,14 @@ }, "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==", "license": "MIT" }, "node_modules/is-promise": { "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "license": "MIT" }, "node_modules/is-redirect": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", "dev": true, "license": "MIT", "engines": { @@ -7042,8 +6297,6 @@ }, "node_modules/is-retry-allowed": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", "dev": true, "license": "MIT", "engines": { @@ -7052,8 +6305,6 @@ }, "node_modules/is-stream": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", "engines": { @@ -7065,20 +6316,14 @@ }, "node_modules/isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/its-fine": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/its-fine/-/its-fine-2.0.0.tgz", - "integrity": "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==", "license": "MIT", "dependencies": { "@types/react-reconciler": "^0.28.9" @@ -7089,8 +6334,6 @@ }, "node_modules/jiti": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", "bin": { @@ -7099,14 +6342,10 @@ }, "node_modules/js-tokens": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -7118,8 +6357,6 @@ }, "node_modules/jsdom": { "version": "28.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-28.0.0.tgz", - "integrity": "sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==", "license": "MIT", "dependencies": { "@acemir/cssom": "^0.9.31", @@ -7157,8 +6394,6 @@ }, "node_modules/jsdom/node_modules/parse5": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -7169,8 +6404,6 @@ }, "node_modules/jsesc": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", "bin": { @@ -7182,44 +6415,32 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, "license": "ISC", "optional": true }, "node_modules/json5": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "license": "MIT", "bin": { @@ -7231,8 +6452,6 @@ }, "node_modules/jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", "optionalDependencies": { @@ -7241,8 +6460,6 @@ }, "node_modules/jspdf": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.1.0.tgz", - "integrity": "sha512-xd1d/XRkwqnsq6FP3zH1Q+Ejqn2ULIJeDZ+FTKpaabVpZREjsJKRJwuokTNgdqOU+fl55KgbvgZ1pRTSWCP2kQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", @@ -7258,8 +6475,6 @@ }, "node_modules/jszip": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "license": "(MIT OR GPL-3.0-or-later)", "dependencies": { "lie": "~3.3.0", @@ -7270,14 +6485,10 @@ }, "node_modules/jszip/node_modules/pako": { "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -7286,8 +6497,6 @@ }, "node_modules/kind-of": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "license": "MIT", "engines": { @@ -7296,8 +6505,6 @@ }, "node_modules/latest-version": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", "dev": true, "license": "MIT", "dependencies": { @@ -7309,8 +6516,6 @@ }, "node_modules/levn": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7321,10 +6526,17 @@ "node": ">= 0.8.0" } }, + "node_modules/libarchive.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/libarchive.js/-/libarchive.js-2.0.2.tgz", + "integrity": "sha512-JHb+P4suNSjvz/dMdRgOe7JAxluXJeialzSFkKHU5y0ZK+m175drPOaIYW6I9WXSDcPcQ13eCUgMnpgY0ggmoQ==", + "license": "MIT", + "dependencies": { + "comlink": "^4.4.1" + } + }, "node_modules/lie": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "license": "MIT", "dependencies": { "immediate": "~3.0.5" @@ -7332,8 +6544,6 @@ }, "node_modules/lightningcss": { "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", - "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -7509,8 +6719,6 @@ }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", "cpu": [ "x64" ], @@ -7530,8 +6738,6 @@ }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", "cpu": [ "x64" ], @@ -7593,15 +6799,11 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true, "license": "MIT" }, "node_modules/linkedom": { "version": "0.18.12", - "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", - "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", "license": "ISC", "dependencies": { "css-select": "^5.1.0", @@ -7624,8 +6826,6 @@ }, "node_modules/lint-staged": { "version": "16.4.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-16.4.0.tgz", - "integrity": "sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==", "dev": true, "license": "MIT", "dependencies": { @@ -7648,8 +6848,6 @@ }, "node_modules/listr2": { "version": "9.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", - "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "dev": true, "license": "MIT", "dependencies": { @@ -7666,8 +6864,6 @@ }, "node_modules/listr2/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": { @@ -7679,8 +6875,6 @@ }, "node_modules/listr2/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, "license": "MIT", "engines": { @@ -7692,15 +6886,11 @@ }, "node_modules/listr2/node_modules/emoji-regex": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, "node_modules/listr2/node_modules/string-width": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7717,8 +6907,6 @@ }, "node_modules/listr2/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": { @@ -7733,8 +6921,6 @@ }, "node_modules/listr2/node_modules/wrap-ansi": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { @@ -7751,8 +6937,6 @@ }, "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -7767,64 +6951,46 @@ }, "node_modules/lodash.camelcase": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true, "license": "MIT" }, "node_modules/lodash.kebabcase": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true, "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", "dev": true, "license": "MIT" }, "node_modules/lodash.startcase": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", "dev": true, "license": "MIT" }, "node_modules/lodash.truncate": { "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true, "license": "MIT" }, "node_modules/lodash.upperfirst": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", "dev": true, "license": "MIT" }, "node_modules/log-update": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "license": "MIT", "dependencies": { @@ -7843,8 +7009,6 @@ }, "node_modules/log-update/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": { @@ -7856,8 +7020,6 @@ }, "node_modules/log-update/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, "license": "MIT", "engines": { @@ -7869,15 +7031,11 @@ }, "node_modules/log-update/node_modules/emoji-regex": { "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7892,8 +7050,6 @@ }, "node_modules/log-update/node_modules/slice-ansi": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "dev": true, "license": "MIT", "dependencies": { @@ -7909,8 +7065,6 @@ }, "node_modules/log-update/node_modules/string-width": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7927,8 +7081,6 @@ }, "node_modules/log-update/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": { @@ -7943,8 +7095,6 @@ }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { @@ -7959,10 +7109,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -7973,8 +7131,6 @@ }, "node_modules/lowercase-keys": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, "license": "MIT", "engines": { @@ -7983,8 +7139,6 @@ }, "node_modules/lowlight": { "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", "license": "MIT", "dependencies": { "fault": "^1.0.0", @@ -7997,8 +7151,6 @@ }, "node_modules/lru-cache": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "license": "ISC", "dependencies": { @@ -8007,8 +7159,6 @@ }, "node_modules/lucide-react": { "version": "0.555.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.555.0.tgz", - "integrity": "sha512-D8FvHUGbxWBRQM90NZeIyhAvkFfsh3u9ekrMvJ30Z6gnpBHS6HC6ldLg7tL45hwiIz/u66eKDtdA23gwwGsAHA==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -8016,8 +7166,6 @@ }, "node_modules/maath": { "version": "0.10.8", - "resolved": "https://registry.npmjs.org/maath/-/maath-0.10.8.tgz", - "integrity": "sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==", "license": "MIT", "peerDependencies": { "@types/three": ">=0.134.0", @@ -8026,8 +7174,6 @@ }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -8036,8 +7182,6 @@ }, "node_modules/make-dir": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8047,10 +7191,18 @@ "node": ">=4" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/marked": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", - "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -8061,8 +7213,6 @@ }, "node_modules/matcher": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, "license": "MIT", "optional": true, @@ -8075,8 +7225,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -8084,8 +7232,6 @@ }, "node_modules/mathml-tag-names": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-4.0.0.tgz", - "integrity": "sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==", "dev": true, "license": "MIT", "funding": { @@ -8093,172 +7239,983 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/mdn-data": { - "version": "2.27.1", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", - "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "license": "CC0-1.0" - }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "dev": true, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meshline": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.3.1.tgz", - "integrity": "sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==", + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", "license": "MIT", - "peerDependencies": { - "three": ">=0.137" + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/meshoptimizer": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", - "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "license": "MIT", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=8.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", "license": "MIT", - "engines": { - "node": ">= 0.6" + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">= 0.6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": "*" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "license": "CC0-1.0" + }, + "node_modules/meow": { + "version": "13.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/meshline": { + "version": "3.3.1", + "license": "MIT", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "0.22.0", + "license": "MIT" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mitt": { + "version": "3.0.1", "dev": true, "license": "MIT" }, "node_modules/monaco-editor": { "version": "0.55.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", - "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", "license": "MIT", "peer": true, "dependencies": { @@ -8268,8 +8225,6 @@ }, "node_modules/monaco-editor/node_modules/dompurify": { "version": "3.2.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", - "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -8277,14 +8232,10 @@ }, "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==", "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -8302,15 +8253,11 @@ }, "node_modules/natural-compare": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/netmask": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "dev": true, "license": "MIT", "engines": { @@ -8319,9 +8266,6 @@ }, "node_modules/node-domexception": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -8339,8 +8283,6 @@ }, "node_modules/node-fetch": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -8359,20 +8301,14 @@ }, "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause" }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -8381,15 +8317,11 @@ }, "node_modules/node-releases": { "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { @@ -8398,8 +8330,6 @@ }, "node_modules/normalize-url": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, "license": "MIT", "engines": { @@ -8411,8 +8341,6 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "license": "MIT", "dependencies": { @@ -8424,8 +8352,6 @@ }, "node_modules/nth-check": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" @@ -8436,8 +8362,6 @@ }, "node_modules/object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8445,8 +8369,6 @@ }, "node_modules/object-keys": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", "optional": true, @@ -8456,8 +8378,6 @@ }, "node_modules/once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "license": "ISC", "dependencies": { @@ -8466,8 +8386,6 @@ }, "node_modules/onetime": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", "dependencies": { @@ -8482,8 +8400,6 @@ }, "node_modules/openai": { "version": "4.104.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", - "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", "license": "Apache-2.0", "dependencies": { "@types/node": "^18.11.18", @@ -8512,8 +8428,6 @@ }, "node_modules/openai/node_modules/@types/node": { "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -8521,14 +8435,10 @@ }, "node_modules/openai/node_modules/undici-types": { "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, "node_modules/optionator": { "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { @@ -8545,8 +8455,6 @@ }, "node_modules/p-cancelable": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, "license": "MIT", "engines": { @@ -8555,8 +8463,6 @@ }, "node_modules/p-event": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", - "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8571,8 +8477,6 @@ }, "node_modules/p-finally": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "dev": true, "license": "MIT", "engines": { @@ -8581,8 +8485,6 @@ }, "node_modules/p-limit": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8597,8 +8499,6 @@ }, "node_modules/p-locate": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -8613,8 +8513,6 @@ }, "node_modules/p-timeout": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "dev": true, "license": "MIT", "dependencies": { @@ -8626,8 +8524,6 @@ }, "node_modules/pac-proxy-agent": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "license": "MIT", "dependencies": { @@ -8646,8 +8542,6 @@ }, "node_modules/pac-resolver": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "license": "MIT", "dependencies": { @@ -8660,8 +8554,6 @@ }, "node_modules/package-json": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", "dev": true, "license": "MIT", "dependencies": { @@ -8676,8 +8568,6 @@ }, "node_modules/package-json/node_modules/get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, "license": "MIT", "engines": { @@ -8686,8 +8576,6 @@ }, "node_modules/package-json/node_modules/got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", "dev": true, "license": "MIT", "dependencies": { @@ -8709,8 +8597,6 @@ }, "node_modules/package-json/node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { @@ -8719,8 +8605,6 @@ }, "node_modules/package-json/node_modules/lowercase-keys": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, "license": "MIT", "engines": { @@ -8729,8 +8613,6 @@ }, "node_modules/package-json/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -8739,14 +8621,10 @@ }, "node_modules/pako": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { @@ -8758,8 +8636,6 @@ }, "node_modules/parse-entities": { "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", @@ -8777,14 +8653,10 @@ }, "node_modules/parse-entities/node_modules/@types/unist": { "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, "node_modules/parse-json": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -8802,8 +8674,6 @@ }, "node_modules/parse5": { "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "license": "MIT", "dependencies": { "entities": "^6.0.0" @@ -8814,8 +8684,6 @@ }, "node_modules/path-exists": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -8824,15 +8692,11 @@ }, "node_modules/path-is-inside": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true, "license": "(WTFPL OR MIT)" }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -8840,29 +8704,21 @@ }, "node_modules/pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, "license": "MIT" }, "node_modules/performance-now": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "license": "MIT", "optional": true }, "node_modules/picocolors": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -8874,8 +8730,6 @@ }, "node_modules/pify": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "license": "MIT", "engines": { @@ -8884,8 +8738,6 @@ }, "node_modules/postcss": { "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, "funding": [ { @@ -8914,8 +8766,6 @@ }, "node_modules/postcss-safe-parser": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.1.tgz", - "integrity": "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==", "dev": true, "funding": [ { @@ -8941,8 +8791,6 @@ }, "node_modules/postcss-selector-parser": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", "peer": true, @@ -8956,21 +8804,15 @@ }, "node_modules/postcss-value-parser": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true, "license": "MIT" }, "node_modules/potpack": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", - "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", "license": "ISC" }, "node_modules/prelude-ls": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { @@ -8979,8 +8821,6 @@ }, "node_modules/prepend-http": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", "dev": true, "license": "MIT", "engines": { @@ -8989,8 +8829,6 @@ }, "node_modules/prettier": { "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", "bin": { @@ -9005,8 +8843,6 @@ }, "node_modules/prismjs": { "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", "license": "MIT", "engines": { "node": ">=6" @@ -9014,14 +8850,10 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", "engines": { @@ -9030,8 +8862,6 @@ }, "node_modules/promise-worker-transferable": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/promise-worker-transferable/-/promise-worker-transferable-1.0.4.tgz", - "integrity": "sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==", "license": "Apache-2.0", "dependencies": { "is-promise": "^2.1.0", @@ -9040,8 +8870,6 @@ }, "node_modules/prop-types": { "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -9051,8 +8879,6 @@ }, "node_modules/property-information": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", "license": "MIT", "funding": { "type": "github", @@ -9061,8 +8887,6 @@ }, "node_modules/proxy-agent": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dev": true, "license": "MIT", "dependencies": { @@ -9081,8 +8905,6 @@ }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, "license": "ISC", "engines": { @@ -9091,22 +8913,16 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true, "license": "MIT" }, "node_modules/pseudomap": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", "dev": true, "license": "ISC" }, "node_modules/pump": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, "license": "MIT", "dependencies": { @@ -9116,8 +8932,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", "engines": { "node": ">=6" @@ -9125,8 +8939,6 @@ }, "node_modules/puppeteer": { "version": "24.36.1", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.36.1.tgz", - "integrity": "sha512-uPiDUyf7gd7Il1KnqfNUtHqntL0w1LapEw5Zsuh8oCK8GsqdxySX1PzdIHKB2Dw273gWY4MW0zC5gy3Re9XlqQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -9147,8 +8959,6 @@ }, "node_modules/puppeteer-core": { "version": "24.36.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.36.1.tgz", - "integrity": "sha512-L7ykMWc3lQf3HS7ME3PSjp7wMIjJeW6+bKfH/RSTz5l6VUDGubnrC2BKj3UvM28Y5PMDFW0xniJOZHBZPpW1dQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -9166,8 +8976,6 @@ }, "node_modules/qified": { "version": "0.9.1", - "resolved": "https://registry.npmjs.org/qified/-/qified-0.9.1.tgz", - "integrity": "sha512-n7mar4T0xQ+39dE2vGTAlbxUEpndwPANH0kDef1/MYsB8Bba9wshkybIRx74qgcvKQPEWErf9AqAdYjhzY2Ilg==", "dev": true, "license": "MIT", "dependencies": { @@ -9179,15 +8987,11 @@ }, "node_modules/qified/node_modules/hookified": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.1.1.tgz", - "integrity": "sha512-AHb76R16GB5EsPBE2J7Ko5kiEyXwviB9P5SMrAKcuAu4vJPZttViAbj9+tZeaQE5zjDme+1vcHP78Yj/WoAveA==", "dev": true, "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, "funding": [ { @@ -9207,8 +9011,6 @@ }, "node_modules/quick-lru": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, "license": "MIT", "engines": { @@ -9220,8 +9022,6 @@ }, "node_modules/raf": { "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", "license": "MIT", "optional": true, "dependencies": { @@ -9230,8 +9030,6 @@ }, "node_modules/rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { @@ -9246,8 +9044,6 @@ }, "node_modules/react": { "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", "peer": true, "engines": { @@ -9256,8 +9052,6 @@ }, "node_modules/react-devtools": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/react-devtools/-/react-devtools-7.0.1.tgz", - "integrity": "sha512-I2UXoJlsqNeN3uCrlrJw0V+K6HTHU5Q1x+BWJlF99hBC6A/lKhe+IuITZXyKb8BluMReSBYUhUYGkJGgr2KPQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9274,8 +9068,6 @@ }, "node_modules/react-devtools-core": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-7.0.1.tgz", - "integrity": "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw==", "dev": true, "license": "MIT", "dependencies": { @@ -9285,8 +9077,6 @@ }, "node_modules/react-devtools-core/node_modules/ws": { "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "license": "MIT", "engines": { @@ -9307,8 +9097,6 @@ }, "node_modules/react-devtools/node_modules/cross-spawn": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, "license": "MIT", "dependencies": { @@ -9319,8 +9107,6 @@ }, "node_modules/react-devtools/node_modules/lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "license": "ISC", "dependencies": { @@ -9330,8 +9116,6 @@ }, "node_modules/react-devtools/node_modules/shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -9343,8 +9127,6 @@ }, "node_modules/react-devtools/node_modules/shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { @@ -9353,8 +9135,6 @@ }, "node_modules/react-devtools/node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { @@ -9366,15 +9146,11 @@ }, "node_modules/react-devtools/node_modules/yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true, "license": "ISC" }, "node_modules/react-dom": { "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", "peer": true, "dependencies": { @@ -9386,8 +9162,6 @@ }, "node_modules/react-draggable": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz", - "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==", "license": "MIT", "dependencies": { "clsx": "^2.1.1", @@ -9400,14 +9174,37 @@ }, "node_modules/react-is": { "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-refresh": { "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", "dev": true, "license": "MIT", "engines": { @@ -9416,8 +9213,6 @@ }, "node_modules/react-resizable": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.1.3.tgz", - "integrity": "sha512-liJBNayhX7qA4tBJiBD321FDhJxgGTJ07uzH5zSORXoE8h7PyEZ8mLqmosST7ppf6C4zUsbd2gzDMmBCfFp9Lw==", "license": "MIT", "dependencies": { "prop-types": "15.x", @@ -9430,8 +9225,6 @@ }, "node_modules/react-simple-code-editor": { "version": "0.14.1", - "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz", - "integrity": "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==", "license": "MIT", "peerDependencies": { "react": ">=16.8.0", @@ -9440,8 +9233,6 @@ }, "node_modules/react-syntax-highlighter": { "version": "16.1.0", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz", - "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.28.4", @@ -9460,8 +9251,6 @@ }, "node_modules/react-use-measure": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", - "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", "license": "MIT", "peerDependencies": { "react": ">=16.13", @@ -9475,8 +9264,6 @@ }, "node_modules/readable-stream": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", @@ -9490,8 +9277,6 @@ }, "node_modules/refractor": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", - "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -9506,15 +9291,11 @@ }, "node_modules/regenerator-runtime": { "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "license": "MIT", "optional": true }, "node_modules/registry-auth-token": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "dev": true, "license": "MIT", "dependencies": { @@ -9524,8 +9305,6 @@ }, "node_modules/registry-url": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", "dev": true, "license": "MIT", "dependencies": { @@ -9535,10 +9314,74 @@ "node": ">=0.10.0" } }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-directory": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -9547,8 +9390,6 @@ }, "node_modules/require-from-string": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9556,15 +9397,11 @@ }, "node_modules/resolve-alpn": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true, "license": "MIT" }, "node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { @@ -9573,8 +9410,6 @@ }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, "license": "MIT", "optional": true, @@ -9584,8 +9419,6 @@ }, "node_modules/responselike": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "dev": true, "license": "MIT", "dependencies": { @@ -9597,8 +9430,6 @@ }, "node_modules/restore-cursor": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "license": "MIT", "dependencies": { @@ -9614,8 +9445,6 @@ }, "node_modules/restore-cursor/node_modules/onetime": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9630,8 +9459,6 @@ }, "node_modules/restore-cursor/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": { @@ -9643,8 +9470,6 @@ }, "node_modules/reusify": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { @@ -9654,15 +9479,11 @@ }, "node_modules/rfdc": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true, "license": "MIT" }, "node_modules/rgbcolor": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", "optional": true, "engines": { @@ -9671,8 +9492,6 @@ }, "node_modules/roarr": { "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -9690,8 +9509,6 @@ }, "node_modules/rollup": { "version": "4.57.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", - "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", "dev": true, "license": "MIT", "dependencies": { @@ -9735,8 +9552,6 @@ }, "node_modules/run-parallel": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "funding": [ { @@ -9759,14 +9574,10 @@ }, "node_modules/safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, "node_modules/saxes": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -9777,14 +9588,10 @@ }, "node_modules/scheduler": { "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -9793,16 +9600,12 @@ }, "node_modules/semver-compare": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true, "license": "MIT", "optional": true }, "node_modules/semver-diff": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==", "dev": true, "license": "MIT", "dependencies": { @@ -9814,8 +9617,6 @@ }, "node_modules/semver-diff/node_modules/semver": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -9824,8 +9625,6 @@ }, "node_modules/serialize-error": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, "license": "MIT", "optional": true, @@ -9841,14 +9640,10 @@ }, "node_modules/setimmediate": { "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT" }, "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==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -9859,8 +9654,6 @@ }, "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==", "license": "MIT", "engines": { "node": ">=8" @@ -9868,8 +9661,6 @@ }, "node_modules/shell-quote": { "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -9881,15 +9672,11 @@ }, "node_modules/signal-exit": { "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, "license": "ISC" }, "node_modules/slash": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "license": "MIT", "engines": { @@ -9901,8 +9688,6 @@ }, "node_modules/slice-ansi": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", - "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", "dev": true, "license": "MIT", "dependencies": { @@ -9918,8 +9703,6 @@ }, "node_modules/slice-ansi/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, "license": "MIT", "engines": { @@ -9931,8 +9714,6 @@ }, "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9947,8 +9728,6 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true, "license": "MIT", "engines": { @@ -9958,8 +9737,6 @@ }, "node_modules/socks": { "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dev": true, "license": "MIT", "dependencies": { @@ -9973,8 +9750,6 @@ }, "node_modules/socks-proxy-agent": { "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", "dependencies": { @@ -9988,8 +9763,6 @@ }, "node_modules/source-map": { "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", "optional": true, @@ -9999,8 +9772,6 @@ }, "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" @@ -10008,8 +9779,6 @@ }, "node_modules/space-separated-tokens": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "license": "MIT", "funding": { "type": "github", @@ -10018,16 +9787,12 @@ }, "node_modules/sprintf-js": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, "license": "BSD-3-Clause", "optional": true }, "node_modules/stackblur-canvas": { "version": "2.7.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", - "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", "license": "MIT", "optional": true, "engines": { @@ -10036,14 +9801,10 @@ }, "node_modules/state-local": { "version": "1.0.7", - "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", - "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", "license": "MIT" }, "node_modules/stats-gl": { "version": "2.4.2", - "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-2.4.2.tgz", - "integrity": "sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==", "license": "MIT", "dependencies": { "@types/three": "*", @@ -10056,20 +9817,14 @@ }, "node_modules/stats-gl/node_modules/three": { "version": "0.170.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.170.0.tgz", - "integrity": "sha512-FQK+LEpYc0fBD+J8g6oSEyyNzjp+Q7Ks1C568WWaoMRLW+TkNNWmenWeGgJjV105Gd+p/2ql1ZcjYvNiPZBhuQ==", "license": "MIT" }, "node_modules/stats.js": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", - "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==", "license": "MIT" }, "node_modules/streamx": { "version": "2.23.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", - "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", "dev": true, "license": "MIT", "dependencies": { @@ -10080,8 +9835,6 @@ }, "node_modules/string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" @@ -10089,8 +9842,6 @@ }, "node_modules/string-argv": { "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "license": "MIT", "engines": { @@ -10099,8 +9850,6 @@ }, "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": { @@ -10112,10 +9861,22 @@ "node": ">=8" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "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": { @@ -10127,8 +9888,6 @@ }, "node_modules/strip-bom": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "license": "MIT", "engines": { @@ -10137,8 +9896,6 @@ }, "node_modules/strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", "dev": true, "license": "MIT", "engines": { @@ -10147,8 +9904,6 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "license": "MIT", "engines": { @@ -10157,18 +9912,32 @@ }, "node_modules/strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/stylelint": { "version": "17.6.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.6.0.tgz", - "integrity": "sha512-tokrsMIVAR9vAQ/q3UVEr7S0dGXCi7zkCezPRnS2kqPUulvUh5Vgfwngrk4EoAoW7wnrThqTdnTFN5Ra7CaxIg==", "dev": true, "funding": [ { @@ -10228,8 +9997,6 @@ }, "node_modules/stylelint-config-recommended": { "version": "18.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-18.0.0.tgz", - "integrity": "sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==", "dev": true, "funding": [ { @@ -10251,8 +10018,6 @@ }, "node_modules/stylelint-config-standard": { "version": "40.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-40.0.0.tgz", - "integrity": "sha512-EznGJxOUhtWck2r6dJpbgAdPATIzvpLdK9+i5qPd4Lx70es66TkBPljSg4wN3Qnc6c4h2n+WbUrUynQ3fanjHw==", "dev": true, "funding": [ { @@ -10277,8 +10042,6 @@ }, "node_modules/stylelint/node_modules/@csstools/css-calc": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", - "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", "dev": true, "funding": [ { @@ -10301,8 +10064,6 @@ }, "node_modules/stylelint/node_modules/@csstools/css-parser-algorithms": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", "dev": true, "funding": [ { @@ -10325,8 +10086,6 @@ }, "node_modules/stylelint/node_modules/@csstools/css-tokenizer": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", "dev": true, "funding": [ { @@ -10346,8 +10105,6 @@ }, "node_modules/stylelint/node_modules/@csstools/media-query-list-parser": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-5.0.0.tgz", - "integrity": "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==", "dev": true, "funding": [ { @@ -10370,8 +10127,6 @@ }, "node_modules/stylelint/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": { @@ -10383,8 +10138,6 @@ }, "node_modules/stylelint/node_modules/file-entry-cache": { "version": "11.1.2", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.2.tgz", - "integrity": "sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==", "dev": true, "license": "MIT", "dependencies": { @@ -10393,8 +10146,6 @@ }, "node_modules/stylelint/node_modules/flat-cache": { "version": "6.1.22", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.22.tgz", - "integrity": "sha512-N2dnzVJIphnNsjHcrxGW7DePckJ6haPrSFqpsBUhHYgwtKGVq4JrBGielEGD2fCVnsGm1zlBVZ8wGhkyuetgug==", "dev": true, "license": "MIT", "dependencies": { @@ -10405,8 +10156,6 @@ }, "node_modules/stylelint/node_modules/ignore": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -10415,8 +10164,6 @@ }, "node_modules/stylelint/node_modules/meow": { "version": "14.1.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-14.1.0.tgz", - "integrity": "sha512-EDYo6VlmtnumlcBCbh1gLJ//9jvM/ndXHfVXIFrZVr6fGcwTUyCTFNTLCKuY3ffbK8L/+3Mzqnd58RojiZqHVw==", "dev": true, "license": "MIT", "engines": { @@ -10428,8 +10175,6 @@ }, "node_modules/stylelint/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": { @@ -10441,8 +10186,6 @@ }, "node_modules/stylelint/node_modules/string-width": { "version": "8.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", - "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "dev": true, "license": "MIT", "dependencies": { @@ -10458,8 +10201,6 @@ }, "node_modules/stylelint/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": { @@ -10474,8 +10215,6 @@ }, "node_modules/stylelint/node_modules/write-file-atomic": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-7.0.1.tgz", - "integrity": "sha512-OTIk8iR8/aCRWBqvxrzxR0hgxWpnYBblY1S5hDWBQfk/VFmJwzmJgQFN3WsoUKHISv2eAwe+PpbUzyL1CKTLXg==", "dev": true, "license": "ISC", "dependencies": { @@ -10487,8 +10226,6 @@ }, "node_modules/sumchecker": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -10500,8 +10237,6 @@ }, "node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { @@ -10513,8 +10248,6 @@ }, "node_modules/supports-hyperlinks": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", - "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==", "dev": true, "license": "MIT", "dependencies": { @@ -10530,8 +10263,6 @@ }, "node_modules/supports-hyperlinks/node_modules/has-flag": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", - "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", "dev": true, "license": "MIT", "engines": { @@ -10543,8 +10274,6 @@ }, "node_modules/supports-hyperlinks/node_modules/supports-color": { "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "dev": true, "license": "MIT", "engines": { @@ -10556,8 +10285,6 @@ }, "node_modules/suspend-react": { "version": "0.1.3", - "resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.1.3.tgz", - "integrity": "sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==", "license": "MIT", "peerDependencies": { "react": ">=17.0" @@ -10565,8 +10292,6 @@ }, "node_modules/svg-pathdata": { "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", "license": "MIT", "optional": true, "engines": { @@ -10575,20 +10300,14 @@ }, "node_modules/svg-tags": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "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==", "license": "MIT" }, "node_modules/table": { "version": "6.9.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", - "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -10604,8 +10323,6 @@ }, "node_modules/table/node_modules/slice-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10622,15 +10339,11 @@ }, "node_modules/tailwindcss": { "version": "4.1.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", - "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -10643,8 +10356,6 @@ }, "node_modules/tar-fs": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", "dev": true, "license": "MIT", "dependencies": { @@ -10658,8 +10369,6 @@ }, "node_modules/tar-stream": { "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10670,8 +10379,6 @@ }, "node_modules/term-size": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha512-7dPUZQGy/+m3/wjVz3ZW5dobSoD/02NxJpoXUX0WIyjfVS3l0c+b/+9phIDFA7FHzkYtwtMFgeGZ/Y8jVTeqQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10683,8 +10390,6 @@ }, "node_modules/term-size/node_modules/cross-spawn": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", "dev": true, "license": "MIT", "dependencies": { @@ -10695,8 +10400,6 @@ }, "node_modules/term-size/node_modules/execa": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", "dev": true, "license": "MIT", "dependencies": { @@ -10714,8 +10417,6 @@ }, "node_modules/term-size/node_modules/get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", "dev": true, "license": "MIT", "engines": { @@ -10724,8 +10425,6 @@ }, "node_modules/term-size/node_modules/is-stream": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", "dev": true, "license": "MIT", "engines": { @@ -10734,8 +10433,6 @@ }, "node_modules/term-size/node_modules/lru-cache": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "license": "ISC", "dependencies": { @@ -10745,8 +10442,6 @@ }, "node_modules/term-size/node_modules/npm-run-path": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", "dev": true, "license": "MIT", "dependencies": { @@ -10758,8 +10453,6 @@ }, "node_modules/term-size/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, "license": "MIT", "engines": { @@ -10768,8 +10461,6 @@ }, "node_modules/term-size/node_modules/shebang-command": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -10781,8 +10472,6 @@ }, "node_modules/term-size/node_modules/shebang-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, "license": "MIT", "engines": { @@ -10791,8 +10480,6 @@ }, "node_modules/term-size/node_modules/which": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { @@ -10804,15 +10491,11 @@ }, "node_modules/term-size/node_modules/yallist": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", "dev": true, "license": "ISC" }, "node_modules/text-decoder": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -10821,8 +10504,6 @@ }, "node_modules/text-segmentation": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", - "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "license": "MIT", "dependencies": { "utrie": "^1.0.2" @@ -10830,15 +10511,11 @@ }, "node_modules/three": { "version": "0.181.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", - "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", "license": "MIT", "peer": true }, "node_modules/three-mesh-bvh": { "version": "0.8.3", - "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.8.3.tgz", - "integrity": "sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==", "license": "MIT", "peerDependencies": { "three": ">= 0.159.0" @@ -10846,8 +10523,6 @@ }, "node_modules/three-stdlib": { "version": "2.36.1", - "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.36.1.tgz", - "integrity": "sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==", "license": "MIT", "dependencies": { "@types/draco3d": "^1.4.0", @@ -10863,14 +10538,10 @@ }, "node_modules/three-stdlib/node_modules/fflate": { "version": "0.6.10", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", - "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==", "license": "MIT" }, "node_modules/timed-out": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", "dev": true, "license": "MIT", "engines": { @@ -10879,8 +10550,6 @@ }, "node_modules/tinyexec": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", "dev": true, "license": "MIT", "engines": { @@ -10889,8 +10558,6 @@ }, "node_modules/tinyglobby": { "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10906,8 +10573,6 @@ }, "node_modules/tldts": { "version": "7.0.22", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.22.tgz", - "integrity": "sha512-nqpKFC53CgopKPjT6Wfb6tpIcZXHcI6G37hesvikhx0EmUGPkZrujRyAjgnmp1SHNgpQfKVanZ+KfpANFt2Hxw==", "license": "MIT", "dependencies": { "tldts-core": "^7.0.22" @@ -10918,14 +10583,10 @@ }, "node_modules/tldts-core": { "version": "7.0.22", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.22.tgz", - "integrity": "sha512-KgbTDC5wzlL6j/x6np6wCnDSMUq4kucHNm00KXPbfNzmllCmtmvtykJHfmgdHntwIeupW04y8s1N/43S1PkQDw==", "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10937,8 +10598,6 @@ }, "node_modules/tough-cookie": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", - "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -10949,8 +10608,6 @@ }, "node_modules/tr46": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -10959,10 +10616,18 @@ "node": ">=20" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/troika-three-text": { "version": "0.52.4", - "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.52.4.tgz", - "integrity": "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg==", "license": "MIT", "dependencies": { "bidi-js": "^1.0.2", @@ -10976,8 +10641,6 @@ }, "node_modules/troika-three-utils": { "version": "0.52.4", - "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.52.4.tgz", - "integrity": "sha512-NORAStSVa/BDiG52Mfudk4j1FG4jC4ILutB3foPnfGbOeIs9+G5vZLa0pnmnaftZUGm4UwSoqEpWdqvC7zms3A==", "license": "MIT", "peerDependencies": { "three": ">=0.125.0" @@ -10985,14 +10648,20 @@ }, "node_modules/troika-worker-utils": { "version": "0.52.0", - "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.52.0.tgz", - "integrity": "sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==", "license": "MIT" }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -11004,8 +10673,6 @@ }, "node_modules/tsconfig-paths": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "license": "MIT", "dependencies": { @@ -11019,15 +10686,11 @@ }, "node_modules/tslib": { "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, "license": "0BSD" }, "node_modules/tsx": { "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", "optional": true, @@ -11319,8 +10982,6 @@ }, "node_modules/tsx/node_modules/@esbuild/linux-x64": { "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", "cpu": [ "x64" ], @@ -11489,8 +11150,6 @@ }, "node_modules/tsx/node_modules/esbuild": { "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -11532,8 +11191,6 @@ }, "node_modules/tunnel-rat": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", - "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", "license": "MIT", "dependencies": { "zustand": "^4.3.2" @@ -11541,8 +11198,6 @@ }, "node_modules/tunnel-rat/node_modules/zustand": { "version": "4.5.7", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", - "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", "license": "MIT", "dependencies": { "use-sync-external-store": "^1.2.2" @@ -11569,8 +11224,6 @@ }, "node_modules/type-check": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { @@ -11582,8 +11235,6 @@ }, "node_modules/type-fest": { "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, "license": "(MIT OR CC0-1.0)", "optional": true, @@ -11596,15 +11247,11 @@ }, "node_modules/typed-query-selector": { "version": "2.12.0", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", - "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", "dev": true, "license": "MIT" }, "node_modules/typescript": { "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -11618,8 +11265,6 @@ }, "node_modules/typescript-eslint": { "version": "8.58.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", - "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", "dev": true, "license": "MIT", "dependencies": { @@ -11642,14 +11287,10 @@ }, "node_modules/uhyphen": { "version": "0.2.0", - "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", - "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", "license": "ISC" }, "node_modules/undici": { "version": "7.20.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.20.0.tgz", - "integrity": "sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -11657,14 +11298,10 @@ }, "node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unicorn-magic": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.4.0.tgz", - "integrity": "sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==", "dev": true, "license": "MIT", "engines": { @@ -11674,10 +11311,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unique-string": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", "dev": true, "license": "MIT", "dependencies": { @@ -11687,10 +11341,76 @@ "node": ">=4" } }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { @@ -11699,8 +11419,6 @@ }, "node_modules/unzip-response": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==", "dev": true, "license": "MIT", "engines": { @@ -11709,8 +11427,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -11740,8 +11456,6 @@ }, "node_modules/update-notifier": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -11762,8 +11476,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -11772,8 +11484,6 @@ }, "node_modules/url-parse-lax": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", "dev": true, "license": "MIT", "dependencies": { @@ -11785,8 +11495,6 @@ }, "node_modules/use-sync-external-store": { "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -11794,14 +11502,10 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/utility-types": { "version": "3.11.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", "license": "MIT", "engines": { "node": ">= 4" @@ -11809,17 +11513,41 @@ }, "node_modules/utrie": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", - "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "license": "MIT", "dependencies": { "base64-arraybuffer": "^1.0.2" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "peer": true, @@ -11894,8 +11622,6 @@ }, "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==", "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -11906,8 +11632,6 @@ }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", "license": "MIT", "engines": { "node": ">= 14" @@ -11915,26 +11639,18 @@ }, "node_modules/webdriver-bidi-protocol": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.0.tgz", - "integrity": "sha512-U9VIlNRrq94d1xxR9JrCEAx5Gv/2W7ERSv8oWRoNe/QYbfccS0V3h/H6qeNeCRJxXGMhhnkqvwNrvPAYeuP9VA==", "dev": true, "license": "Apache-2.0" }, "node_modules/webgl-constants": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", - "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + "version": "1.1.1" }, "node_modules/webgl-sdf-generator": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", - "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==", "license": "MIT" }, "node_modules/webidl-conversions": { "version": "8.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", - "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", "license": "BSD-2-Clause", "engines": { "node": ">=20" @@ -11942,8 +11658,6 @@ }, "node_modules/whatwg-mimetype": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", - "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", "license": "MIT", "engines": { "node": ">=20" @@ -11951,8 +11665,6 @@ }, "node_modules/whatwg-url": { "version": "16.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.0.tgz", - "integrity": "sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==", "license": "MIT", "dependencies": { "@exodus/bytes": "^1.11.0", @@ -11965,8 +11677,6 @@ }, "node_modules/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -11980,8 +11690,6 @@ }, "node_modules/widest-line": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "license": "MIT", "dependencies": { @@ -11993,8 +11701,6 @@ }, "node_modules/widest-line/node_modules/ansi-regex": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", - "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", "dev": true, "license": "MIT", "engines": { @@ -12003,8 +11709,6 @@ }, "node_modules/widest-line/node_modules/is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, "license": "MIT", "engines": { @@ -12013,8 +11717,6 @@ }, "node_modules/widest-line/node_modules/string-width": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "license": "MIT", "dependencies": { @@ -12027,8 +11729,6 @@ }, "node_modules/widest-line/node_modules/strip-ansi": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "license": "MIT", "dependencies": { @@ -12040,8 +11740,6 @@ }, "node_modules/word-wrap": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { @@ -12050,8 +11748,6 @@ }, "node_modules/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": { @@ -12068,15 +11764,11 @@ }, "node_modules/wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "dev": true, "license": "ISC", "dependencies": { @@ -12087,8 +11779,6 @@ }, "node_modules/ws": { "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "devOptional": true, "license": "MIT", "peer": true, @@ -12110,8 +11800,6 @@ }, "node_modules/xdg-basedir": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", "dev": true, "license": "MIT", "engines": { @@ -12120,8 +11808,6 @@ }, "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==", "license": "Apache-2.0", "engines": { "node": ">=18" @@ -12129,14 +11815,10 @@ }, "node_modules/xmlchars": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, "node_modules/y18n": { "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { @@ -12145,15 +11827,11 @@ }, "node_modules/yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "license": "ISC" }, "node_modules/yaml": { "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "license": "ISC", "bin": { @@ -12168,8 +11846,6 @@ }, "node_modules/yargs": { "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -12187,8 +11863,6 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { @@ -12197,8 +11871,6 @@ }, "node_modules/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", "dependencies": { @@ -12208,8 +11880,6 @@ }, "node_modules/yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { @@ -12221,8 +11891,6 @@ }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "devOptional": true, "license": "MIT", "peer": true, @@ -12232,8 +11900,6 @@ }, "node_modules/zustand": { "version": "5.0.11", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", - "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", "license": "MIT", "engines": { "node": ">=12.20.0" @@ -12259,6 +11925,16 @@ } } }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "packages/react-robot-canvas": { "name": "@urdf-studio/react-robot-canvas", "version": "0.1.0", diff --git a/package.json b/package.json index 0bf7d15de..482fea5cb 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "inspection-criteria:generate": "node scripts/generate_inspection_criteria.mjs", "ai-prompts:generate": "node scripts/generate_ai_prompt_templates.mjs && node scripts/generate_inspection_criteria.mjs", - "dev": "node scripts/start_dev_server.mjs", + "dev": "vite", "build": "npm run generate:check && npm run build:app", "lint": "npm run lint:eslint && npm run lint:style", "lint:eslint": "eslint . --cache --max-warnings=0", @@ -60,6 +60,7 @@ "test": "npm run test:unit:app-hooks && npm run test:fixtures:imports", "test:fixtures:imports": "esbuild scripts/regression/validate_import_fixture_matrix.ts --bundle --platform=node --format=cjs --alias:@=./src --external:jsdom --outfile=tmp/regression/fixture-import-matrix.cjs && node tmp/regression/fixture-import-matrix.cjs", "test:fixtures:myosuite-imports": "esbuild scripts/regression/validate_myosuite_imports.ts --bundle --platform=node --format=cjs --alias:@=./src --external:jsdom --outfile=tmp/regression/myosuite-import-matrix.cjs && node tmp/regression/myosuite-import-matrix.cjs", + "test:fixtures:unitree-ros-usd-export-benchmark": "node scripts/regression/validate_unitree_ros_usd_export_benchmark.mjs", "test:fixtures:unitree-ros-urdfs": "npx tsx scripts/regression/validate_unitree_ros_urdfs.ts", "test:fixtures:unitree-usd": "node scripts/regression/validate_unitree_selected_browser.mjs", "verify:fast": "npm run format:check && npm run lint && npm run typecheck:quality && npm run test && npm run build", @@ -67,9 +68,9 @@ "verify:full": "npm run verify:fast && npm run verify:fixtures", "build:app": "vite build", "test:fixtures:unitree-ros-usda": "node scripts/regression/validate_unitree_ros_usda_selected_browser.mjs", - "test:fixtures": "npm run test:fixtures:imports && npm run test:fixtures:unitree-ros-urdfs && npm run test:fixtures:unitree-usd && npm run test:fixtures:unitree-ros-usda", + "test:fixtures": "npm run test:fixtures:imports && npm run test:fixtures:unitree-ros-urdfs && npm run test:fixtures:unitree-usd && npm run test:fixtures:unitree-ros-usda && npm run test:fixtures:unitree-ros-usd-export-benchmark", "dev:with-generate": "node scripts/start_dev_server.mjs --generate", - "test:unit:app-hooks": "node --test src/app/hooks/useEditableSourcePatches.test.ts src/app/hooks/workspaceGeneratedSourceState.test.ts src/app/utils/importPreparation.workerSafe.test.ts src/app/components/UnifiedViewer.typecheck.test.ts" + "test:unit:app-hooks": "node --test src/app/hooks/useEditableSourcePatches.test.ts src/app/hooks/workspaceGeneratedSourceState.test.ts src/app/utils/assemblyRootComponentSelection.test.ts src/app/utils/importPreparation.workerSafe.test.ts src/app/components/UnifiedViewer.typecheck.test.ts src/app/handoff/bootstrap.test.ts src/app/utils/popupHandoffImport.test.ts" }, "license": "Apache-2.0", "dependencies": { @@ -84,6 +85,7 @@ "jsdom": "^28.0.0", "jspdf": "^4.0.0", "jszip": "^3.10.1", + "libarchive.js": "^2.0.2", "linkedom": "^0.18.12", "lucide-react": "^0.555.0", "monaco-editor": "^0.55.1", @@ -92,9 +94,11 @@ "react": "^19.2.0", "react-dom": "^19.2.0", "react-draggable": "^4.5.0", + "react-markdown": "^10.1.0", "react-resizable": "^3.1.3", "react-simple-code-editor": "^0.14.1", "react-syntax-highlighter": "^16.1.0", + "remark-gfm": "^4.0.1", "three": "^0.181.2", "zustand": "^5.0.10" }, diff --git a/public/assets/libarchive.wasm b/public/assets/libarchive.wasm new file mode 100644 index 000000000..31fd233b2 Binary files /dev/null and b/public/assets/libarchive.wasm differ diff --git a/public/assets/worker-bundle.js b/public/assets/worker-bundle.js new file mode 100644 index 000000000..88e6e96bb --- /dev/null +++ b/public/assets/worker-bundle.js @@ -0,0 +1,3259 @@ +/** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ +const e = Symbol('Comlink.proxy'), + r = Symbol('Comlink.endpoint'), + t = Symbol('Comlink.releaseProxy'), + n = Symbol('Comlink.finalizer'), + o = Symbol('Comlink.thrown'), + a = (e) => ('object' == typeof e && null !== e) || 'function' == typeof e, + s = new Map([ + [ + 'proxy', + { + canHandle: (r) => a(r) && r[e], + serialize(e) { + const { port1: r, port2: t } = new MessageChannel(); + return (i(e, r), [t, [t]]); + }, + deserialize(e) { + return (e.start(), f(e, [], r)); + var r; + }, + }, + ], + [ + 'throw', + { + canHandle: (e) => a(e) && o in e, + serialize({ value: e }) { + let r; + return ( + (r = + e instanceof Error + ? { isError: !0, value: { message: e.message, name: e.name, stack: e.stack } } + : { isError: !1, value: e }), + [r, []] + ); + }, + deserialize(e) { + if (e.isError) throw Object.assign(new Error(e.value.message), e.value); + throw e.value; + }, + }, + ], + ]); +function i(r, t = globalThis, a = ['*']) { + (t.addEventListener('message', function s(l) { + if (!l || !l.data) return; + if ( + !(function (e, r) { + for (const t of e) { + if (r === t || '*' === t) return !0; + if (t instanceof RegExp && t.test(r)) return !0; + } + return !1; + })(a, l.origin) + ) + return void console.warn(`Invalid origin '${l.origin}' for comlink proxy`); + const { id: c, type: d, path: h } = Object.assign({ path: [] }, l.data), + f = (l.data.argumentList || []).map(w); + let m; + try { + const t = h.slice(0, -1).reduce((e, r) => e[r], r), + n = h.reduce((e, r) => e[r], r); + switch (d) { + case 'GET': + m = n; + break; + case 'SET': + ((t[h.slice(-1)[0]] = w(l.data.value)), (m = !0)); + break; + case 'APPLY': + m = n.apply(t, f); + break; + case 'CONSTRUCT': + m = (function (r) { + return Object.assign(r, { [e]: !0 }); + })(new n(...f)); + break; + case 'ENDPOINT': + { + const { port1: e, port2: t } = new MessageChannel(); + (i(r, t), + (m = (function (e, r) { + return (p.set(e, r), e); + })(e, [e]))); + } + break; + case 'RELEASE': + m = void 0; + break; + default: + return; + } + } catch (e) { + m = { value: e, [o]: 0 }; + } + Promise.resolve(m) + .catch((e) => ({ value: e, [o]: 0 })) + .then((e) => { + const [o, a] = v(e); + (t.postMessage(Object.assign(Object.assign({}, o), { id: c }), a), + 'RELEASE' === d && + (t.removeEventListener('message', s), + u(t), + n in r && 'function' == typeof r[n] && r[n]())); + }) + .catch((e) => { + const [r, n] = v({ value: new TypeError('Unserializable return value'), [o]: 0 }); + t.postMessage(Object.assign(Object.assign({}, r), { id: c }), n); + }); + }), + t.start && t.start()); +} +function u(e) { + (function (e) { + return 'MessagePort' === e.constructor.name; + })(e) && e.close(); +} +function l(e) { + if (e) throw new Error('Proxy has been released and is not useable'); +} +function c(e) { + return g(e, { type: 'RELEASE' }).then(() => { + u(e); + }); +} +const d = new WeakMap(), + h = + 'FinalizationRegistry' in globalThis && + new FinalizationRegistry((e) => { + const r = (d.get(e) || 0) - 1; + (d.set(e, r), 0 === r && c(e)); + }); +function f(e, n = [], o = function () {}) { + let a = !1; + const s = new Proxy(o, { + get(r, o) { + if ((l(a), o === t)) + return () => { + (!(function (e) { + h && h.unregister(e); + })(s), + c(e), + (a = !0)); + }; + if ('then' === o) { + if (0 === n.length) return { then: () => s }; + const r = g(e, { type: 'GET', path: n.map((e) => e.toString()) }).then(w); + return r.then.bind(r); + } + return f(e, [...n, o]); + }, + set(r, t, o) { + l(a); + const [s, i] = v(o); + return g(e, { type: 'SET', path: [...n, t].map((e) => e.toString()), value: s }, i).then(w); + }, + apply(t, o, s) { + l(a); + const i = n[n.length - 1]; + if (i === r) return g(e, { type: 'ENDPOINT' }).then(w); + if ('bind' === i) return f(e, n.slice(0, -1)); + const [u, c] = m(s); + return g(e, { type: 'APPLY', path: n.map((e) => e.toString()), argumentList: u }, c).then(w); + }, + construct(r, t) { + l(a); + const [o, s] = m(t); + return g(e, { type: 'CONSTRUCT', path: n.map((e) => e.toString()), argumentList: o }, s).then( + w, + ); + }, + }); + return ( + (function (e, r) { + const t = (d.get(r) || 0) + 1; + (d.set(r, t), h && h.register(e, r, e)); + })(s, e), + s + ); +} +function m(e) { + const r = e.map(v); + return [r.map((e) => e[0]), ((t = r.map((e) => e[1])), Array.prototype.concat.apply([], t))]; + var t; +} +const p = new WeakMap(); +function v(e) { + for (const [r, t] of s) + if (t.canHandle(e)) { + const [n, o] = t.serialize(e); + return [{ type: 'HANDLER', name: r, value: n }, o]; + } + return [{ type: 'RAW', value: e }, p.get(e) || []]; +} +function w(e) { + switch (e.type) { + case 'HANDLER': + return s.get(e.name).deserialize(e.value); + case 'RAW': + return e.value; + } +} +function g(e, r, t) { + return new Promise((n) => { + const o = new Array(4) + .fill(0) + .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)) + .join('-'); + (e.addEventListener('message', function r(t) { + t.data && t.data.id && t.data.id === o && (e.removeEventListener('message', r), n(t.data)); + }), + e.start && e.start(), + e.postMessage(Object.assign({ id: o }, r), t)); + }); +} +const _ = { + 32768: 'FILE', + 16384: 'DIR', + 40960: 'SYMBOLIC_LINK', + 49152: 'SOCKET', + 8192: 'CHARACTER_DEVICE', + 24576: 'BLOCK_DEVICE', + 4096: 'NAMED_PIPE', +}; +class y { + constructor(e) { + ((this._wasmModule = e), + (this._runCode = e.runCode), + (this._file = null), + (this._passphrase = null), + (this._locale = 'en_US.UTF-8')); + } + async open(e) { + null !== this._file && (console.warn('Closing previous file'), this.close()); + const r = await this._loadFile(e); + ((this._fileLength = r.length), (this._filePtr = r.ptr)); + } + close() { + (this._runCode.closeArchive(this._archive), + this._wasmModule._free(this._filePtr), + (this._file = null), + (this._filePtr = null), + (this._archive = null)); + } + hasEncryptedData() { + ((this._archive = this._runCode.openArchive( + this._filePtr, + this._fileLength, + this._passphrase, + this._locale, + )), + this._runCode.getNextEntry(this._archive)); + const e = this._runCode.hasEncryptedEntries(this._archive); + return 0 !== e && (e > 0 || null); + } + setPassphrase(e) { + this._passphrase = e; + } + setLocale(e) { + this._locale = e; + } + *entries(e = !1, r = null) { + let t; + for ( + this._archive = this._runCode.openArchive( + this._filePtr, + this._fileLength, + this._passphrase, + this._locale, + ); + (t = this._runCode.getNextEntry(this._archive)), 0 !== t; + ) { + const n = { + size: this._runCode.getEntrySize(t), + path: this._runCode.getEntryName(t), + type: _[this._runCode.getEntryType(t)], + lastModified: this._runCode.getEntryLastModified(t), + ref: t, + }; + if ('FILE' === n.type) { + let e = n.path.split('/'); + n.fileName = e[e.length - 1]; + } + if (e && r !== n.path) this._runCode.skipEntry(this._archive); + else { + const e = this._runCode.getFileData(this._archive, n.size); + if (e < 0) throw new Error(this._runCode.getError(this._archive)); + ((n.fileData = this._wasmModule.HEAPU8.slice(e, e + n.size)), this._wasmModule._free(e)); + } + yield n; + } + } + async _loadFile(e) { + const r = await e.arrayBuffer(), + t = new Uint8Array(r), + n = this._runCode.malloc(t.length); + return (this._wasmModule.HEAPU8.set(t, n), { ptr: n, length: t.length }); + } +} +class E { + constructor(e) { + ((this._wasmModule = e), + (this._runCode = e.runCode), + (this._passphrase = null), + (this._locale = 'en_US.UTF-8')); + } + async write(e, r, t, n = null) { + let o = e.reduce((e, { file: r }) => e + r.size + 128, 0) + 128; + const a = this._runCode.malloc(o), + s = this._runCode.malloc(this._runCode.sizeOfSizeT()), + i = this._runCode.startArchiveWrite(r, t, a, o, s, n); + for (const { file: r, pathname: t } of e) { + const e = await this._loadFile(r); + (this._runCode.writeArchiveFile(i, t || r.name, e.length, e.ptr), this._runCode.free(e.ptr)); + } + const u = this._runCode.finishArchiveWrite(i, s); + if (u < 0) throw new Error(this._runCode.getError(i)); + return this._wasmModule.HEAPU8.slice(a, a + u); + } + async _loadFile(e) { + const r = await e.arrayBuffer(), + t = new Uint8Array(r), + n = this._runCode.malloc(t.length); + return (this._wasmModule.HEAPU8.set(t, n), { ptr: n, length: t.length }); + } +} +var b, + k = + ((b = import.meta.url), + async function (e = {}) { + var r, + t, + n = e; + n.ready = new Promise((e, n) => { + ((r = e), (t = n)); + }); + var o, + a, + s, + i = Object.assign({}, n), + u = './this.program', + l = (e, r) => { + throw r; + }, + c = 'object' == typeof window, + d = 'function' == typeof importScripts, + h = + 'object' == typeof process && + 'object' == typeof process.versions && + 'string' == typeof process.versions.node, + f = ''; + if (h) { + const { createRequire: e } = await import('module'); + var m = e(import.meta.url), + p = m('fs'), + v = m('path'); + ((f = d ? v.dirname(f) + '/' : m('url').fileURLToPath(new URL('./', import.meta.url))), + (o = (e, r) => ( + (e = Y(e) ? new URL(e) : v.normalize(e)), + p.readFileSync(e, r ? void 0 : 'utf8') + )), + (s = (e) => { + var r = o(e, !0); + return (r.buffer || (r = new Uint8Array(r)), r); + }), + (a = (e, r, t, n = !0) => { + ((e = Y(e) ? new URL(e) : v.normalize(e)), + p.readFile(e, n ? void 0 : 'utf8', (e, o) => { + e ? t(e) : r(n ? o.buffer : o); + })); + }), + !n.thisProgram && process.argv.length > 1 && (u = process.argv[1].replace(/\\/g, '/')), + process.argv.slice(2), + (l = (e, r) => { + throw ((process.exitCode = e), r); + }), + (n.inspect = () => '[Emscripten Module object]')); + } else + (c || d) && + (d + ? (f = self.location.href) + : 'undefined' != typeof document && + document.currentScript && + (f = document.currentScript.src), + b && (f = b), + (f = + 0 !== f.indexOf('blob:') + ? f.substr(0, f.replace(/[?#].*/, '').lastIndexOf('/') + 1) + : ''), + (o = (e) => { + var r = new XMLHttpRequest(); + return (r.open('GET', e, !1), r.send(null), r.responseText); + }), + d && + (s = (e) => { + var r = new XMLHttpRequest(); + return ( + r.open('GET', e, !1), + (r.responseType = 'arraybuffer'), + r.send(null), + new Uint8Array(r.response) + ); + }), + (a = (e, r, t) => { + var n = new XMLHttpRequest(); + (n.open('GET', e, !0), + (n.responseType = 'arraybuffer'), + (n.onload = () => { + 200 == n.status || (0 == n.status && n.response) ? r(n.response) : t(); + }), + (n.onerror = t), + n.send(null)); + })); + var w, + g, + _ = n.print || console.log.bind(console), + y = n.printErr || console.error.bind(console); + (Object.assign(n, i), + (i = null), + n.arguments && n.arguments, + n.thisProgram && (u = n.thisProgram), + n.quit && (l = n.quit), + n.wasmBinary && (w = n.wasmBinary), + 'object' != typeof WebAssembly && O('no native wasm support detected')); + var E, + k, + S, + F, + D, + M = !1; + function A(e, r) { + e || O(r); + } + function C() { + var e = g.buffer; + ((n.HEAP8 = E = new Int8Array(e)), + (n.HEAP16 = S = new Int16Array(e)), + (n.HEAPU8 = k = new Uint8Array(e)), + (n.HEAPU16 = new Uint16Array(e)), + (n.HEAP32 = F = new Int32Array(e)), + (n.HEAPU32 = D = new Uint32Array(e)), + (n.HEAPF32 = new Float32Array(e)), + (n.HEAPF64 = new Float64Array(e))); + } + var P = [], + T = [], + R = [], + z = 0, + U = null; + function L(e) { + (z++, n.monitorRunDependencies?.(z)); + } + function x(e) { + if ((z--, n.monitorRunDependencies?.(z), 0 == z && U)) { + var r = U; + ((U = null), r()); + } + } + function O(e) { + (n.onAbort?.(e), + y((e = 'Aborted(' + e + ')')), + (M = !0), + (e += '. Build with -sASSERTIONS for more info.')); + var r = new WebAssembly.RuntimeError(e); + throw (t(r), r); + } + var N, + B, + j, + I, + H = (e) => e.startsWith('data:application/octet-stream;base64,'), + Y = (e) => e.startsWith('file://'); + function W(e) { + if (e == N && w) return new Uint8Array(w); + if (s) return s(e); + throw 'both async and sync fetching of the wasm failed'; + } + function K(e, r, t) { + return (function (e) { + if (!w && (c || d)) { + if ('function' == typeof fetch && !Y(e)) + return fetch(e, { credentials: 'same-origin' }) + .then((r) => { + if (!r.ok) throw "failed to load wasm binary file at '" + e + "'"; + return r.arrayBuffer(); + }) + .catch(() => W(e)); + if (a) + return new Promise((r, t) => { + a(e, (e) => r(new Uint8Array(e)), t); + }); + } + return Promise.resolve().then(() => W(e)); + })(e) + .then((e) => WebAssembly.instantiate(e, r)) + .then((e) => e) + .then(t, (e) => { + (y(`failed to asynchronously prepare wasm: ${e}`), O(e)); + }); + } + function q(e) { + ((this.name = 'ExitStatus'), + (this.message = `Program terminated with exit(${e})`), + (this.status = e)); + } + n.locateFile + ? H((N = 'libarchive.wasm')) || ((B = N), (N = n.locateFile ? n.locateFile(B, f) : f + B)) + : (N = new URL('libarchive.wasm', import.meta.url).href); + var $ = (e) => { + for (; e.length > 0; ) e.shift()(n); + }, + X = n.noExitRuntime || !0, + Z = { + isAbs: (e) => '/' === e.charAt(0), + splitPath: (e) => + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(e).slice(1), + normalizeArray: (e, r) => { + for (var t = 0, n = e.length - 1; n >= 0; n--) { + var o = e[n]; + '.' === o + ? e.splice(n, 1) + : '..' === o + ? (e.splice(n, 1), t++) + : t && (e.splice(n, 1), t--); + } + if (r) for (; t; t--) e.unshift('..'); + return e; + }, + normalize: (e) => { + var r = Z.isAbs(e), + t = '/' === e.substr(-1); + return ( + (e = Z.normalizeArray( + e.split('/').filter((e) => !!e), + !r, + ).join('/')) || + r || + (e = '.'), + e && t && (e += '/'), + (r ? '/' : '') + e + ); + }, + dirname: (e) => { + var r = Z.splitPath(e), + t = r[0], + n = r[1]; + return t || n ? (n && (n = n.substr(0, n.length - 1)), t + n) : '.'; + }, + basename: (e) => { + if ('/' === e) return '/'; + var r = (e = (e = Z.normalize(e)).replace(/\/$/, '')).lastIndexOf('/'); + return -1 === r ? e : e.substr(r + 1); + }, + join: function () { + var e = Array.prototype.slice.call(arguments); + return Z.normalize(e.join('/')); + }, + join2: (e, r) => Z.normalize(e + '/' + r), + }, + G = (e) => + (G = (() => { + if ('object' == typeof crypto && 'function' == typeof crypto.getRandomValues) + return (e) => crypto.getRandomValues(e); + if (h) + try { + var e = m('crypto'); + if (e.randomFillSync) return (r) => e.randomFillSync(r); + var r = e.randomBytes; + return (e) => (e.set(r(e.byteLength)), e); + } catch (e) {} + O('initRandomDevice'); + })())(e), + V = { + resolve: function () { + for (var e = '', r = !1, t = arguments.length - 1; t >= -1 && !r; t--) { + var n = t >= 0 ? arguments[t] : le.cwd(); + if ('string' != typeof n) + throw new TypeError('Arguments to path.resolve must be strings'); + if (!n) return ''; + ((e = n + '/' + e), (r = Z.isAbs(n))); + } + return ( + (r ? '/' : '') + + (e = Z.normalizeArray( + e.split('/').filter((e) => !!e), + !r, + ).join('/')) || '.' + ); + }, + relative: (e, r) => { + function t(e) { + for (var r = 0; r < e.length && '' === e[r]; r++); + for (var t = e.length - 1; t >= 0 && '' === e[t]; t--); + return r > t ? [] : e.slice(r, t - r + 1); + } + ((e = V.resolve(e).substr(1)), (r = V.resolve(r).substr(1))); + for ( + var n = t(e.split('/')), + o = t(r.split('/')), + a = Math.min(n.length, o.length), + s = a, + i = 0; + i < a; + i++ + ) + if (n[i] !== o[i]) { + s = i; + break; + } + var u = []; + for (i = s; i < n.length; i++) u.push('..'); + return (u = u.concat(o.slice(s))).join('/'); + }, + }, + J = 'undefined' != typeof TextDecoder ? new TextDecoder('utf8') : void 0, + Q = (e, r, t) => { + for (var n = r + t, o = r; e[o] && !(o >= n); ) ++o; + if (o - r > 16 && e.buffer && J) return J.decode(e.subarray(r, o)); + for (var a = ''; r < o; ) { + var s = e[r++]; + if (128 & s) { + var i = 63 & e[r++]; + if (192 != (224 & s)) { + var u = 63 & e[r++]; + if ( + (s = + 224 == (240 & s) + ? ((15 & s) << 12) | (i << 6) | u + : ((7 & s) << 18) | (i << 12) | (u << 6) | (63 & e[r++])) < 65536 + ) + a += String.fromCharCode(s); + else { + var l = s - 65536; + a += String.fromCharCode(55296 | (l >> 10), 56320 | (1023 & l)); + } + } else a += String.fromCharCode(((31 & s) << 6) | i); + } else a += String.fromCharCode(s); + } + return a; + }, + ee = [], + re = (e) => { + for (var r = 0, t = 0; t < e.length; ++t) { + var n = e.charCodeAt(t); + n <= 127 + ? r++ + : n <= 2047 + ? (r += 2) + : n >= 55296 && n <= 57343 + ? ((r += 4), ++t) + : (r += 3); + } + return r; + }, + te = (e, r, t, n) => { + if (!(n > 0)) return 0; + for (var o = t, a = t + n - 1, s = 0; s < e.length; ++s) { + var i = e.charCodeAt(s); + if ( + (i >= 55296 && + i <= 57343 && + (i = (65536 + ((1023 & i) << 10)) | (1023 & e.charCodeAt(++s))), + i <= 127) + ) { + if (t >= a) break; + r[t++] = i; + } else if (i <= 2047) { + if (t + 1 >= a) break; + ((r[t++] = 192 | (i >> 6)), (r[t++] = 128 | (63 & i))); + } else if (i <= 65535) { + if (t + 2 >= a) break; + ((r[t++] = 224 | (i >> 12)), + (r[t++] = 128 | ((i >> 6) & 63)), + (r[t++] = 128 | (63 & i))); + } else { + if (t + 3 >= a) break; + ((r[t++] = 240 | (i >> 18)), + (r[t++] = 128 | ((i >> 12) & 63)), + (r[t++] = 128 | ((i >> 6) & 63)), + (r[t++] = 128 | (63 & i))); + } + } + return ((r[t] = 0), t - o); + }; + function ne(e, r, t) { + var n = t > 0 ? t : re(e) + 1, + o = new Array(n), + a = te(e, o, 0, o.length); + return (r && (o.length = a), o); + } + var oe = { + ttys: [], + init() {}, + shutdown() {}, + register(e, r) { + ((oe.ttys[e] = { input: [], output: [], ops: r }), le.registerDevice(e, oe.stream_ops)); + }, + stream_ops: { + open(e) { + var r = oe.ttys[e.node.rdev]; + if (!r) throw new le.ErrnoError(43); + ((e.tty = r), (e.seekable = !1)); + }, + close(e) { + e.tty.ops.fsync(e.tty); + }, + fsync(e) { + e.tty.ops.fsync(e.tty); + }, + read(e, r, t, n, o) { + if (!e.tty || !e.tty.ops.get_char) throw new le.ErrnoError(60); + for (var a = 0, s = 0; s < n; s++) { + var i; + try { + i = e.tty.ops.get_char(e.tty); + } catch (e) { + throw new le.ErrnoError(29); + } + if (void 0 === i && 0 === a) throw new le.ErrnoError(6); + if (null == i) break; + (a++, (r[t + s] = i)); + } + return (a && (e.node.timestamp = Date.now()), a); + }, + write(e, r, t, n, o) { + if (!e.tty || !e.tty.ops.put_char) throw new le.ErrnoError(60); + try { + for (var a = 0; a < n; a++) e.tty.ops.put_char(e.tty, r[t + a]); + } catch (e) { + throw new le.ErrnoError(29); + } + return (n && (e.node.timestamp = Date.now()), a); + }, + }, + default_tty_ops: { + get_char: (e) => + (() => { + if (!ee.length) { + var e = null; + if (h) { + var r = Buffer.alloc(256), + t = 0, + n = process.stdin.fd; + try { + t = p.readSync(n, r); + } catch (e) { + if (!e.toString().includes('EOF')) throw e; + t = 0; + } + e = t > 0 ? r.slice(0, t).toString('utf-8') : null; + } else + 'undefined' != typeof window && 'function' == typeof window.prompt + ? null !== (e = window.prompt('Input: ')) && (e += '\n') + : 'function' == typeof readline && null !== (e = readline()) && (e += '\n'); + if (!e) return null; + ee = ne(e, !0); + } + return ee.shift(); + })(), + put_char(e, r) { + null === r || 10 === r + ? (_(Q(e.output, 0)), (e.output = [])) + : 0 != r && e.output.push(r); + }, + fsync(e) { + e.output && e.output.length > 0 && (_(Q(e.output, 0)), (e.output = [])); + }, + ioctl_tcgets: (e) => ({ + c_iflag: 25856, + c_oflag: 5, + c_cflag: 191, + c_lflag: 35387, + c_cc: [ + 3, 28, 127, 21, 4, 0, 1, 0, 17, 19, 26, 0, 18, 15, 23, 22, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + ], + }), + ioctl_tcsets: (e, r, t) => 0, + ioctl_tiocgwinsz: (e) => [24, 80], + }, + default_tty1_ops: { + put_char(e, r) { + null === r || 10 === r + ? (y(Q(e.output, 0)), (e.output = [])) + : 0 != r && e.output.push(r); + }, + fsync(e) { + e.output && e.output.length > 0 && (y(Q(e.output, 0)), (e.output = [])); + }, + }, + }, + ae = (e) => { + O(); + }, + se = { + ops_table: null, + mount: (e) => se.createNode(null, '/', 16895, 0), + createNode(e, r, t, n) { + if (le.isBlkdev(t) || le.isFIFO(t)) throw new le.ErrnoError(63); + se.ops_table ||= { + dir: { + node: { + getattr: se.node_ops.getattr, + setattr: se.node_ops.setattr, + lookup: se.node_ops.lookup, + mknod: se.node_ops.mknod, + rename: se.node_ops.rename, + unlink: se.node_ops.unlink, + rmdir: se.node_ops.rmdir, + readdir: se.node_ops.readdir, + symlink: se.node_ops.symlink, + }, + stream: { llseek: se.stream_ops.llseek }, + }, + file: { + node: { getattr: se.node_ops.getattr, setattr: se.node_ops.setattr }, + stream: { + llseek: se.stream_ops.llseek, + read: se.stream_ops.read, + write: se.stream_ops.write, + allocate: se.stream_ops.allocate, + mmap: se.stream_ops.mmap, + msync: se.stream_ops.msync, + }, + }, + link: { + node: { + getattr: se.node_ops.getattr, + setattr: se.node_ops.setattr, + readlink: se.node_ops.readlink, + }, + stream: {}, + }, + chrdev: { + node: { getattr: se.node_ops.getattr, setattr: se.node_ops.setattr }, + stream: le.chrdev_stream_ops, + }, + }; + var o = le.createNode(e, r, t, n); + return ( + le.isDir(o.mode) + ? ((o.node_ops = se.ops_table.dir.node), + (o.stream_ops = se.ops_table.dir.stream), + (o.contents = {})) + : le.isFile(o.mode) + ? ((o.node_ops = se.ops_table.file.node), + (o.stream_ops = se.ops_table.file.stream), + (o.usedBytes = 0), + (o.contents = null)) + : le.isLink(o.mode) + ? ((o.node_ops = se.ops_table.link.node), + (o.stream_ops = se.ops_table.link.stream)) + : le.isChrdev(o.mode) && + ((o.node_ops = se.ops_table.chrdev.node), + (o.stream_ops = se.ops_table.chrdev.stream)), + (o.timestamp = Date.now()), + e && ((e.contents[r] = o), (e.timestamp = o.timestamp)), + o + ); + }, + getFileDataAsTypedArray: (e) => + e.contents + ? e.contents.subarray + ? e.contents.subarray(0, e.usedBytes) + : new Uint8Array(e.contents) + : new Uint8Array(0), + expandFileStorage(e, r) { + var t = e.contents ? e.contents.length : 0; + if (!(t >= r)) { + ((r = Math.max(r, (t * (t < 1048576 ? 2 : 1.125)) >>> 0)), + 0 != t && (r = Math.max(r, 256))); + var n = e.contents; + ((e.contents = new Uint8Array(r)), + e.usedBytes > 0 && e.contents.set(n.subarray(0, e.usedBytes), 0)); + } + }, + resizeFileStorage(e, r) { + if (e.usedBytes != r) + if (0 == r) ((e.contents = null), (e.usedBytes = 0)); + else { + var t = e.contents; + ((e.contents = new Uint8Array(r)), + t && e.contents.set(t.subarray(0, Math.min(r, e.usedBytes))), + (e.usedBytes = r)); + } + }, + node_ops: { + getattr(e) { + var r = {}; + return ( + (r.dev = le.isChrdev(e.mode) ? e.id : 1), + (r.ino = e.id), + (r.mode = e.mode), + (r.nlink = 1), + (r.uid = 0), + (r.gid = 0), + (r.rdev = e.rdev), + le.isDir(e.mode) + ? (r.size = 4096) + : le.isFile(e.mode) + ? (r.size = e.usedBytes) + : le.isLink(e.mode) + ? (r.size = e.link.length) + : (r.size = 0), + (r.atime = new Date(e.timestamp)), + (r.mtime = new Date(e.timestamp)), + (r.ctime = new Date(e.timestamp)), + (r.blksize = 4096), + (r.blocks = Math.ceil(r.size / r.blksize)), + r + ); + }, + setattr(e, r) { + (void 0 !== r.mode && (e.mode = r.mode), + void 0 !== r.timestamp && (e.timestamp = r.timestamp), + void 0 !== r.size && se.resizeFileStorage(e, r.size)); + }, + lookup(e, r) { + throw le.genericErrors[44]; + }, + mknod: (e, r, t, n) => se.createNode(e, r, t, n), + rename(e, r, t) { + if (le.isDir(e.mode)) { + var n; + try { + n = le.lookupNode(r, t); + } catch (e) {} + if (n) for (var o in n.contents) throw new le.ErrnoError(55); + } + (delete e.parent.contents[e.name], + (e.parent.timestamp = Date.now()), + (e.name = t), + (r.contents[t] = e), + (r.timestamp = e.parent.timestamp), + (e.parent = r)); + }, + unlink(e, r) { + (delete e.contents[r], (e.timestamp = Date.now())); + }, + rmdir(e, r) { + var t = le.lookupNode(e, r); + for (var n in t.contents) throw new le.ErrnoError(55); + (delete e.contents[r], (e.timestamp = Date.now())); + }, + readdir(e) { + var r = ['.', '..']; + for (var t of Object.keys(e.contents)) r.push(t); + return r; + }, + symlink(e, r, t) { + var n = se.createNode(e, r, 41471, 0); + return ((n.link = t), n); + }, + readlink(e) { + if (!le.isLink(e.mode)) throw new le.ErrnoError(28); + return e.link; + }, + }, + stream_ops: { + read(e, r, t, n, o) { + var a = e.node.contents; + if (o >= e.node.usedBytes) return 0; + var s = Math.min(e.node.usedBytes - o, n); + if (s > 8 && a.subarray) r.set(a.subarray(o, o + s), t); + else for (var i = 0; i < s; i++) r[t + i] = a[o + i]; + return s; + }, + write(e, r, t, n, o, a) { + if ((r.buffer === E.buffer && (a = !1), !n)) return 0; + var s = e.node; + if ( + ((s.timestamp = Date.now()), r.subarray && (!s.contents || s.contents.subarray)) + ) { + if (a) return ((s.contents = r.subarray(t, t + n)), (s.usedBytes = n), n); + if (0 === s.usedBytes && 0 === o) + return ((s.contents = r.slice(t, t + n)), (s.usedBytes = n), n); + if (o + n <= s.usedBytes) return (s.contents.set(r.subarray(t, t + n), o), n); + } + if ((se.expandFileStorage(s, o + n), s.contents.subarray && r.subarray)) + s.contents.set(r.subarray(t, t + n), o); + else for (var i = 0; i < n; i++) s.contents[o + i] = r[t + i]; + return ((s.usedBytes = Math.max(s.usedBytes, o + n)), n); + }, + llseek(e, r, t) { + var n = r; + if ( + (1 === t + ? (n += e.position) + : 2 === t && le.isFile(e.node.mode) && (n += e.node.usedBytes), + n < 0) + ) + throw new le.ErrnoError(28); + return n; + }, + allocate(e, r, t) { + (se.expandFileStorage(e.node, r + t), + (e.node.usedBytes = Math.max(e.node.usedBytes, r + t))); + }, + mmap(e, r, t, n, o) { + if (!le.isFile(e.node.mode)) throw new le.ErrnoError(43); + var a, + s, + i = e.node.contents; + if (2 & o || i.buffer !== E.buffer) { + if ( + ((t > 0 || t + r < i.length) && + (i = i.subarray + ? i.subarray(t, t + r) + : Array.prototype.slice.call(i, t, t + r)), + (s = !0), + !(a = ae())) + ) + throw new le.ErrnoError(48); + E.set(i, a); + } else ((s = !1), (a = i.byteOffset)); + return { ptr: a, allocated: s }; + }, + msync: (e, r, t, n, o) => (se.stream_ops.write(e, r, 0, n, t, !1), 0), + }, + }, + ie = n.preloadPlugins || [], + ue = (e, r) => { + var t = 0; + return (e && (t |= 365), r && (t |= 146), t); + }, + le = { + root: null, + mounts: [], + devices: {}, + streams: [], + nextInode: 1, + nameTable: null, + currentPath: '/', + initialized: !1, + ignorePermissions: !0, + ErrnoError: null, + genericErrors: {}, + filesystems: null, + syncFSRequests: 0, + lookupPath(e, r = {}) { + if (!(e = V.resolve(e))) return { path: '', node: null }; + if ((r = Object.assign({ follow_mount: !0, recurse_count: 0 }, r)).recurse_count > 8) + throw new le.ErrnoError(32); + for ( + var t = e.split('/').filter((e) => !!e), n = le.root, o = '/', a = 0; + a < t.length; + a++ + ) { + var s = a === t.length - 1; + if (s && r.parent) break; + if ( + ((n = le.lookupNode(n, t[a])), + (o = Z.join2(o, t[a])), + le.isMountpoint(n) && (!s || (s && r.follow_mount)) && (n = n.mounted.root), + !s || r.follow) + ) + for (var i = 0; le.isLink(n.mode); ) { + var u = le.readlink(o); + if ( + ((o = V.resolve(Z.dirname(o), u)), + (n = le.lookupPath(o, { recurse_count: r.recurse_count + 1 }).node), + i++ > 40) + ) + throw new le.ErrnoError(32); + } + } + return { path: o, node: n }; + }, + getPath(e) { + for (var r; ; ) { + if (le.isRoot(e)) { + var t = e.mount.mountpoint; + return r ? ('/' !== t[t.length - 1] ? `${t}/${r}` : t + r) : t; + } + ((r = r ? `${e.name}/${r}` : e.name), (e = e.parent)); + } + }, + hashName(e, r) { + for (var t = 0, n = 0; n < r.length; n++) t = ((t << 5) - t + r.charCodeAt(n)) | 0; + return ((e + t) >>> 0) % le.nameTable.length; + }, + hashAddNode(e) { + var r = le.hashName(e.parent.id, e.name); + ((e.name_next = le.nameTable[r]), (le.nameTable[r] = e)); + }, + hashRemoveNode(e) { + var r = le.hashName(e.parent.id, e.name); + if (le.nameTable[r] === e) le.nameTable[r] = e.name_next; + else + for (var t = le.nameTable[r]; t; ) { + if (t.name_next === e) { + t.name_next = e.name_next; + break; + } + t = t.name_next; + } + }, + lookupNode(e, r) { + var t = le.mayLookup(e); + if (t) throw new le.ErrnoError(t, e); + for (var n = le.hashName(e.id, r), o = le.nameTable[n]; o; o = o.name_next) { + var a = o.name; + if (o.parent.id === e.id && a === r) return o; + } + return le.lookup(e, r); + }, + createNode(e, r, t, n) { + var o = new le.FSNode(e, r, t, n); + return (le.hashAddNode(o), o); + }, + destroyNode(e) { + le.hashRemoveNode(e); + }, + isRoot: (e) => e === e.parent, + isMountpoint: (e) => !!e.mounted, + isFile: (e) => 32768 == (61440 & e), + isDir: (e) => 16384 == (61440 & e), + isLink: (e) => 40960 == (61440 & e), + isChrdev: (e) => 8192 == (61440 & e), + isBlkdev: (e) => 24576 == (61440 & e), + isFIFO: (e) => 4096 == (61440 & e), + isSocket: (e) => 49152 == (49152 & e), + flagsToPermissionString(e) { + var r = ['r', 'w', 'rw'][3 & e]; + return (512 & e && (r += 'w'), r); + }, + nodePermissions: (e, r) => + le.ignorePermissions || + ((!r.includes('r') || 292 & e.mode) && + (!r.includes('w') || 146 & e.mode) && + (!r.includes('x') || 73 & e.mode)) + ? 0 + : 2, + mayLookup(e) { + var r = le.nodePermissions(e, 'x'); + return r || (e.node_ops.lookup ? 0 : 2); + }, + mayCreate(e, r) { + try { + return (le.lookupNode(e, r), 20); + } catch (e) {} + return le.nodePermissions(e, 'wx'); + }, + mayDelete(e, r, t) { + var n; + try { + n = le.lookupNode(e, r); + } catch (e) { + return e.errno; + } + var o = le.nodePermissions(e, 'wx'); + if (o) return o; + if (t) { + if (!le.isDir(n.mode)) return 54; + if (le.isRoot(n) || le.getPath(n) === le.cwd()) return 10; + } else if (le.isDir(n.mode)) return 31; + return 0; + }, + mayOpen: (e, r) => + e + ? le.isLink(e.mode) + ? 32 + : le.isDir(e.mode) && ('r' !== le.flagsToPermissionString(r) || 512 & r) + ? 31 + : le.nodePermissions(e, le.flagsToPermissionString(r)) + : 44, + MAX_OPEN_FDS: 4096, + nextfd() { + for (var e = 0; e <= le.MAX_OPEN_FDS; e++) if (!le.streams[e]) return e; + throw new le.ErrnoError(33); + }, + getStreamChecked(e) { + var r = le.getStream(e); + if (!r) throw new le.ErrnoError(8); + return r; + }, + getStream: (e) => le.streams[e], + createStream: (e, r = -1) => ( + le.FSStream || + ((le.FSStream = function () { + this.shared = {}; + }), + (le.FSStream.prototype = {}), + Object.defineProperties(le.FSStream.prototype, { + object: { + get() { + return this.node; + }, + set(e) { + this.node = e; + }, + }, + isRead: { + get() { + return 1 != (2097155 & this.flags); + }, + }, + isWrite: { + get() { + return 0 != (2097155 & this.flags); + }, + }, + isAppend: { + get() { + return 1024 & this.flags; + }, + }, + flags: { + get() { + return this.shared.flags; + }, + set(e) { + this.shared.flags = e; + }, + }, + position: { + get() { + return this.shared.position; + }, + set(e) { + this.shared.position = e; + }, + }, + })), + (e = Object.assign(new le.FSStream(), e)), + -1 == r && (r = le.nextfd()), + (e.fd = r), + (le.streams[r] = e), + e + ), + closeStream(e) { + le.streams[e] = null; + }, + chrdev_stream_ops: { + open(e) { + var r = le.getDevice(e.node.rdev); + ((e.stream_ops = r.stream_ops), e.stream_ops.open?.(e)); + }, + llseek() { + throw new le.ErrnoError(70); + }, + }, + major: (e) => e >> 8, + minor: (e) => 255 & e, + makedev: (e, r) => (e << 8) | r, + registerDevice(e, r) { + le.devices[e] = { stream_ops: r }; + }, + getDevice: (e) => le.devices[e], + getMounts(e) { + for (var r = [], t = [e]; t.length; ) { + var n = t.pop(); + (r.push(n), t.push.apply(t, n.mounts)); + } + return r; + }, + syncfs(e, r) { + ('function' == typeof e && ((r = e), (e = !1)), + le.syncFSRequests++, + le.syncFSRequests > 1 && + y( + `warning: ${le.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`, + )); + var t = le.getMounts(le.root.mount), + n = 0; + function o(e) { + return (le.syncFSRequests--, r(e)); + } + function a(e) { + if (e) return a.errored ? void 0 : ((a.errored = !0), o(e)); + ++n >= t.length && o(null); + } + t.forEach((r) => { + if (!r.type.syncfs) return a(null); + r.type.syncfs(r, e, a); + }); + }, + mount(e, r, t) { + var n, + o = '/' === t, + a = !t; + if (o && le.root) throw new le.ErrnoError(10); + if (!o && !a) { + var s = le.lookupPath(t, { follow_mount: !1 }); + if (((t = s.path), (n = s.node), le.isMountpoint(n))) throw new le.ErrnoError(10); + if (!le.isDir(n.mode)) throw new le.ErrnoError(54); + } + var i = { type: e, opts: r, mountpoint: t, mounts: [] }, + u = e.mount(i); + return ( + (u.mount = i), + (i.root = u), + o ? (le.root = u) : n && ((n.mounted = i), n.mount && n.mount.mounts.push(i)), + u + ); + }, + unmount(e) { + var r = le.lookupPath(e, { follow_mount: !1 }); + if (!le.isMountpoint(r.node)) throw new le.ErrnoError(28); + var t = r.node, + n = t.mounted, + o = le.getMounts(n); + (Object.keys(le.nameTable).forEach((e) => { + for (var r = le.nameTable[e]; r; ) { + var t = r.name_next; + (o.includes(r.mount) && le.destroyNode(r), (r = t)); + } + }), + (t.mounted = null)); + var a = t.mount.mounts.indexOf(n); + t.mount.mounts.splice(a, 1); + }, + lookup: (e, r) => e.node_ops.lookup(e, r), + mknod(e, r, t) { + var n = le.lookupPath(e, { parent: !0 }).node, + o = Z.basename(e); + if (!o || '.' === o || '..' === o) throw new le.ErrnoError(28); + var a = le.mayCreate(n, o); + if (a) throw new le.ErrnoError(a); + if (!n.node_ops.mknod) throw new le.ErrnoError(63); + return n.node_ops.mknod(n, o, r, t); + }, + create: (e, r) => ( + (r = void 0 !== r ? r : 438), + (r &= 4095), + (r |= 32768), + le.mknod(e, r, 0) + ), + mkdir: (e, r) => ( + (r = void 0 !== r ? r : 511), + (r &= 1023), + (r |= 16384), + le.mknod(e, r, 0) + ), + mkdirTree(e, r) { + for (var t = e.split('/'), n = '', o = 0; o < t.length; ++o) + if (t[o]) { + n += '/' + t[o]; + try { + le.mkdir(n, r); + } catch (e) { + if (20 != e.errno) throw e; + } + } + }, + mkdev: (e, r, t) => ( + void 0 === t && ((t = r), (r = 438)), + (r |= 8192), + le.mknod(e, r, t) + ), + symlink(e, r) { + if (!V.resolve(e)) throw new le.ErrnoError(44); + var t = le.lookupPath(r, { parent: !0 }).node; + if (!t) throw new le.ErrnoError(44); + var n = Z.basename(r), + o = le.mayCreate(t, n); + if (o) throw new le.ErrnoError(o); + if (!t.node_ops.symlink) throw new le.ErrnoError(63); + return t.node_ops.symlink(t, n, e); + }, + rename(e, r) { + var t, + n, + o = Z.dirname(e), + a = Z.dirname(r), + s = Z.basename(e), + i = Z.basename(r); + if ( + ((t = le.lookupPath(e, { parent: !0 }).node), + (n = le.lookupPath(r, { parent: !0 }).node), + !t || !n) + ) + throw new le.ErrnoError(44); + if (t.mount !== n.mount) throw new le.ErrnoError(75); + var u, + l = le.lookupNode(t, s), + c = V.relative(e, a); + if ('.' !== c.charAt(0)) throw new le.ErrnoError(28); + if ('.' !== (c = V.relative(r, o)).charAt(0)) throw new le.ErrnoError(55); + try { + u = le.lookupNode(n, i); + } catch (e) {} + if (l !== u) { + var d = le.isDir(l.mode), + h = le.mayDelete(t, s, d); + if (h) throw new le.ErrnoError(h); + if ((h = u ? le.mayDelete(n, i, d) : le.mayCreate(n, i))) throw new le.ErrnoError(h); + if (!t.node_ops.rename) throw new le.ErrnoError(63); + if (le.isMountpoint(l) || (u && le.isMountpoint(u))) throw new le.ErrnoError(10); + if (n !== t && (h = le.nodePermissions(t, 'w'))) throw new le.ErrnoError(h); + le.hashRemoveNode(l); + try { + t.node_ops.rename(l, n, i); + } catch (e) { + throw e; + } finally { + le.hashAddNode(l); + } + } + }, + rmdir(e) { + var r = le.lookupPath(e, { parent: !0 }).node, + t = Z.basename(e), + n = le.lookupNode(r, t), + o = le.mayDelete(r, t, !0); + if (o) throw new le.ErrnoError(o); + if (!r.node_ops.rmdir) throw new le.ErrnoError(63); + if (le.isMountpoint(n)) throw new le.ErrnoError(10); + (r.node_ops.rmdir(r, t), le.destroyNode(n)); + }, + readdir(e) { + var r = le.lookupPath(e, { follow: !0 }).node; + if (!r.node_ops.readdir) throw new le.ErrnoError(54); + return r.node_ops.readdir(r); + }, + unlink(e) { + var r = le.lookupPath(e, { parent: !0 }).node; + if (!r) throw new le.ErrnoError(44); + var t = Z.basename(e), + n = le.lookupNode(r, t), + o = le.mayDelete(r, t, !1); + if (o) throw new le.ErrnoError(o); + if (!r.node_ops.unlink) throw new le.ErrnoError(63); + if (le.isMountpoint(n)) throw new le.ErrnoError(10); + (r.node_ops.unlink(r, t), le.destroyNode(n)); + }, + readlink(e) { + var r = le.lookupPath(e).node; + if (!r) throw new le.ErrnoError(44); + if (!r.node_ops.readlink) throw new le.ErrnoError(28); + return V.resolve(le.getPath(r.parent), r.node_ops.readlink(r)); + }, + stat(e, r) { + var t = le.lookupPath(e, { follow: !r }).node; + if (!t) throw new le.ErrnoError(44); + if (!t.node_ops.getattr) throw new le.ErrnoError(63); + return t.node_ops.getattr(t); + }, + lstat: (e) => le.stat(e, !0), + chmod(e, r, t) { + var n; + if ( + !(n = 'string' == typeof e ? le.lookupPath(e, { follow: !t }).node : e).node_ops + .setattr + ) + throw new le.ErrnoError(63); + n.node_ops.setattr(n, { mode: (4095 & r) | (-4096 & n.mode), timestamp: Date.now() }); + }, + lchmod(e, r) { + le.chmod(e, r, !0); + }, + fchmod(e, r) { + var t = le.getStreamChecked(e); + le.chmod(t.node, r); + }, + chown(e, r, t, n) { + var o; + if ( + !(o = 'string' == typeof e ? le.lookupPath(e, { follow: !n }).node : e).node_ops + .setattr + ) + throw new le.ErrnoError(63); + o.node_ops.setattr(o, { timestamp: Date.now() }); + }, + lchown(e, r, t) { + le.chown(e, r, t, !0); + }, + fchown(e, r, t) { + var n = le.getStreamChecked(e); + le.chown(n.node, r, t); + }, + truncate(e, r) { + if (r < 0) throw new le.ErrnoError(28); + var t; + if ( + !(t = 'string' == typeof e ? le.lookupPath(e, { follow: !0 }).node : e).node_ops + .setattr + ) + throw new le.ErrnoError(63); + if (le.isDir(t.mode)) throw new le.ErrnoError(31); + if (!le.isFile(t.mode)) throw new le.ErrnoError(28); + var n = le.nodePermissions(t, 'w'); + if (n) throw new le.ErrnoError(n); + t.node_ops.setattr(t, { size: r, timestamp: Date.now() }); + }, + ftruncate(e, r) { + var t = le.getStreamChecked(e); + if (0 == (2097155 & t.flags)) throw new le.ErrnoError(28); + le.truncate(t.node, r); + }, + utime(e, r, t) { + var n = le.lookupPath(e, { follow: !0 }).node; + n.node_ops.setattr(n, { timestamp: Math.max(r, t) }); + }, + open(e, r, t) { + if ('' === e) throw new le.ErrnoError(44); + var o; + if ( + ((t = void 0 === t ? 438 : t), + (t = + 64 & + (r = + 'string' == typeof r + ? ((e) => { + var r = { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }[e]; + if (void 0 === r) throw new Error(`Unknown file open mode: ${e}`); + return r; + })(r) + : r) + ? (4095 & t) | 32768 + : 0), + 'object' == typeof e) + ) + o = e; + else { + e = Z.normalize(e); + try { + o = le.lookupPath(e, { follow: !(131072 & r) }).node; + } catch (e) {} + } + var a = !1; + if (64 & r) + if (o) { + if (128 & r) throw new le.ErrnoError(20); + } else ((o = le.mknod(e, t, 0)), (a = !0)); + if (!o) throw new le.ErrnoError(44); + if ((le.isChrdev(o.mode) && (r &= -513), 65536 & r && !le.isDir(o.mode))) + throw new le.ErrnoError(54); + if (!a) { + var s = le.mayOpen(o, r); + if (s) throw new le.ErrnoError(s); + } + (512 & r && !a && le.truncate(o, 0), (r &= -131713)); + var i = le.createStream({ + node: o, + path: le.getPath(o), + flags: r, + seekable: !0, + position: 0, + stream_ops: o.stream_ops, + ungotten: [], + error: !1, + }); + return ( + i.stream_ops.open && i.stream_ops.open(i), + !n.logReadFiles || + 1 & r || + (le.readFiles || (le.readFiles = {}), e in le.readFiles || (le.readFiles[e] = 1)), + i + ); + }, + close(e) { + if (le.isClosed(e)) throw new le.ErrnoError(8); + e.getdents && (e.getdents = null); + try { + e.stream_ops.close && e.stream_ops.close(e); + } catch (e) { + throw e; + } finally { + le.closeStream(e.fd); + } + e.fd = null; + }, + isClosed: (e) => null === e.fd, + llseek(e, r, t) { + if (le.isClosed(e)) throw new le.ErrnoError(8); + if (!e.seekable || !e.stream_ops.llseek) throw new le.ErrnoError(70); + if (0 != t && 1 != t && 2 != t) throw new le.ErrnoError(28); + return ((e.position = e.stream_ops.llseek(e, r, t)), (e.ungotten = []), e.position); + }, + read(e, r, t, n, o) { + if (n < 0 || o < 0) throw new le.ErrnoError(28); + if (le.isClosed(e)) throw new le.ErrnoError(8); + if (1 == (2097155 & e.flags)) throw new le.ErrnoError(8); + if (le.isDir(e.node.mode)) throw new le.ErrnoError(31); + if (!e.stream_ops.read) throw new le.ErrnoError(28); + var a = void 0 !== o; + if (a) { + if (!e.seekable) throw new le.ErrnoError(70); + } else o = e.position; + var s = e.stream_ops.read(e, r, t, n, o); + return (a || (e.position += s), s); + }, + write(e, r, t, n, o, a) { + if (n < 0 || o < 0) throw new le.ErrnoError(28); + if (le.isClosed(e)) throw new le.ErrnoError(8); + if (0 == (2097155 & e.flags)) throw new le.ErrnoError(8); + if (le.isDir(e.node.mode)) throw new le.ErrnoError(31); + if (!e.stream_ops.write) throw new le.ErrnoError(28); + e.seekable && 1024 & e.flags && le.llseek(e, 0, 2); + var s = void 0 !== o; + if (s) { + if (!e.seekable) throw new le.ErrnoError(70); + } else o = e.position; + var i = e.stream_ops.write(e, r, t, n, o, a); + return (s || (e.position += i), i); + }, + allocate(e, r, t) { + if (le.isClosed(e)) throw new le.ErrnoError(8); + if (r < 0 || t <= 0) throw new le.ErrnoError(28); + if (0 == (2097155 & e.flags)) throw new le.ErrnoError(8); + if (!le.isFile(e.node.mode) && !le.isDir(e.node.mode)) throw new le.ErrnoError(43); + if (!e.stream_ops.allocate) throw new le.ErrnoError(138); + e.stream_ops.allocate(e, r, t); + }, + mmap(e, r, t, n, o) { + if (0 != (2 & n) && 0 == (2 & o) && 2 != (2097155 & e.flags)) + throw new le.ErrnoError(2); + if (1 == (2097155 & e.flags)) throw new le.ErrnoError(2); + if (!e.stream_ops.mmap) throw new le.ErrnoError(43); + return e.stream_ops.mmap(e, r, t, n, o); + }, + msync: (e, r, t, n, o) => (e.stream_ops.msync ? e.stream_ops.msync(e, r, t, n, o) : 0), + munmap: (e) => 0, + ioctl(e, r, t) { + if (!e.stream_ops.ioctl) throw new le.ErrnoError(59); + return e.stream_ops.ioctl(e, r, t); + }, + readFile(e, r = {}) { + if ( + ((r.flags = r.flags || 0), + (r.encoding = r.encoding || 'binary'), + 'utf8' !== r.encoding && 'binary' !== r.encoding) + ) + throw new Error(`Invalid encoding type "${r.encoding}"`); + var t, + n = le.open(e, r.flags), + o = le.stat(e).size, + a = new Uint8Array(o); + return ( + le.read(n, a, 0, o, 0), + 'utf8' === r.encoding ? (t = Q(a, 0)) : 'binary' === r.encoding && (t = a), + le.close(n), + t + ); + }, + writeFile(e, r, t = {}) { + t.flags = t.flags || 577; + var n = le.open(e, t.flags, t.mode); + if ('string' == typeof r) { + var o = new Uint8Array(re(r) + 1), + a = te(r, o, 0, o.length); + le.write(n, o, 0, a, void 0, t.canOwn); + } else { + if (!ArrayBuffer.isView(r)) throw new Error('Unsupported data type'); + le.write(n, r, 0, r.byteLength, void 0, t.canOwn); + } + le.close(n); + }, + cwd: () => le.currentPath, + chdir(e) { + var r = le.lookupPath(e, { follow: !0 }); + if (null === r.node) throw new le.ErrnoError(44); + if (!le.isDir(r.node.mode)) throw new le.ErrnoError(54); + var t = le.nodePermissions(r.node, 'x'); + if (t) throw new le.ErrnoError(t); + le.currentPath = r.path; + }, + createDefaultDirectories() { + (le.mkdir('/tmp'), le.mkdir('/home'), le.mkdir('/home/web_user')); + }, + createDefaultDevices() { + (le.mkdir('/dev'), + le.registerDevice(le.makedev(1, 3), { read: () => 0, write: (e, r, t, n, o) => n }), + le.mkdev('/dev/null', le.makedev(1, 3)), + oe.register(le.makedev(5, 0), oe.default_tty_ops), + oe.register(le.makedev(6, 0), oe.default_tty1_ops), + le.mkdev('/dev/tty', le.makedev(5, 0)), + le.mkdev('/dev/tty1', le.makedev(6, 0))); + var e = new Uint8Array(1024), + r = 0, + t = () => (0 === r && (r = G(e).byteLength), e[--r]); + (le.createDevice('/dev', 'random', t), + le.createDevice('/dev', 'urandom', t), + le.mkdir('/dev/shm'), + le.mkdir('/dev/shm/tmp')); + }, + createSpecialDirectories() { + le.mkdir('/proc'); + var e = le.mkdir('/proc/self'); + (le.mkdir('/proc/self/fd'), + le.mount( + { + mount() { + var r = le.createNode(e, 'fd', 16895, 73); + return ( + (r.node_ops = { + lookup(e, r) { + var t = +r, + n = le.getStreamChecked(t), + o = { + parent: null, + mount: { mountpoint: 'fake' }, + node_ops: { readlink: () => n.path }, + }; + return ((o.parent = o), o); + }, + }), + r + ); + }, + }, + {}, + '/proc/self/fd', + )); + }, + createStandardStreams() { + (n.stdin + ? le.createDevice('/dev', 'stdin', n.stdin) + : le.symlink('/dev/tty', '/dev/stdin'), + n.stdout + ? le.createDevice('/dev', 'stdout', null, n.stdout) + : le.symlink('/dev/tty', '/dev/stdout'), + n.stderr + ? le.createDevice('/dev', 'stderr', null, n.stderr) + : le.symlink('/dev/tty1', '/dev/stderr'), + le.open('/dev/stdin', 0), + le.open('/dev/stdout', 1), + le.open('/dev/stderr', 1)); + }, + ensureErrnoError() { + le.ErrnoError || + ((le.ErrnoError = function (e, r) { + ((this.name = 'ErrnoError'), + (this.node = r), + (this.setErrno = function (e) { + this.errno = e; + }), + this.setErrno(e), + (this.message = 'FS error')); + }), + (le.ErrnoError.prototype = new Error()), + (le.ErrnoError.prototype.constructor = le.ErrnoError), + [44].forEach((e) => { + ((le.genericErrors[e] = new le.ErrnoError(e)), + (le.genericErrors[e].stack = '')); + })); + }, + staticInit() { + (le.ensureErrnoError(), + (le.nameTable = new Array(4096)), + le.mount(se, {}, '/'), + le.createDefaultDirectories(), + le.createDefaultDevices(), + le.createSpecialDirectories(), + (le.filesystems = { MEMFS: se })); + }, + init(e, r, t) { + ((le.init.initialized = !0), + le.ensureErrnoError(), + (n.stdin = e || n.stdin), + (n.stdout = r || n.stdout), + (n.stderr = t || n.stderr), + le.createStandardStreams()); + }, + quit() { + le.init.initialized = !1; + for (var e = 0; e < le.streams.length; e++) { + var r = le.streams[e]; + r && le.close(r); + } + }, + findObject(e, r) { + var t = le.analyzePath(e, r); + return t.exists ? t.object : null; + }, + analyzePath(e, r) { + try { + e = (n = le.lookupPath(e, { follow: !r })).path; + } catch (e) {} + var t = { + isRoot: !1, + exists: !1, + error: 0, + name: null, + path: null, + object: null, + parentExists: !1, + parentPath: null, + parentObject: null, + }; + try { + var n = le.lookupPath(e, { parent: !0 }); + ((t.parentExists = !0), + (t.parentPath = n.path), + (t.parentObject = n.node), + (t.name = Z.basename(e)), + (n = le.lookupPath(e, { follow: !r })), + (t.exists = !0), + (t.path = n.path), + (t.object = n.node), + (t.name = n.node.name), + (t.isRoot = '/' === n.path)); + } catch (e) { + t.error = e.errno; + } + return t; + }, + createPath(e, r, t, n) { + e = 'string' == typeof e ? e : le.getPath(e); + for (var o = r.split('/').reverse(); o.length; ) { + var a = o.pop(); + if (a) { + var s = Z.join2(e, a); + try { + le.mkdir(s); + } catch (e) {} + e = s; + } + } + return s; + }, + createFile(e, r, t, n, o) { + var a = Z.join2('string' == typeof e ? e : le.getPath(e), r), + s = ue(n, o); + return le.create(a, s); + }, + createDataFile(e, r, t, n, o, a) { + var s = r; + e && ((e = 'string' == typeof e ? e : le.getPath(e)), (s = r ? Z.join2(e, r) : e)); + var i = ue(n, o), + u = le.create(s, i); + if (t) { + if ('string' == typeof t) { + for (var l = new Array(t.length), c = 0, d = t.length; c < d; ++c) + l[c] = t.charCodeAt(c); + t = l; + } + le.chmod(u, 146 | i); + var h = le.open(u, 577); + (le.write(h, t, 0, t.length, 0, a), le.close(h), le.chmod(u, i)); + } + }, + createDevice(e, r, t, n) { + var o = Z.join2('string' == typeof e ? e : le.getPath(e), r), + a = ue(!!t, !!n); + le.createDevice.major || (le.createDevice.major = 64); + var s = le.makedev(le.createDevice.major++, 0); + return ( + le.registerDevice(s, { + open(e) { + e.seekable = !1; + }, + close(e) { + n?.buffer?.length && n(10); + }, + read(e, r, n, o, a) { + for (var s = 0, i = 0; i < o; i++) { + var u; + try { + u = t(); + } catch (e) { + throw new le.ErrnoError(29); + } + if (void 0 === u && 0 === s) throw new le.ErrnoError(6); + if (null == u) break; + (s++, (r[n + i] = u)); + } + return (s && (e.node.timestamp = Date.now()), s); + }, + write(e, r, t, o, a) { + for (var s = 0; s < o; s++) + try { + n(r[t + s]); + } catch (e) { + throw new le.ErrnoError(29); + } + return (o && (e.node.timestamp = Date.now()), s); + }, + }), + le.mkdev(o, a, s) + ); + }, + forceLoadFile(e) { + if (e.isDevice || e.isFolder || e.link || e.contents) return !0; + if ('undefined' != typeof XMLHttpRequest) + throw new Error( + 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.', + ); + if (!o) throw new Error('Cannot load without read() or XMLHttpRequest.'); + try { + ((e.contents = ne(o(e.url), !0)), (e.usedBytes = e.contents.length)); + } catch (e) { + throw new le.ErrnoError(29); + } + }, + createLazyFile(e, r, t, n, o) { + function a() { + ((this.lengthKnown = !1), (this.chunks = [])); + } + if ( + ((a.prototype.get = function (e) { + if (!(e > this.length - 1 || e < 0)) { + var r = e % this.chunkSize, + t = (e / this.chunkSize) | 0; + return this.getter(t)[r]; + } + }), + (a.prototype.setDataGetter = function (e) { + this.getter = e; + }), + (a.prototype.cacheLength = function () { + var e = new XMLHttpRequest(); + if ( + (e.open('HEAD', t, !1), + e.send(null), + !((e.status >= 200 && e.status < 300) || 304 === e.status)) + ) + throw new Error("Couldn't load " + t + '. Status: ' + e.status); + var r, + n = Number(e.getResponseHeader('Content-length')), + o = (r = e.getResponseHeader('Accept-Ranges')) && 'bytes' === r, + a = (r = e.getResponseHeader('Content-Encoding')) && 'gzip' === r, + s = 1048576; + o || (s = n); + var i = this; + (i.setDataGetter((e) => { + var r = e * s, + o = (e + 1) * s - 1; + if ( + ((o = Math.min(o, n - 1)), + void 0 === i.chunks[e] && + (i.chunks[e] = ((e, r) => { + if (e > r) + throw new Error( + 'invalid range (' + e + ', ' + r + ') or no bytes requested!', + ); + if (r > n - 1) + throw new Error('only ' + n + ' bytes available! programmer error!'); + var o = new XMLHttpRequest(); + if ( + (o.open('GET', t, !1), + n !== s && o.setRequestHeader('Range', 'bytes=' + e + '-' + r), + (o.responseType = 'arraybuffer'), + o.overrideMimeType && + o.overrideMimeType('text/plain; charset=x-user-defined'), + o.send(null), + !((o.status >= 200 && o.status < 300) || 304 === o.status)) + ) + throw new Error("Couldn't load " + t + '. Status: ' + o.status); + return void 0 !== o.response + ? new Uint8Array(o.response || []) + : ne(o.responseText || '', !0); + })(r, o)), + void 0 === i.chunks[e]) + ) + throw new Error('doXHR failed!'); + return i.chunks[e]; + }), + (!a && n) || + ((s = n = 1), + (n = this.getter(0).length), + (s = n), + _( + 'LazyFiles on gzip forces download of the whole file when length is accessed', + )), + (this._length = n), + (this._chunkSize = s), + (this.lengthKnown = !0)); + }), + 'undefined' != typeof XMLHttpRequest) + ) { + if (!d) + throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; + var s = new a(); + Object.defineProperties(s, { + length: { + get: function () { + return (this.lengthKnown || this.cacheLength(), this._length); + }, + }, + chunkSize: { + get: function () { + return (this.lengthKnown || this.cacheLength(), this._chunkSize); + }, + }, + }); + var i = { isDevice: !1, contents: s }; + } else i = { isDevice: !1, url: t }; + var u = le.createFile(e, r, i, n, o); + (i.contents + ? (u.contents = i.contents) + : i.url && ((u.contents = null), (u.url = i.url)), + Object.defineProperties(u, { + usedBytes: { + get: function () { + return this.contents.length; + }, + }, + })); + var l = {}; + function c(e, r, t, n, o) { + var a = e.node.contents; + if (o >= a.length) return 0; + var s = Math.min(a.length - o, n); + if (a.slice) for (var i = 0; i < s; i++) r[t + i] = a[o + i]; + else for (i = 0; i < s; i++) r[t + i] = a.get(o + i); + return s; + } + return ( + Object.keys(u.stream_ops).forEach((e) => { + var r = u.stream_ops[e]; + l[e] = function () { + return (le.forceLoadFile(u), r.apply(null, arguments)); + }; + }), + (l.read = (e, r, t, n, o) => (le.forceLoadFile(u), c(e, r, t, n, o))), + (l.mmap = (e, r, t, n, o) => { + le.forceLoadFile(u); + var a = ae(); + if (!a) throw new le.ErrnoError(48); + return (c(e, E, a, r, t), { ptr: a, allocated: !0 }); + }), + (u.stream_ops = l), + u + ); + }, + }, + ce = (e, r) => (e ? Q(k, e, r) : ''), + de = { + DEFAULT_POLLMASK: 5, + calculateAt(e, r, t) { + if (Z.isAbs(r)) return r; + var n; + if (((n = -100 === e ? le.cwd() : de.getStreamFromFD(e).path), 0 == r.length)) { + if (!t) throw new le.ErrnoError(44); + return n; + } + return Z.join2(n, r); + }, + doStat(e, r, t) { + try { + var n = e(r); + } catch (e) { + if (e && e.node && Z.normalize(r) !== Z.normalize(le.getPath(e.node))) return -54; + throw e; + } + ((F[t >> 2] = n.dev), + (F[(t + 4) >> 2] = n.mode), + (D[(t + 8) >> 2] = n.nlink), + (F[(t + 12) >> 2] = n.uid), + (F[(t + 16) >> 2] = n.gid), + (F[(t + 20) >> 2] = n.rdev), + (I = [ + n.size >>> 0, + ((j = n.size), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ]), + (F[(t + 24) >> 2] = I[0]), + (F[(t + 28) >> 2] = I[1]), + (F[(t + 32) >> 2] = 4096), + (F[(t + 36) >> 2] = n.blocks)); + var o = n.atime.getTime(), + a = n.mtime.getTime(), + s = n.ctime.getTime(); + return ( + (I = [ + Math.floor(o / 1e3) >>> 0, + ((j = Math.floor(o / 1e3)), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ]), + (F[(t + 40) >> 2] = I[0]), + (F[(t + 44) >> 2] = I[1]), + (D[(t + 48) >> 2] = (o % 1e3) * 1e3), + (I = [ + Math.floor(a / 1e3) >>> 0, + ((j = Math.floor(a / 1e3)), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ]), + (F[(t + 56) >> 2] = I[0]), + (F[(t + 60) >> 2] = I[1]), + (D[(t + 64) >> 2] = (a % 1e3) * 1e3), + (I = [ + Math.floor(s / 1e3) >>> 0, + ((j = Math.floor(s / 1e3)), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ]), + (F[(t + 72) >> 2] = I[0]), + (F[(t + 76) >> 2] = I[1]), + (D[(t + 80) >> 2] = (s % 1e3) * 1e3), + (I = [ + n.ino >>> 0, + ((j = n.ino), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ]), + (F[(t + 88) >> 2] = I[0]), + (F[(t + 92) >> 2] = I[1]), + 0 + ); + }, + doMsync(e, r, t, n, o) { + if (!le.isFile(r.node.mode)) throw new le.ErrnoError(43); + if (2 & n) return 0; + var a = k.slice(e, e + t); + le.msync(r, a, o, t, n); + }, + varargs: void 0, + get() { + var e = F[+de.varargs >> 2]; + return ((de.varargs += 4), e); + }, + getp: () => de.get(), + getStr: (e) => ce(e), + getStreamFromFD: (e) => le.getStreamChecked(e), + }, + he = (e) => ((F[Oe() >> 2] = e), e), + fe = { + BUCKET_BUFFER_SIZE: 8192, + mount: (e) => le.createNode(null, '/', 16895, 0), + createPipe() { + var e = { buckets: [], refcnt: 2 }; + e.buckets.push({ + buffer: new Uint8Array(fe.BUCKET_BUFFER_SIZE), + offset: 0, + roffset: 0, + }); + var r = fe.nextname(), + t = fe.nextname(), + n = le.createNode(fe.root, r, 4096, 0), + o = le.createNode(fe.root, t, 4096, 0); + ((n.pipe = e), (o.pipe = e)); + var a = le.createStream({ + path: r, + node: n, + flags: 0, + seekable: !1, + stream_ops: fe.stream_ops, + }); + n.stream = a; + var s = le.createStream({ + path: t, + node: o, + flags: 1, + seekable: !1, + stream_ops: fe.stream_ops, + }); + return ((o.stream = s), { readable_fd: a.fd, writable_fd: s.fd }); + }, + stream_ops: { + poll(e) { + var r = e.node.pipe; + if (1 == (2097155 & e.flags)) return 260; + if (r.buckets.length > 0) + for (var t = 0; t < r.buckets.length; t++) { + var n = r.buckets[t]; + if (n.offset - n.roffset > 0) return 65; + } + return 0; + }, + ioctl: (e, r, t) => 28, + fsync: (e) => 28, + read(e, r, t, n, o) { + for (var a = e.node.pipe, s = 0, i = 0; i < a.buckets.length; i++) { + var u = a.buckets[i]; + s += u.offset - u.roffset; + } + var l = r.subarray(t, t + n); + if (n <= 0) return 0; + if (0 == s) throw new le.ErrnoError(6); + var c = Math.min(s, n), + d = c, + h = 0; + for (i = 0; i < a.buckets.length; i++) { + var f = a.buckets[i], + m = f.offset - f.roffset; + if (c <= m) { + var p = f.buffer.subarray(f.roffset, f.offset); + (c < m ? ((p = p.subarray(0, c)), (f.roffset += c)) : h++, l.set(p)); + break; + } + ((p = f.buffer.subarray(f.roffset, f.offset)), + l.set(p), + (l = l.subarray(p.byteLength)), + (c -= p.byteLength), + h++); + } + return ( + h && + h == a.buckets.length && + (h--, (a.buckets[h].offset = 0), (a.buckets[h].roffset = 0)), + a.buckets.splice(0, h), + d + ); + }, + write(e, r, t, n, o) { + var a = e.node.pipe, + s = r.subarray(t, t + n), + i = s.byteLength; + if (i <= 0) return 0; + var u = null; + (0 == a.buckets.length + ? ((u = { buffer: new Uint8Array(fe.BUCKET_BUFFER_SIZE), offset: 0, roffset: 0 }), + a.buckets.push(u)) + : (u = a.buckets[a.buckets.length - 1]), + A(u.offset <= fe.BUCKET_BUFFER_SIZE)); + var l = fe.BUCKET_BUFFER_SIZE - u.offset; + if (l >= i) return (u.buffer.set(s, u.offset), (u.offset += i), i); + l > 0 && + (u.buffer.set(s.subarray(0, l), u.offset), + (u.offset += l), + (s = s.subarray(l, s.byteLength))); + for ( + var c = (s.byteLength / fe.BUCKET_BUFFER_SIZE) | 0, + d = s.byteLength % fe.BUCKET_BUFFER_SIZE, + h = 0; + h < c; + h++ + ) { + var f = { + buffer: new Uint8Array(fe.BUCKET_BUFFER_SIZE), + offset: fe.BUCKET_BUFFER_SIZE, + roffset: 0, + }; + (a.buckets.push(f), + f.buffer.set(s.subarray(0, fe.BUCKET_BUFFER_SIZE)), + (s = s.subarray(fe.BUCKET_BUFFER_SIZE, s.byteLength))); + } + return ( + d > 0 && + ((f = { + buffer: new Uint8Array(fe.BUCKET_BUFFER_SIZE), + offset: s.byteLength, + roffset: 0, + }), + a.buckets.push(f), + f.buffer.set(s)), + i + ); + }, + close(e) { + var r = e.node.pipe; + (r.refcnt--, 0 === r.refcnt && (r.buckets = null)); + }, + }, + nextname: () => ( + fe.nextname.current || (fe.nextname.current = 0), + 'pipe[' + fe.nextname.current++ + ']' + ), + }, + me = (e, r) => ((r + 2097152) >>> 0 < 4194305 - !!e ? (e >>> 0) + 4294967296 * r : NaN), + pe = (e) => e % 4 == 0 && (e % 100 != 0 || e % 400 == 0), + ve = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], + we = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], + ge = (e) => (pe(e.getFullYear()) ? ve : we)[e.getMonth()] + e.getDate() - 1, + _e = (e, r, t) => te(e, k, r, t), + ye = (e) => { + var r = re(e) + 1, + t = xe(r); + return (t && _e(e, t, r), t); + }, + Ee = (e) => { + var r = (e - g.buffer.byteLength + 65535) / 65536; + try { + return (g.grow(r), C(), 1); + } catch (e) {} + }, + be = {}, + ke = () => { + if (!ke.strings) { + var e = { + USER: 'web_user', + LOGNAME: 'web_user', + PATH: '/', + PWD: '/', + HOME: '/home/web_user', + LANG: + ( + ('object' == typeof navigator && navigator.languages && navigator.languages[0]) || + 'C' + ).replace('-', '_') + '.UTF-8', + _: u || './this.program', + }; + for (var r in be) void 0 === be[r] ? delete e[r] : (e[r] = be[r]); + var t = []; + for (var r in e) t.push(`${r}=${e[r]}`); + ke.strings = t; + } + return ke.strings; + }, + Se = (e) => { + (X || (n.onExit?.(e), (M = !0)), l(e, new q(e))); + }, + Fe = (e, r) => { + Se(e); + }, + De = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + Me = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + Ae = (e, r) => { + E.set(e, r); + }, + Ce = (e) => n['_' + e], + Pe = (e, r, t, n, o) => { + var a = { + string: (e) => { + var r = 0; + return ( + null != e && + 0 !== e && + (r = ((e) => { + var r = re(e) + 1, + t = He(r); + return (_e(e, t, r), t); + })(e)), + r + ); + }, + array: (e) => { + var r = He(e.length); + return (Ae(e, r), r); + }, + }, + s = Ce(e), + i = [], + u = 0; + if (n) + for (var l = 0; l < n.length; l++) { + var c = a[t[l]]; + c ? (0 === u && (u = je()), (i[l] = c(n[l]))) : (i[l] = n[l]); + } + var d = s.apply(null, i); + return (d = (function (e) { + return ( + 0 !== u && Ie(u), + (function (e) { + return 'string' === r ? ce(e) : 'boolean' === r ? Boolean(e) : e; + })(e) + ); + })(d)); + }, + Te = function (e, r, t, n) { + (e || (e = this), + (this.parent = e), + (this.mount = e.mount), + (this.mounted = null), + (this.id = le.nextInode++), + (this.name = r), + (this.mode = t), + (this.node_ops = {}), + (this.stream_ops = {}), + (this.rdev = n)); + }, + Re = 365, + ze = 146; + (Object.defineProperties(Te.prototype, { + read: { + get: function () { + return (this.mode & Re) === Re; + }, + set: function (e) { + e ? (this.mode |= Re) : (this.mode &= -366); + }, + }, + write: { + get: function () { + return (this.mode & ze) === ze; + }, + set: function (e) { + e ? (this.mode |= ze) : (this.mode &= -147); + }, + }, + isFolder: { + get: function () { + return le.isDir(this.mode); + }, + }, + isDevice: { + get: function () { + return le.isChrdev(this.mode); + }, + }, + }), + (le.FSNode = Te), + (le.createPreloadedFile = (e, r, t, n, o, s, i, u, l, c) => { + var d = r ? V.resolve(Z.join2(e, r)) : e; + function h(t) { + function a(t) { + (c?.(), + u || + ((e, r, t, n, o, a) => { + le.createDataFile(e, r, t, n, o, a); + })(e, r, t, n, o, l), + s?.(), + x()); + } + ((e, r, t, n) => { + 'undefined' != typeof Browser && Browser.init(); + var o = !1; + return ( + ie.forEach((a) => { + o || (a.canHandle(r) && (a.handle(e, r, t, n), (o = !0))); + }), + o + ); + })(t, d, a, () => { + (i?.(), x()); + }) || a(t); + } + (L(), + 'string' == typeof t + ? ((e, r, t, n) => { + var o = n ? '' : `al ${e}`; + (a( + e, + (t) => { + (A(t, `Loading data file "${e}" failed (no arrayBuffer).`), + r(new Uint8Array(t)), + o && x()); + }, + (r) => { + if (!t) throw `Loading data file "${e}" failed.`; + t(); + }, + ), + o && L()); + })(t, (e) => h(e), i) + : h(t)); + }), + le.staticInit()); + var Ue = { + i: function (e) { + try { + var r = de.getStreamFromFD(e); + return le.createStream(r).fd; + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + a: function (e, r, t) { + de.varargs = t; + try { + var n = de.getStreamFromFD(e); + switch (r) { + case 0: + if ((o = de.get()) < 0) return -28; + for (; le.streams[o]; ) o++; + return le.createStream(n, o).fd; + case 1: + case 2: + case 6: + case 7: + return 0; + case 3: + return n.flags; + case 4: + var o = de.get(); + return ((n.flags |= o), 0); + case 5: + return ((o = de.getp()), (S[(o + 0) >> 1] = 2), 0); + case 16: + case 8: + default: + return -28; + case 9: + return (he(28), -1); + } + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + x: function (e, r) { + try { + var t = de.getStreamFromFD(e); + return de.doStat(le.stat, t.path, r); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + u: function (e, r) { + try { + return ((e = de.getStr(e)), de.doStat(le.lstat, e, r)); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + v: function (e, r, t, n) { + try { + r = de.getStr(r); + var o = 256 & n, + a = 4096 & n; + return ( + (n &= -6401), + (r = de.calculateAt(e, r, a)), + de.doStat(o ? le.lstat : le.stat, r, t) + ); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + y: function (e, r, t, n) { + de.varargs = n; + try { + ((r = de.getStr(r)), (r = de.calculateAt(e, r))); + var o = n ? de.get() : 0; + return le.open(r, t, o).fd; + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + t: function (e) { + try { + if (0 == e) throw new le.ErrnoError(21); + var r = fe.createPipe(); + return ((F[e >> 2] = r.readable_fd), (F[(e + 4) >> 2] = r.writable_fd), 0); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + s: function (e, r, t) { + try { + for (var n = 0, o = 0; o < r; o++) { + var a = e + 8 * o, + s = F[a >> 2], + i = S[(a + 4) >> 1], + u = 32, + l = le.getStream(s); + (l && + ((u = de.DEFAULT_POLLMASK), l.stream_ops.poll && (u = l.stream_ops.poll(l, -1))), + (u &= 24 | i) && n++, + (S[(a + 6) >> 1] = u)); + } + return n; + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + w: function (e, r) { + try { + return ((e = de.getStr(e)), de.doStat(le.stat, e, r)); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + q: function (e, r, t) { + try { + return ( + (r = de.getStr(r)), + (r = de.calculateAt(e, r)), + 0 === t + ? le.unlink(r) + : 512 === t + ? le.rmdir(r) + : O('Invalid flags passed to unlinkat'), + 0 + ); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return -e.errno; + } + }, + B: () => 1, + k: function (e, r, t) { + var n = me(e, r), + o = new Date(1e3 * n); + ((F[t >> 2] = o.getUTCSeconds()), + (F[(t + 4) >> 2] = o.getUTCMinutes()), + (F[(t + 8) >> 2] = o.getUTCHours()), + (F[(t + 12) >> 2] = o.getUTCDate()), + (F[(t + 16) >> 2] = o.getUTCMonth()), + (F[(t + 20) >> 2] = o.getUTCFullYear() - 1900), + (F[(t + 24) >> 2] = o.getUTCDay())); + var a = Date.UTC(o.getUTCFullYear(), 0, 1, 0, 0, 0, 0), + s = ((o.getTime() - a) / 864e5) | 0; + F[(t + 28) >> 2] = s; + }, + l: function (e, r, t) { + var n = me(e, r), + o = new Date(1e3 * n); + ((F[t >> 2] = o.getSeconds()), + (F[(t + 4) >> 2] = o.getMinutes()), + (F[(t + 8) >> 2] = o.getHours()), + (F[(t + 12) >> 2] = o.getDate()), + (F[(t + 16) >> 2] = o.getMonth()), + (F[(t + 20) >> 2] = o.getFullYear() - 1900), + (F[(t + 24) >> 2] = o.getDay())); + var a = 0 | ge(o); + ((F[(t + 28) >> 2] = a), (F[(t + 36) >> 2] = -60 * o.getTimezoneOffset())); + var s = new Date(o.getFullYear(), 0, 1), + i = new Date(o.getFullYear(), 6, 1).getTimezoneOffset(), + u = s.getTimezoneOffset(), + l = 0 | (i != u && o.getTimezoneOffset() == Math.min(u, i)); + F[(t + 32) >> 2] = l; + }, + m: function (e) { + var r = (() => { + var r = new Date( + F[(e + 20) >> 2] + 1900, + F[(e + 16) >> 2], + F[(e + 12) >> 2], + F[(e + 8) >> 2], + F[(e + 4) >> 2], + F[e >> 2], + 0, + ), + t = F[(e + 32) >> 2], + n = r.getTimezoneOffset(), + o = new Date(r.getFullYear(), 0, 1), + a = new Date(r.getFullYear(), 6, 1).getTimezoneOffset(), + s = o.getTimezoneOffset(), + i = Math.min(s, a); + if (t < 0) F[(e + 32) >> 2] = Number(a != s && i == n); + else if (t > 0 != (i == n)) { + var u = Math.max(s, a), + l = t > 0 ? i : u; + r.setTime(r.getTime() + 6e4 * (l - n)); + } + F[(e + 24) >> 2] = r.getDay(); + var c = 0 | ge(r); + ((F[(e + 28) >> 2] = c), + (F[e >> 2] = r.getSeconds()), + (F[(e + 4) >> 2] = r.getMinutes()), + (F[(e + 8) >> 2] = r.getHours()), + (F[(e + 12) >> 2] = r.getDate()), + (F[(e + 16) >> 2] = r.getMonth()), + (F[(e + 20) >> 2] = r.getYear())); + var d = r.getTime(); + return isNaN(d) ? (he(61), -1) : d / 1e3; + })(); + return ( + Be( + ((j = r), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ), + r >>> 0 + ); + }, + n: function (e) { + var r = (() => { + var r = Date.UTC( + F[(e + 20) >> 2] + 1900, + F[(e + 16) >> 2], + F[(e + 12) >> 2], + F[(e + 8) >> 2], + F[(e + 4) >> 2], + F[e >> 2], + 0, + ), + t = new Date(r); + F[(e + 24) >> 2] = t.getUTCDay(); + var n = Date.UTC(t.getUTCFullYear(), 0, 1, 0, 0, 0, 0), + o = ((t.getTime() - n) / 864e5) | 0; + return ((F[(e + 28) >> 2] = o), t.getTime() / 1e3); + })(); + return ( + Be( + ((j = r), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ), + r >>> 0 + ); + }, + r: (e, r, t) => { + var n = new Date().getFullYear(), + o = new Date(n, 0, 1), + a = new Date(n, 6, 1), + s = o.getTimezoneOffset(), + i = a.getTimezoneOffset(), + u = Math.max(s, i); + function l(e) { + var r = e.toTimeString().match(/\(([A-Za-z ]+)\)$/); + return r ? r[1] : 'GMT'; + } + ((D[e >> 2] = 60 * u), (F[r >> 2] = Number(s != i))); + var c = l(o), + d = l(a), + h = ye(c), + f = ye(d); + i < s + ? ((D[t >> 2] = h), (D[(t + 4) >> 2] = f)) + : ((D[t >> 2] = f), (D[(t + 4) >> 2] = h)); + }, + b: () => { + O(''); + }, + c: () => Date.now(), + h: (e, r, t) => k.copyWithin(e, r, r + t), + p: (e) => { + var r = k.length, + t = 2147483648; + if ((e >>>= 0) > t) return !1; + for (var n, o, a = 1; a <= 4; a *= 2) { + var s = r * (1 + 0.2 / a); + s = Math.min(s, e + 100663296); + var i = Math.min(t, (n = Math.max(e, s)) + (((o = 65536) - (n % o)) % o)); + if (Ee(i)) return !0; + } + return !1; + }, + z: (e, r) => { + var t = 0; + return ( + ke().forEach((n, o) => { + var a = r + t; + ((D[(e + 4 * o) >> 2] = a), + ((e, r) => { + for (var t = 0; t < e.length; ++t) E[r++ >> 0] = e.charCodeAt(t); + E[r >> 0] = 0; + })(n, a), + (t += n.length + 1)); + }), + 0 + ); + }, + A: (e, r) => { + var t = ke(); + D[e >> 2] = t.length; + var n = 0; + return (t.forEach((e) => (n += e.length + 1)), (D[r >> 2] = n), 0); + }, + g: Fe, + e: function (e) { + try { + var r = de.getStreamFromFD(e); + return (le.close(r), 0); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return e.errno; + } + }, + j: function (e, r, t, n) { + try { + var o = ((e, r, t, n) => { + for (var o = 0, a = 0; a < t; a++) { + var s = D[r >> 2], + i = D[(r + 4) >> 2]; + r += 8; + var u = le.read(e, E, s, i, n); + if (u < 0) return -1; + if (((o += u), u < i)) break; + void 0 !== n && (n += u); + } + return o; + })(de.getStreamFromFD(e), r, t); + return ((D[n >> 2] = o), 0); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return e.errno; + } + }, + o: function (e, r, t, n, o) { + var a = me(r, t); + try { + if (isNaN(a)) return 61; + var s = de.getStreamFromFD(e); + return ( + le.llseek(s, a, n), + (I = [ + s.position >>> 0, + ((j = s.position), + +Math.abs(j) >= 1 + ? j > 0 + ? +Math.floor(j / 4294967296) >>> 0 + : ~~+Math.ceil((j - +(~~j >>> 0)) / 4294967296) >>> 0 + : 0), + ]), + (F[o >> 2] = I[0]), + (F[(o + 4) >> 2] = I[1]), + s.getdents && 0 === a && 0 === n && (s.getdents = null), + 0 + ); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return e.errno; + } + }, + d: function (e, r, t, n) { + try { + var o = ((e, r, t, n) => { + for (var o = 0, a = 0; a < t; a++) { + var s = D[r >> 2], + i = D[(r + 4) >> 2]; + r += 8; + var u = le.write(e, E, s, i, n); + if (u < 0) return -1; + ((o += u), void 0 !== n && (n += u)); + } + return o; + })(de.getStreamFromFD(e), r, t); + return ((D[n >> 2] = o), 0); + } catch (e) { + if (void 0 === le || 'ErrnoError' !== e.name) throw e; + return e.errno; + } + }, + f: (e, r, t, n) => { + var o = D[(n + 40) >> 2], + a = { + tm_sec: F[n >> 2], + tm_min: F[(n + 4) >> 2], + tm_hour: F[(n + 8) >> 2], + tm_mday: F[(n + 12) >> 2], + tm_mon: F[(n + 16) >> 2], + tm_year: F[(n + 20) >> 2], + tm_wday: F[(n + 24) >> 2], + tm_yday: F[(n + 28) >> 2], + tm_isdst: F[(n + 32) >> 2], + tm_gmtoff: F[(n + 36) >> 2], + tm_zone: o ? ce(o) : '', + }, + s = ce(t), + i = { + '%c': '%a %b %d %H:%M:%S %Y', + '%D': '%m/%d/%y', + '%F': '%Y-%m-%d', + '%h': '%b', + '%r': '%I:%M:%S %p', + '%R': '%H:%M', + '%T': '%H:%M:%S', + '%x': '%m/%d/%y', + '%X': '%H:%M:%S', + '%Ec': '%c', + '%EC': '%C', + '%Ex': '%m/%d/%y', + '%EX': '%H:%M:%S', + '%Ey': '%y', + '%EY': '%Y', + '%Od': '%d', + '%Oe': '%e', + '%OH': '%H', + '%OI': '%I', + '%Om': '%m', + '%OM': '%M', + '%OS': '%S', + '%Ou': '%u', + '%OU': '%U', + '%OV': '%V', + '%Ow': '%w', + '%OW': '%W', + '%Oy': '%y', + }; + for (var u in i) s = s.replace(new RegExp(u, 'g'), i[u]); + var l = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + c = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ]; + function d(e, r, t) { + for (var n = 'number' == typeof e ? e.toString() : e || ''; n.length < r; ) + n = t[0] + n; + return n; + } + function h(e, r) { + return d(e, r, '0'); + } + function f(e, r) { + function t(e) { + return e < 0 ? -1 : e > 0 ? 1 : 0; + } + var n; + return ( + 0 === (n = t(e.getFullYear() - r.getFullYear())) && + 0 === (n = t(e.getMonth() - r.getMonth())) && + (n = t(e.getDate() - r.getDate())), + n + ); + } + function m(e) { + switch (e.getDay()) { + case 0: + return new Date(e.getFullYear() - 1, 11, 29); + case 1: + return e; + case 2: + return new Date(e.getFullYear(), 0, 3); + case 3: + return new Date(e.getFullYear(), 0, 2); + case 4: + return new Date(e.getFullYear(), 0, 1); + case 5: + return new Date(e.getFullYear() - 1, 11, 31); + case 6: + return new Date(e.getFullYear() - 1, 11, 30); + } + } + function p(e) { + var r = ((e, r) => { + for (var t = new Date(e.getTime()); r > 0; ) { + var n = pe(t.getFullYear()), + o = t.getMonth(), + a = (n ? De : Me)[o]; + if (!(r > a - t.getDate())) return (t.setDate(t.getDate() + r), t); + ((r -= a - t.getDate() + 1), + t.setDate(1), + o < 11 + ? t.setMonth(o + 1) + : (t.setMonth(0), t.setFullYear(t.getFullYear() + 1))); + } + return t; + })(new Date(e.tm_year + 1900, 0, 1), e.tm_yday), + t = new Date(r.getFullYear(), 0, 4), + n = new Date(r.getFullYear() + 1, 0, 4), + o = m(t), + a = m(n); + return f(o, r) <= 0 + ? f(a, r) <= 0 + ? r.getFullYear() + 1 + : r.getFullYear() + : r.getFullYear() - 1; + } + var v = { + '%a': (e) => l[e.tm_wday].substring(0, 3), + '%A': (e) => l[e.tm_wday], + '%b': (e) => c[e.tm_mon].substring(0, 3), + '%B': (e) => c[e.tm_mon], + '%C': (e) => h(((e.tm_year + 1900) / 100) | 0, 2), + '%d': (e) => h(e.tm_mday, 2), + '%e': (e) => d(e.tm_mday, 2, ' '), + '%g': (e) => p(e).toString().substring(2), + '%G': (e) => p(e), + '%H': (e) => h(e.tm_hour, 2), + '%I': (e) => { + var r = e.tm_hour; + return (0 == r ? (r = 12) : r > 12 && (r -= 12), h(r, 2)); + }, + '%j': (e) => + h( + e.tm_mday + + ((e, r) => { + for (var t = 0, n = 0; n <= r; t += e[n++]); + return t; + })(pe(e.tm_year + 1900) ? De : Me, e.tm_mon - 1), + 3, + ), + '%m': (e) => h(e.tm_mon + 1, 2), + '%M': (e) => h(e.tm_min, 2), + '%n': () => '\n', + '%p': (e) => (e.tm_hour >= 0 && e.tm_hour < 12 ? 'AM' : 'PM'), + '%S': (e) => h(e.tm_sec, 2), + '%t': () => '\t', + '%u': (e) => e.tm_wday || 7, + '%U': (e) => { + var r = e.tm_yday + 7 - e.tm_wday; + return h(Math.floor(r / 7), 2); + }, + '%V': (e) => { + var r = Math.floor((e.tm_yday + 7 - ((e.tm_wday + 6) % 7)) / 7); + if (((e.tm_wday + 371 - e.tm_yday - 2) % 7 <= 2 && r++, r)) { + if (53 == r) { + var t = (e.tm_wday + 371 - e.tm_yday) % 7; + 4 == t || (3 == t && pe(e.tm_year)) || (r = 1); + } + } else { + r = 52; + var n = (e.tm_wday + 7 - e.tm_yday - 1) % 7; + (4 == n || (5 == n && pe((e.tm_year % 400) - 1))) && r++; + } + return h(r, 2); + }, + '%w': (e) => e.tm_wday, + '%W': (e) => { + var r = e.tm_yday + 7 - ((e.tm_wday + 6) % 7); + return h(Math.floor(r / 7), 2); + }, + '%y': (e) => (e.tm_year + 1900).toString().substring(2), + '%Y': (e) => e.tm_year + 1900, + '%z': (e) => { + var r = e.tm_gmtoff, + t = r >= 0; + return ( + (r = ((r = Math.abs(r) / 60) / 60) * 100 + (r % 60)), + (t ? '+' : '-') + String('0000' + r).slice(-4) + ); + }, + '%Z': (e) => e.tm_zone, + '%%': () => '%', + }; + for (var u in ((s = s.replace(/%%/g, '\0\0')), v)) + s.includes(u) && (s = s.replace(new RegExp(u, 'g'), v[u](a))); + var w = ne((s = s.replace(/\0\0/g, '%')), !1); + return w.length > r ? 0 : (Ae(w, e), w.length - 1); + }, + }, + Le = (function () { + var e, + r, + o, + a, + s = { a: Ue }; + function i(e, r) { + var t; + return ((Le = e.exports), (g = Le.C), C(), (t = Le.D), T.unshift(t), x(), Le); + } + if ((L(), n.instantiateWasm)) + try { + return n.instantiateWasm(s, i); + } catch (e) { + (y(`Module.instantiateWasm callback failed with error: ${e}`), t(e)); + } + return ( + ((e = w), + (r = N), + (o = s), + (a = function (e) { + i(e.instance); + }), + e || + 'function' != typeof WebAssembly.instantiateStreaming || + H(r) || + Y(r) || + h || + 'function' != typeof fetch + ? K(r, o, a) + : fetch(r, { credentials: 'same-origin' }).then((e) => + WebAssembly.instantiateStreaming(e, o).then(a, function (e) { + return ( + y(`wasm streaming compile failed: ${e}`), + y('falling back to ArrayBuffer instantiation'), + K(r, o, a) + ); + }), + )).catch(t), + {} + ); + })(); + ((n._get_version = () => (n._get_version = Le.E)()), + (n._archive_open = (e, r, t, o) => (n._archive_open = Le.F)(e, r, t, o)), + (n._archive_read_add_passphrase = (e, r) => (n._archive_read_add_passphrase = Le.G)(e, r)), + (n._archive_error_string = (e) => (n._archive_error_string = Le.H)(e)), + (n._get_next_entry = (e) => (n._get_next_entry = Le.I)(e)), + (n._get_filedata = (e, r) => (n._get_filedata = Le.J)(e, r))); + var xe = (n._malloc = (e) => (xe = n._malloc = Le.K)(e)); + ((n._archive_close = (e) => (n._archive_close = Le.L)(e)), + (n._start_archive_write = (e, r, t, o, a, s) => + (n._start_archive_write = Le.M)(e, r, t, o, a, s)), + (n._write_archive_file = (e, r, t, o) => (n._write_archive_file = Le.N)(e, r, t, o)), + (n._size_of_size_t = () => (n._size_of_size_t = Le.O)()), + (n._finish_archive_write = (e, r) => (n._finish_archive_write = Le.P)(e, r)), + (n._free = (e) => (n._free = Le.Q)(e))); + var Oe = () => (Oe = Le.R)(); + ((n._archive_entry_birthtime_nsec = (e) => (n._archive_entry_birthtime_nsec = Le.S)(e)), + (n._archive_entry_filetype = (e) => (n._archive_entry_filetype = Le.T)(e)), + (n._archive_entry_mtime_nsec = (e) => (n._archive_entry_mtime_nsec = Le.U)(e)), + (n._archive_entry_pathname = (e) => (n._archive_entry_pathname = Le.V)(e)), + (n._archive_entry_pathname_utf8 = (e) => (n._archive_entry_pathname_utf8 = Le.W)(e)), + (n._archive_entry_size = (e) => (n._archive_entry_size = Le.X)(e)), + (n._archive_entry_is_encrypted = (e) => (n._archive_entry_is_encrypted = Le.Y)(e)), + (n._archive_read_has_encrypted_entries = (e) => + (n._archive_read_has_encrypted_entries = Le.Z)(e)), + (n._archive_read_data_skip = (e) => (n._archive_read_data_skip = Le._)(e))); + var Ne, + Be = (e) => (Be = Le.aa)(e), + je = () => (je = Le.ba)(), + Ie = (e) => (Ie = Le.ca)(e), + He = (e) => (He = Le.da)(e); + function Ye() { + function e() { + Ne || + ((Ne = !0), + (n.calledRun = !0), + M || + (n.noFSInit || le.init.initialized || le.init(), + (le.ignorePermissions = !1), + (fe.root = le.mount(fe, {}, null)), + $(T), + r(n), + n.onRuntimeInitialized && n.onRuntimeInitialized(), + (function () { + if (n.postRun) + for ( + 'function' == typeof n.postRun && (n.postRun = [n.postRun]); + n.postRun.length; + ) + ((e = n.postRun.shift()), R.unshift(e)); + var e; + $(R); + })())); + } + z > 0 || + ((function () { + if (n.preRun) + for ('function' == typeof n.preRun && (n.preRun = [n.preRun]); n.preRun.length; ) + ((e = n.preRun.shift()), P.unshift(e)); + var e; + $(P); + })(), + z > 0 || + (n.setStatus + ? (n.setStatus('Running...'), + setTimeout(function () { + (setTimeout(function () { + n.setStatus(''); + }, 1), + e()); + }, 1)) + : e())); + } + if ( + ((n.cwrap = (e, r, t, n) => { + var o = !t || t.every((e) => 'number' === e || 'boolean' === e); + return 'string' !== r && o && !n + ? Ce(e) + : function () { + return Pe(e, r, t, arguments); + }; + }), + (n.allocate = (e, r) => { + var t; + return ( + (t = 1 == r ? He(e.length) : xe(e.length)), + e.subarray || e.slice || (e = new Uint8Array(e)), + k.set(e, t), + t + ); + }), + (U = function e() { + (Ne || Ye(), Ne || (U = e)); + }), + n.preInit) + ) + for ('function' == typeof n.preInit && (n.preInit = [n.preInit]); n.preInit.length > 0; ) + n.preInit.pop()(); + return (Ye(), e.ready); + }); +class S { + constructor() { + ((this.preRun = []), (this.postRun = []), (this.totalDependencies = 0)); + } + print(...e) { + console.log(e); + } + printErr(...e) { + console.error(e); + } + initFunctions() { + this.runCode = { + getVersion: this.cwrap('get_version', 'string', []), + openArchive: this.cwrap('archive_open', 'number', ['number', 'number', 'string', 'string']), + getNextEntry: this.cwrap('get_next_entry', 'number', ['number']), + getFileData: this.cwrap('get_filedata', 'number', ['number', 'number']), + skipEntry: this.cwrap('archive_read_data_skip', 'number', ['number']), + closeArchive: this.cwrap('archive_close', null, ['number']), + getEntrySize: this.cwrap('archive_entry_size', 'number', ['number']), + getEntryName: this.cwrap('archive_entry_pathname', 'string', ['number']), + getEntryType: this.cwrap('archive_entry_filetype', 'number', ['number']), + getEntryLastModified: this.cwrap('archive_entry_mtime_nsec', 'number', ['number']), + getError: this.cwrap('archive_error_string', 'string', ['number']), + startArchiveWrite: this.cwrap('start_archive_write', 'number', [ + 'string', + 'string', + 'number', + 'number', + 'number', + 'string', + ]), + writeArchiveFile: this.cwrap('write_archive_file', null, [ + 'number', + 'string', + 'number', + 'number', + ]), + finishArchiveWrite: this.cwrap('finish_archive_write', 'number', ['number', 'number']), + entryIsEncrypted: this.cwrap('archive_entry_is_encrypted', 'number', ['number']), + hasEncryptedEntries: this.cwrap('archive_read_has_encrypted_entries', 'number', ['number']), + addPassphrase: this.cwrap('archive_read_add_passphrase', 'number', ['number', 'string']), + string: (e) => this.allocate(this.intArrayFromString(e), 'i8', 0), + malloc: this.cwrap('malloc', 'number', ['number']), + free: this.cwrap('free', null, ['number']), + sizeOfSizeT: this.cwrap('size_of_size_t', 'number', []), + }; + } + monitorRunDependencies() {} +} +let F = null, + D = null, + M = !1; +class A { + constructor(e) { + ((A.readyCallback = e), M && setTimeout(() => e(), 0)); + } + open(e, r) { + F.open(e).then(() => r()); + } + listFiles() { + let e = []; + for (const r of F.entries(!0)) e.push(r); + return e; + } + extractFiles() { + let e = []; + for (const r of F.entries(!1)) e.push(r); + return e; + } + extractSingleFile(e) { + for (const r of F.entries(!0, e)) if (r.fileData) return r; + } + hasEncryptedData() { + return F.hasEncryptedData(); + } + usePassword(e) { + F.setPassphrase(e); + } + setLocale(e) { + F.setLocale(e); + } + writeArchive(e, r, t, n) { + return D.write(e, r, t, n); + } + close() { + F.close(); + } +} +var C; +((C = (e) => { + ((F = new y(e)), (D = new E(e)), A?.readyCallback?.(), (M = !0)); +}), + k(new S()).then((e) => { + (e.initFunctions(), C(e)); + }), + i(A)); diff --git a/scripts/generate-robot-previews.js b/scripts/generate-robot-previews.js deleted file mode 100644 index 280cbcf2e..000000000 --- a/scripts/generate-robot-previews.js +++ /dev/null @@ -1,918 +0,0 @@ -#!/usr/bin/env node -/** - * Robot Preview Generator Script - * Generates high-quality animated WebP/GIF previews for URDF models - * - * Usage: node scripts/generate-robot-previews.js - * - * Requirements: - * - npm install puppeteer sharp gif-encoder-2 - * - Dev server running at http://localhost:5173 - */ - -const puppeteer = require('puppeteer'); -const fs = require('fs'); -const path = require('path'); -const { execSync } = require('child_process'); - -// Robot configurations - add your robots here -const ROBOTS = [ - { - id: 'go2', - name: 'Unitree Go2', - urdfPath: '/library/urdf/unitree/go2_description', - outputName: 'go2_preview' - }, - { - id: 'go1', - name: 'Unitree Go1', - urdfPath: '/library/urdf/unitree/go1_description', - outputName: 'go1_preview' - }, - { - id: 'g1', - name: 'Unitree G1', - urdfPath: '/library/urdf/unitree/g1_description', - urdfFile: 'g1_29dof_with_hand.urdf', - outputName: 'g1_preview' - }, - { - id: 'h1', - name: 'Unitree H1', - urdfPath: '/library/urdf/unitree/h1_description', - urdfFile: 'urdf/h1_with_hand.urdf', - outputName: 'h1_preview' - }, - { - id: 'h1_2', - name: 'Unitree H1 2.0', - urdfPath: '/library/urdf/unitree/h1_2_description', - outputName: 'h1_2_preview' - }, - { - id: 'a1', - name: 'Unitree A1', - urdfPath: '/library/urdf/unitree/a1_description', - outputName: 'a1_preview' - }, - { - id: 'b1', - name: 'Unitree B1', - urdfPath: '/library/urdf/unitree/b1_description', - outputName: 'b1_preview' - }, - { - id: 'b2', - name: 'Unitree B2', - urdfPath: '/library/urdf/unitree/b2_description', - outputName: 'b2_preview' - }, - { - id: 'aliengo', - name: 'Unitree Aliengo', - urdfPath: '/library/urdf/unitree/aliengo_description', - outputName: 'aliengo_preview' - }, -]; - -// Configuration -const CONFIG = { - width: 400, // Preview width - height: 400, // Preview height - frameCount: 60, // Number of frames (60 = 2 seconds at 30fps) - fps: 30, // Frames per second - quality: 90, // Image quality (1-100) - rotationSpeed: 360, // Degrees per full animation - outputDir: 'public/previews', - devServerUrl: 'http://localhost:5173', -}; - -// HTML template for rendering a single robot -const getPreviewHTML = (urdfPath, urdfFile) => ` - - - - - - -
- - - - -`; - -async function generatePreview(browser, robot) { - console.log(`\\n📦 Generating preview for ${robot.name}...`); - - const page = await browser.newPage(); - await page.setViewport({ width: CONFIG.width, height: CONFIG.height, deviceScaleFactor: 2 }); - - // Set HTML content - const html = getPreviewHTML(robot.urdfPath, robot.urdfFile); - await page.setContent(html, { waitUntil: 'networkidle0' }); - - // Wait for robot to load - console.log(' ⏳ Loading robot...'); - try { - await page.waitForFunction('window.robotReady === true', { timeout: 30000 }); - } catch (e) { - const error = await page.evaluate(() => window.robotError); - console.log(` ❌ Failed to load: ${error || 'timeout'}`); - await page.close(); - return null; - } - - // Wait a bit for rendering to settle - await new Promise(r => setTimeout(r, 500)); - - // Capture frames - console.log(` 📸 Capturing ${CONFIG.frameCount} frames...`); - const frames = []; - const anglePerFrame = (Math.PI * 2) / CONFIG.frameCount; - - for (let i = 0; i < CONFIG.frameCount; i++) { - const angle = i * anglePerFrame; - await page.evaluate((a) => window.setRotation(a), angle); - await new Promise(r => setTimeout(r, 16)); // Wait for render - - const screenshot = await page.screenshot({ - type: 'png', - omitBackground: false - }); - frames.push(screenshot); - - if ((i + 1) % 10 === 0) { - process.stdout.write(` Progress: ${i + 1}/${CONFIG.frameCount}\\r`); - } - } - console.log(' ✅ Frames captured'); - - await page.close(); - - // Save frames and create video - const outputDir = path.join(process.cwd(), CONFIG.outputDir, robot.id); - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - // Save individual frames - const framesDir = path.join(outputDir, 'frames'); - if (!fs.existsSync(framesDir)) { - fs.mkdirSync(framesDir, { recursive: true }); - } - - for (let i = 0; i < frames.length; i++) { - const framePath = path.join(framesDir, `frame_${String(i).padStart(3, '0')}.png`); - fs.writeFileSync(framePath, frames[i]); - } - - // Create WebM video using ffmpeg - const webmPath = path.join(outputDir, `${robot.outputName}.webm`); - const mp4Path = path.join(outputDir, `${robot.outputName}.mp4`); - const gifPath = path.join(outputDir, `${robot.outputName}.gif`); - - try { - console.log(' 🎬 Creating WebM video...'); - execSync(`ffmpeg -y -framerate ${CONFIG.fps} -i "${framesDir}/frame_%03d.png" -c:v libvpx-vp9 -pix_fmt yuva420p -b:v 1M "${webmPath}"`, { stdio: 'pipe' }); - - console.log(' 🎬 Creating MP4 video...'); - execSync(`ffmpeg -y -framerate ${CONFIG.fps} -i "${framesDir}/frame_%03d.png" -c:v libx264 -pix_fmt yuv420p -b:v 2M "${mp4Path}"`, { stdio: 'pipe' }); - - console.log(' 🎬 Creating GIF...'); - execSync(`ffmpeg -y -framerate ${CONFIG.fps} -i "${framesDir}/frame_%03d.png" -vf "fps=${CONFIG.fps},scale=${CONFIG.width}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" "${gifPath}"`, { stdio: 'pipe' }); - - // Also create a static thumbnail from frame 0 - const thumbnailPath = path.join(outputDir, 'thumbnail.png'); - fs.copyFileSync(path.join(framesDir, 'frame_000.png'), thumbnailPath); - - console.log(` ✅ Created: ${webmPath}`); - console.log(` ✅ Created: ${mp4Path}`); - console.log(` ✅ Created: ${gifPath}`); - - // Clean up frames - fs.rmSync(framesDir, { recursive: true }); - - return { - webm: `/previews/${robot.id}/${robot.outputName}.webm`, - mp4: `/previews/${robot.id}/${robot.outputName}.mp4`, - gif: `/previews/${robot.id}/${robot.outputName}.gif`, - thumbnail: `/previews/${robot.id}/thumbnail.png` - }; - - } catch (e) { - console.log(' ⚠️ ffmpeg not available, keeping PNG frames instead'); - return { - frames: `/previews/${robot.id}/frames/`, - thumbnail: `/previews/${robot.id}/frames/frame_000.png` - }; - } -} - -async function main() { - console.log('🤖 Robot Preview Generator'); - console.log('==========================\\n'); - console.log(`Output: ${CONFIG.width}x${CONFIG.height} @ ${CONFIG.fps}fps`); - console.log(`Frames: ${CONFIG.frameCount}`); - console.log(`Output directory: ${CONFIG.outputDir}\\n`); - - // Create output directory - const outputDir = path.join(process.cwd(), CONFIG.outputDir); - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - // Launch browser - console.log('🚀 Launching browser...'); - const browser = await puppeteer.launch({ - headless: 'new', - args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-web-security'] - }); - - const results = {}; - - for (const robot of ROBOTS) { - const result = await generatePreview(browser, robot); - if (result) { - results[robot.id] = result; - } - } - - await browser.close(); - - // Save manifest - const manifestPath = path.join(outputDir, 'manifest.json'); - fs.writeFileSync(manifestPath, JSON.stringify(results, null, 2)); - - console.log('\\n✅ All previews generated!'); - console.log(`📄 Manifest saved to: ${manifestPath}`); -} - -main().catch(console.error); diff --git a/scripts/generate_ai_prompt_templates.mjs b/scripts/generate_ai_prompt_templates.mjs index a3506aff9..325e46ed5 100644 --- a/scripts/generate_ai_prompt_templates.mjs +++ b/scripts/generate_ai_prompt_templates.mjs @@ -11,11 +11,29 @@ const outputPath = path.join( const source = fs.readFileSync(sourcePath, 'utf8'); -const REQUIRED_PROMPT_SECTIONS = ['generation', 'inspection.en', 'inspection.zh']; +const REQUIRED_PROMPT_SECTIONS = [ + 'generation', + 'inspection.en', + 'inspection.zh', + 'conversation.en', + 'conversation.zh', +]; const REQUIRED_SECTION_PLACEHOLDERS = { generation: ['__ROBOT_CONTEXT__', '__MOTOR_LIBRARY_CONTEXT__'], 'inspection.en': ['__CRITERIA_DESCRIPTION__', '__INSPECTION_NOTES__', '__LANGUAGE_INSTRUCTION__'], 'inspection.zh': ['__CRITERIA_DESCRIPTION__', '__INSPECTION_NOTES__', '__LANGUAGE_INSTRUCTION__'], + 'conversation.en': [ + '__CONVERSATION_MODE__', + '__CONVERSATION_CONTEXT__', + '__CONVERSATION_HISTORY__', + '__LANGUAGE_INSTRUCTION__', + ], + 'conversation.zh': [ + '__CONVERSATION_MODE__', + '__CONVERSATION_CONTEXT__', + '__CONVERSATION_HISTORY__', + '__LANGUAGE_INSTRUCTION__', + ], }; const sectionPattern = /\n([\s\S]*?)\n/g; @@ -60,6 +78,10 @@ export const AI_PROMPT_TEMPLATES = { inspection: { en: ${JSON.stringify(extractPrompt('inspection.en'))}, zh: ${JSON.stringify(extractPrompt('inspection.zh'))} + }, + conversation: { + en: ${JSON.stringify(extractPrompt('conversation.en'))}, + zh: ${JSON.stringify(extractPrompt('conversation.zh'))} } } as const; `; diff --git a/scripts/regression/build_menagerie_truth.py b/scripts/regression/build_menagerie_truth.py deleted file mode 100644 index 00050022c..000000000 --- a/scripts/regression/build_menagerie_truth.py +++ /dev/null @@ -1,260 +0,0 @@ -#!/usr/bin/env python3 -"""Build a MuJoCo menagerie truth manifest with per-body and per-joint facts.""" - -from __future__ import annotations - -import argparse -import json -import tempfile -from dataclasses import dataclass -from datetime import datetime, timezone -from pathlib import Path -from typing import Any - -import mujoco - - -DEFAULT_MENAGERIE_ROOT = Path(".tmp/regression/mujoco_menagerie") -DEFAULT_OUTPUT_PATH = Path(".tmp/regression/menagerie_truth.json") -SKIP_MODEL_DIRS = {"assets", "test"} -JOINT_TYPE_NAMES = { - int(mujoco.mjtJoint.mjJNT_FREE): "free", - int(mujoco.mjtJoint.mjJNT_BALL): "ball", - int(mujoco.mjtJoint.mjJNT_SLIDE): "slide", - int(mujoco.mjtJoint.mjJNT_HINGE): "hinge", -} - - -@dataclass(frozen=True) -class BuildPaths: - menagerie_root: Path - output_path: Path - - -def parse_args() -> BuildPaths: - parser = argparse.ArgumentParser( - description=( - "Compile top-level MJCF files from mujoco_menagerie and emit a " - "truth manifest for URDF Studio regression checks." - ) - ) - parser.add_argument( - "--menagerie-root", - type=Path, - default=DEFAULT_MENAGERIE_ROOT, - help="Path to the cloned mujoco_menagerie repository.", - ) - parser.add_argument( - "--output", - type=Path, - default=DEFAULT_OUTPUT_PATH, - help="Where to write the JSON manifest.", - ) - args = parser.parse_args() - return BuildPaths( - menagerie_root=args.menagerie_root.expanduser().resolve(), - output_path=args.output.expanduser().resolve(), - ) - - -def iter_model_dirs(menagerie_root: Path) -> list[Path]: - if not menagerie_root.is_dir(): - raise FileNotFoundError(f"Menagerie root does not exist: {menagerie_root}") - - model_dirs: list[Path] = [] - for child in sorted(menagerie_root.iterdir(), key=lambda path: path.name): - if not child.is_dir(): - continue - if child.name.startswith("."): - continue - if child.name in SKIP_MODEL_DIRS: - continue - model_dirs.append(child) - return model_dirs - - -def iter_top_level_xmls(model_dir: Path) -> list[Path]: - return sorted( - ( - path - for path in model_dir.iterdir() - if path.is_file() and path.suffix.lower() == ".xml" - ), - key=lambda path: path.name, - ) - - -def json_number_list(values: Any) -> list[float]: - return [float(value) for value in values] - - -def is_visual_geom(model: mujoco.MjModel, geom_id: int) -> bool: - group = int(model.geom_group[geom_id]) - contype = int(model.geom_contype[geom_id]) - conaffinity = int(model.geom_conaffinity[geom_id]) - return group in (1, 2) or (contype == 0 and conaffinity == 0) - - -def joint_qpos0(model: mujoco.MjModel, joint_id: int) -> list[float]: - qpos_start = int(model.jnt_qposadr[joint_id]) - if qpos_start < 0: - return [] - - qpos_end = int(model.nq) - for next_joint_id in range(joint_id + 1, int(model.njnt)): - next_qpos_start = int(model.jnt_qposadr[next_joint_id]) - if next_qpos_start > qpos_start: - qpos_end = next_qpos_start - break - return json_number_list(model.qpos0[qpos_start:qpos_end]) - - -def mj_name(model: mujoco.MjModel, obj_type: mujoco.mjtObj, obj_id: int, fallback: str) -> str: - name = mujoco.mj_id2name(model, obj_type, obj_id) - return name if name else fallback - - -def build_body_entries(model: mujoco.MjModel) -> list[dict[str, Any]]: - bodies: list[dict[str, Any]] = [] - - for body_id in range(int(model.nbody)): - geom_adr = int(model.body_geomadr[body_id]) - geom_num = int(model.body_geomnum[body_id]) - - visual_geom_count = 0 - collision_geom_count = 0 - if geom_adr >= 0 and geom_num > 0: - for geom_id in range(geom_adr, geom_adr + geom_num): - if is_visual_geom(model, geom_id): - visual_geom_count += 1 - else: - collision_geom_count += 1 - - bodies.append( - { - "name": mj_name(model, mujoco.mjtObj.mjOBJ_BODY, body_id, f"body_{body_id}"), - "mass": float(model.body_mass[body_id]), - "ipos": json_number_list(model.body_ipos[body_id]), - "inertia": json_number_list(model.body_inertia[body_id]), - "geom_count": geom_num, - "visual_geom_count": visual_geom_count, - "collision_geom_count": collision_geom_count, - } - ) - - return bodies - - -def build_joint_entries(model: mujoco.MjModel) -> list[dict[str, Any]]: - joints: list[dict[str, Any]] = [] - - for joint_id in range(int(model.njnt)): - joint_type = int(model.jnt_type[joint_id]) - joints.append( - { - "name": mj_name( - model, - mujoco.mjtObj.mjOBJ_JOINT, - joint_id, - f"joint_{joint_id}", - ), - "type": JOINT_TYPE_NAMES.get(joint_type, f"unknown_{joint_type}"), - "range": json_number_list(model.jnt_range[joint_id]), - "axis": json_number_list(model.jnt_axis[joint_id]), - "qpos0": joint_qpos0(model, joint_id), - } - ) - - return joints - - -def compile_xml(xml_path: Path, menagerie_root: Path) -> dict[str, Any]: - entry: dict[str, Any] = { - "model_dir": xml_path.parent.name, - "xml_file": xml_path.name, - "xml_relpath": str(xml_path.relative_to(menagerie_root)), - "compile_ok": False, - "error": None, - "body_count": 0, - "joint_count": 0, - "geom_count": 0, - "total_mass": 0.0, - "per_body": [], - "per_joint": [], - } - - try: - model = mujoco.MjModel.from_xml_path(str(xml_path)) - except Exception as exc: - entry["error"] = f"{type(exc).__name__}: {exc}" - return entry - - per_body = build_body_entries(model) - per_joint = build_joint_entries(model) - - entry.update( - { - "compile_ok": True, - "body_count": int(model.nbody), - "joint_count": int(model.njnt), - "geom_count": int(model.ngeom), - "total_mass": float(sum(body["mass"] for body in per_body)), - "per_body": per_body, - "per_joint": per_joint, - } - ) - return entry - - -def build_manifest(paths: BuildPaths) -> dict[str, Any]: - model_dirs = iter_model_dirs(paths.menagerie_root) - entries: list[dict[str, Any]] = [] - - for model_dir in model_dirs: - for xml_path in iter_top_level_xmls(model_dir): - entries.append(compile_xml(xml_path.resolve(), paths.menagerie_root)) - - return { - "generated_at": datetime.now(timezone.utc).isoformat(), - "generator": "scripts/regression/build_menagerie_truth.py", - "menagerie_root": str(paths.menagerie_root), - "model_dir_count": len(model_dirs), - "entry_count": len(entries), - "compile_ok_count": sum(1 for entry in entries if entry["compile_ok"]), - "compile_error_count": sum(1 for entry in entries if not entry["compile_ok"]), - "entries": entries, - } - - -def write_manifest(output_path: Path, manifest: dict[str, Any]) -> None: - output_path.parent.mkdir(parents=True, exist_ok=True) - with tempfile.NamedTemporaryFile( - "w", - encoding="utf-8", - dir=output_path.parent, - delete=False, - prefix=f"{output_path.name}.", - suffix=".tmp", - ) as handle: - json.dump(manifest, handle, indent=2, ensure_ascii=False) - handle.write("\n") - temp_path = Path(handle.name) - temp_path.replace(output_path) - - -def main() -> int: - paths = parse_args() - manifest = build_manifest(paths) - write_manifest(paths.output_path, manifest) - print( - "Wrote " - f"{manifest['entry_count']} entries " - f"({manifest['compile_ok_count']} compiled, " - f"{manifest['compile_error_count']} failed) " - f"to {paths.output_path}" - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/regression/run_menagerie_browser_regression.mjs b/scripts/regression/run_menagerie_browser_regression.mjs deleted file mode 100644 index d2b91e982..000000000 --- a/scripts/regression/run_menagerie_browser_regression.mjs +++ /dev/null @@ -1,1355 +0,0 @@ -#!/usr/bin/env node - -import fs from "node:fs/promises"; -import path from "node:path"; -import process from "node:process"; -import { spawn } from "node:child_process"; -import { setTimeout as delay } from "node:timers/promises"; -import puppeteer from "puppeteer"; - -const DEFAULT_REGRESSION_ROOT = path.resolve(".tmp/regression"); -const DEFAULT_TRUTH_PATH = path.join(DEFAULT_REGRESSION_ROOT, "menagerie_truth.json"); -const DEFAULT_OUTPUT_PATH = path.join( - DEFAULT_REGRESSION_ROOT, - "browser_regression_results.json", -); -const DEFAULT_SITE_URL = "http://127.0.0.1:4173"; -const DEFAULT_SITE_TIMEOUT_MS = 120_000; -const DEFAULT_OPERATION_TIMEOUT_MS = 120_000; -const DEFAULT_FLOAT_TOLERANCE = 1e-5; -const DEFAULT_START_COMMAND = (host, port) => - `npm run dev -- --host ${host} --port ${port}`; -const DEFAULT_EXECUTABLE_CANDIDATES = [ - process.env.CHROME_PATH, - "/usr/bin/google-chrome", - "/usr/bin/google-chrome-stable", - "/usr/bin/chromium-browser", - "/usr/bin/chromium", -].filter(Boolean); - -const HELP_TEXT = `Usage: - node scripts/regression/run_menagerie_browser_regression.mjs [options] - -Options: - --site-url URDF Studio site URL. Default: ${DEFAULT_SITE_URL} - --truth Truth manifest path. Default: ${DEFAULT_TRUTH_PATH} - --output Result JSON path. Default: ${DEFAULT_OUTPUT_PATH} - --regression-root Regression workspace root. Default: ${DEFAULT_REGRESSION_ROOT} - --model-dir Restrict run to one model_dir. Repeatable. - --site-timeout-ms Site startup/connect timeout. Default: ${DEFAULT_SITE_TIMEOUT_MS} - --timeout-ms Browser/debug operation timeout. Default: ${DEFAULT_OPERATION_TIMEOUT_MS} - --float-tol Numeric diff tolerance. Default: ${DEFAULT_FLOAT_TOLERANCE} - --start-command Override auto-start command when site is offline. - --no-start Fail instead of starting the site automatically. - --chrome-path Browser executable path. - --headed Launch headed browser instead of headless. - --help Show this help. - -Expected debug API on window.__URDF_STUDIO_DEBUG__: - - An async load method matching one of: - loadImportedXml, loadImportedXmlFile, loadXmlFile, loadXml, loadMjcfXml - - An async snapshot method matching one of: - captureRegressionSnapshot, captureSnapshot, getSnapshot, snapshot - - Optional ping/health method: - ping, healthCheck, healthcheck, ready -`; - -function fail(message) { - throw new Error(message); -} - -function parseInteger(value, flagName) { - const parsed = Number.parseInt(value, 10); - if (!Number.isFinite(parsed) || parsed < 0) { - fail(`Invalid value for ${flagName}: ${value}`); - } - return parsed; -} - -function parseFloatValue(value, flagName) { - const parsed = Number.parseFloat(value); - if (!Number.isFinite(parsed) || parsed < 0) { - fail(`Invalid value for ${flagName}: ${value}`); - } - return parsed; -} - -function parseArgs(argv) { - const options = { - siteUrl: DEFAULT_SITE_URL, - truthPath: DEFAULT_TRUTH_PATH, - outputPath: DEFAULT_OUTPUT_PATH, - regressionRoot: DEFAULT_REGRESSION_ROOT, - modelDirs: [], - siteTimeoutMs: DEFAULT_SITE_TIMEOUT_MS, - timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS, - floatTolerance: DEFAULT_FLOAT_TOLERANCE, - noStart: false, - startCommand: null, - chromePath: null, - headed: false, - }; - - for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; - - const nextValue = () => { - const value = argv[index + 1]; - if (value == null) { - fail(`Missing value for ${arg}`); - } - index += 1; - return value; - }; - - switch (arg) { - case "--site-url": - options.siteUrl = nextValue(); - break; - case "--truth": - options.truthPath = path.resolve(nextValue()); - break; - case "--output": - options.outputPath = path.resolve(nextValue()); - break; - case "--regression-root": - options.regressionRoot = path.resolve(nextValue()); - break; - case "--model-dir": - options.modelDirs.push(nextValue()); - break; - case "--site-timeout-ms": - options.siteTimeoutMs = parseInteger(nextValue(), "--site-timeout-ms"); - break; - case "--timeout-ms": - options.timeoutMs = parseInteger(nextValue(), "--timeout-ms"); - break; - case "--float-tol": - options.floatTolerance = parseFloatValue(nextValue(), "--float-tol"); - break; - case "--start-command": - options.startCommand = nextValue(); - break; - case "--no-start": - options.noStart = true; - break; - case "--chrome-path": - options.chromePath = path.resolve(nextValue()); - break; - case "--headed": - options.headed = true; - break; - case "--help": - case "-h": - console.log(HELP_TEXT); - process.exit(0); - break; - default: - fail(`Unknown argument: ${arg}`); - } - } - - options.siteUrl = new URL(options.siteUrl).toString(); - options.truthPath = path.resolve(options.truthPath); - options.outputPath = path.resolve(options.outputPath); - options.regressionRoot = path.resolve(options.regressionRoot); - options.modelDirs = [...new Set(options.modelDirs)]; - return options; -} - -async function readJson(filePath) { - const raw = await fs.readFile(filePath, "utf8"); - return JSON.parse(raw); -} - -async function writeJsonAtomic(filePath, value) { - await fs.mkdir(path.dirname(filePath), { recursive: true }); - const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`; - await fs.writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, "utf8"); - await fs.rename(tempPath, filePath); -} - -function assertTruthManifest(manifest) { - if (!manifest || typeof manifest !== "object") { - fail("Truth manifest must be a JSON object."); - } - if (!Array.isArray(manifest.entries)) { - fail("Truth manifest must contain an entries array."); - } -} - -function groupTruthEntries(manifest, selectedModelDirs) { - const selected = new Set(selectedModelDirs); - const grouped = new Map(); - - for (const entry of manifest.entries) { - if (!entry || typeof entry !== "object") { - continue; - } - - const modelDir = entry.model_dir; - const xmlFile = entry.xml_file; - if (typeof modelDir !== "string" || typeof xmlFile !== "string") { - continue; - } - - if (selected.size > 0 && !selected.has(modelDir)) { - continue; - } - - const bucket = grouped.get(modelDir) ?? []; - bucket.push(entry); - grouped.set(modelDir, bucket); - } - - for (const entries of grouped.values()) { - entries.sort((left, right) => String(left.xml_file).localeCompare(String(right.xml_file))); - } - - return grouped; -} - -async function listZipCandidates(rootDir) { - const results = []; - - async function visit(currentPath) { - const dirents = await fs.readdir(currentPath, { withFileTypes: true }); - for (const dirent of dirents) { - if (dirent.name === ".git" || dirent.name === "node_modules") { - continue; - } - - const fullPath = path.join(currentPath, dirent.name); - if (dirent.isDirectory()) { - await visit(fullPath); - continue; - } - - if (dirent.isFile() && dirent.name.toLowerCase().endsWith(".zip")) { - results.push(fullPath); - } - } - } - - try { - await visit(rootDir); - } catch (error) { - if (error?.code === "ENOENT") { - return []; - } - throw error; - } - - return results.sort(); -} - -function getExplicitZipPaths(modelEntries, regressionRoot) { - const explicitKeys = [ - "zip_path", - "zipPath", - "archive_path", - "archivePath", - "zip_file", - "zipFile", - ]; - const results = []; - - for (const entry of modelEntries) { - for (const key of explicitKeys) { - const candidate = entry?.[key]; - if (typeof candidate !== "string" || candidate.trim() === "") { - continue; - } - - if (path.isAbsolute(candidate)) { - results.push(path.normalize(candidate)); - } else { - results.push(path.resolve(regressionRoot, candidate)); - } - } - } - - return [...new Set(results)]; -} - -async function fileExists(filePath) { - try { - await fs.access(filePath); - return true; - } catch { - return false; - } -} - -function scoreZipCandidate(modelDir, candidatePath) { - const normalizedModelDir = modelDir.toLowerCase(); - const basename = path.basename(candidatePath, ".zip").toLowerCase(); - const parentName = path.basename(path.dirname(candidatePath)).toLowerCase(); - const fullPath = candidatePath.toLowerCase(); - - let score = 0; - if (basename === normalizedModelDir) score += 100; - if (parentName === normalizedModelDir) score += 80; - if (basename.includes(normalizedModelDir)) score += 40; - if (fullPath.includes(`/${normalizedModelDir}/`) || fullPath.includes(`\\${normalizedModelDir}\\`)) { - score += 20; - } - if (fullPath.includes(normalizedModelDir)) score += 10; - return score; -} - -async function resolveZipPath(modelDir, modelEntries, regressionRoot, zipCandidates) { - const explicitPaths = getExplicitZipPaths(modelEntries, regressionRoot); - for (const candidate of explicitPaths) { - if (await fileExists(candidate)) { - return candidate; - } - } - - const preferredPaths = [ - path.join(regressionRoot, `${modelDir}.zip`), - path.join(regressionRoot, "zips", `${modelDir}.zip`), - path.join(regressionRoot, "packages", `${modelDir}.zip`), - path.join(regressionRoot, modelDir, `${modelDir}.zip`), - path.join(regressionRoot, modelDir, "archive.zip"), - path.join(regressionRoot, modelDir, "model.zip"), - ]; - - for (const candidate of preferredPaths) { - if (await fileExists(candidate)) { - return candidate; - } - } - - const scored = zipCandidates - .map((candidatePath) => ({ - candidatePath, - score: scoreZipCandidate(modelDir, candidatePath), - })) - .filter((entry) => entry.score > 0) - .sort((left, right) => right.score - left.score || left.candidatePath.localeCompare(right.candidatePath)); - - if (scored.length === 0) { - fail(`Could not find a zip package for model_dir=${modelDir}`); - } - - if (scored.length > 1 && scored[0].score === scored[1].score) { - const ambiguous = scored.slice(0, 5).map((entry) => entry.candidatePath).join(", "); - fail( - `Multiple zip candidates matched model_dir=${modelDir} with the same score. ` + - `Pass an explicit zip path in the truth manifest. Candidates: ${ambiguous}`, - ); - } - - return scored[0].candidatePath; -} - -async function fetchWithTimeout(url, timeoutMs) { - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), timeoutMs); - try { - return await fetch(url, { - signal: controller.signal, - redirect: "follow", - cache: "no-store", - }); - } finally { - clearTimeout(timeoutId); - } -} - -async function isSiteReachable(siteUrl, timeoutMs) { - try { - const response = await fetchWithTimeout(siteUrl, Math.min(timeoutMs, 10_000)); - return response.ok; - } catch { - return false; - } -} - -function createLogBuffer(limit = 200) { - const lines = []; - return { - push(line) { - if (typeof line !== "string" || line.length === 0) { - return; - } - lines.push(line); - if (lines.length > limit) { - lines.splice(0, lines.length - limit); - } - }, - toString() { - return lines.join("\n"); - }, - }; -} - -function spawnSiteProcess(command, cwd) { - const logs = createLogBuffer(); - const child = spawn(command, { - cwd, - shell: true, - detached: true, - stdio: ["ignore", "pipe", "pipe"], - env: { - ...process.env, - BROWSER: "none", - }, - }); - - child.stdout?.setEncoding("utf8"); - child.stderr?.setEncoding("utf8"); - child.stdout?.on("data", (chunk) => logs.push(String(chunk).trimEnd())); - child.stderr?.on("data", (chunk) => logs.push(String(chunk).trimEnd())); - - return { - child, - logs, - async stop() { - if (child.exitCode != null || child.signalCode != null) { - return; - } - - try { - process.kill(-child.pid, "SIGTERM"); - } catch { - return; - } - - const deadline = Date.now() + 5_000; - while (Date.now() < deadline) { - if (child.exitCode != null || child.signalCode != null) { - return; - } - await delay(100); - } - - try { - process.kill(-child.pid, "SIGKILL"); - } catch { - // ignore - } - }, - }; -} - -async function ensureSite(siteUrl, options) { - if (await isSiteReachable(siteUrl, options.siteTimeoutMs)) { - return { - startedByScript: false, - siteUrl, - stop: async () => {}, - }; - } - - if (options.noStart) { - fail(`Site is not reachable at ${siteUrl} and --no-start was set.`); - } - - const parsedUrl = new URL(siteUrl); - const host = parsedUrl.hostname; - const port = parsedUrl.port || (parsedUrl.protocol === "https:" ? "443" : "80"); - const command = options.startCommand ?? DEFAULT_START_COMMAND(host, port); - const siteProcess = spawnSiteProcess(command, process.cwd()); - const deadline = Date.now() + options.siteTimeoutMs; - - try { - while (Date.now() < deadline) { - if (await isSiteReachable(siteUrl, 5_000)) { - return { - startedByScript: true, - siteUrl, - stop: siteProcess.stop, - }; - } - - if (siteProcess.child.exitCode != null) { - fail( - `Site start command exited early: ${command}\n` + - `Last logs:\n${siteProcess.logs.toString() || "(no logs captured)"}`, - ); - } - - await delay(500); - } - - fail( - `Timed out waiting for site ${siteUrl} after starting: ${command}\n` + - `Last logs:\n${siteProcess.logs.toString() || "(no logs captured)"}`, - ); - } catch (error) { - await siteProcess.stop(); - throw error; - } -} - -async function resolveChromeExecutable(chromePath) { - if (chromePath) { - return chromePath; - } - - for (const candidate of DEFAULT_EXECUTABLE_CANDIDATES) { - if (await fileExists(candidate)) { - return candidate; - } - } - - return null; -} - -async function launchBrowser(options) { - const executablePath = await resolveChromeExecutable(options.chromePath); - const browser = await puppeteer.launch({ - headless: options.headed ? false : true, - executablePath: executablePath ?? undefined, - args: ["--no-sandbox", "--disable-dev-shm-usage"], - defaultViewport: { - width: 1600, - height: 1000, - deviceScaleFactor: 1, - }, - }); - - return browser; -} - -function ringBuffer(limit = 100) { - const values = []; - return { - push(value) { - values.push(value); - if (values.length > limit) { - values.splice(0, values.length - limit); - } - }, - snapshot() { - return [...values]; - }, - }; -} - -async function createRegressionPage(browser, siteUrl, timeoutMs) { - const page = await browser.newPage(); - const consoleMessages = ringBuffer(100); - const pageErrors = ringBuffer(50); - - page.setDefaultTimeout(timeoutMs); - page.setDefaultNavigationTimeout(timeoutMs); - page.on("console", (message) => { - const text = message.text(); - consoleMessages.push(`[${message.type()}] ${text}`); - }); - page.on("pageerror", (error) => { - pageErrors.push(String(error?.stack || error?.message || error)); - }); - - await page.goto(siteUrl, { - waitUntil: "domcontentloaded", - timeout: timeoutMs, - }); - await waitForDebugApi(page, timeoutMs); - - return { page, consoleMessages, pageErrors }; -} - -async function waitForDebugApi(page, timeoutMs) { - await page.waitForFunction(() => { - return Boolean(globalThis.window && window.__URDF_STUDIO_DEBUG__); - }, { timeout: timeoutMs }); - - try { - await page.evaluate(async () => { - const api = window.__URDF_STUDIO_DEBUG__; - const candidateNames = ["ping", "healthCheck", "healthcheck", "ready"]; - for (const name of candidateNames) { - if (typeof api?.[name] === "function") { - await api[name](); - return; - } - } - }); - } catch { - // ping is optional - } -} - -async function findImportInput(page, timeoutMs) { - await page.waitForSelector('input[type="file"]', { timeout: timeoutMs }); - const handles = await page.$$('input[type="file"]'); - if (handles.length === 0) { - fail("Could not find a file input on the page."); - } - - let bestHandle = handles[0]; - let bestScore = -1; - - for (const handle of handles) { - const score = await handle.evaluate((element) => { - if (!(element instanceof HTMLInputElement) || element.type !== "file") { - return -1; - } - - const accept = (element.accept || "").toLowerCase(); - let value = 0; - if (accept.includes(".zip") || accept.includes("zip")) value += 100; - if (element.multiple) value += 1; - return value; - }); - - if (score > bestScore) { - bestScore = score; - bestHandle = handle; - } - } - - return bestHandle; -} - -async function importZipIntoPage(page, zipPath, timeoutMs) { - const inputHandle = await findImportInput(page, timeoutMs); - await inputHandle.uploadFile(zipPath); - await delay(100); - await waitForDebugApi(page, timeoutMs); -} - -async function callDebugMethod(page, candidateNames, payload) { - const result = await page.evaluate( - async ({ names, methodPayload }) => { - const api = window.__URDF_STUDIO_DEBUG__; - if (!api || typeof api !== "object") { - throw new Error("window.__URDF_STUDIO_DEBUG__ is not available"); - } - - for (const name of names) { - if (typeof api[name] === "function") { - return await api[name](methodPayload); - } - } - - throw new Error( - `No debug method found. Tried: ${names.join(", ")}`, - ); - }, - { - names: candidateNames, - methodPayload: payload, - }, - ); - - return result ?? null; -} - -async function loadXmlWithDebug(page, modelDir, xmlFile, timeoutMs) { - return await callDebugMethod( - page, - [ - "loadImportedXml", - "loadImportedXmlFile", - "loadXmlFile", - "loadXml", - "loadMjcfXml", - ], - { - modelDir, - xmlFile, - timeoutMs, - }, - ); -} - -async function snapshotWithDebug(page, modelDir, xmlFile, timeoutMs, fallbackSnapshot) { - try { - return await callDebugMethod( - page, - [ - "captureRegressionSnapshot", - "captureSnapshot", - "getSnapshot", - "snapshot", - ], - { - modelDir, - xmlFile, - timeoutMs, - }, - ); - } catch (error) { - if (fallbackSnapshot && typeof fallbackSnapshot === "object") { - return fallbackSnapshot; - } - throw error; - } -} - -function toNumber(value) { - if (typeof value === "number" && Number.isFinite(value)) { - return value; - } - if (typeof value === "string" && value.trim() !== "") { - const parsed = Number.parseFloat(value); - if (Number.isFinite(parsed)) { - return parsed; - } - } - return null; -} - -function normalizeNumberArray(values) { - if (!Array.isArray(values)) { - return null; - } - - const result = []; - for (const value of values) { - const parsed = toNumber(value); - if (parsed == null) { - return null; - } - result.push(parsed); - } - - return result; -} - -function normalizeBodyEntry(body) { - if (!body || typeof body !== "object") { - return null; - } - - const name = typeof body.name === "string" ? body.name : null; - if (!name) { - return null; - } - - return { - name, - mass: - toNumber(body.mass) ?? - toNumber(body.body_mass) ?? - toNumber(body.bodyMass), - ipos: - normalizeNumberArray(body.ipos) ?? - normalizeNumberArray(body.center_of_mass) ?? - normalizeNumberArray(body.centerOfMass), - inertia: - normalizeNumberArray(body.inertia) ?? - normalizeNumberArray(body.inertial) ?? - normalizeNumberArray(body.body_inertia) ?? - normalizeNumberArray(body.bodyInertia), - geom_count: - toNumber(body.geom_count) ?? - toNumber(body.geomCount), - visual_geom_count: - toNumber(body.visual_geom_count) ?? - toNumber(body.visualGeomCount) ?? - toNumber(body.visual_count) ?? - toNumber(body.visualCount), - collision_geom_count: - toNumber(body.collision_geom_count) ?? - toNumber(body.collisionGeomCount) ?? - toNumber(body.collision_count) ?? - toNumber(body.collisionCount), - }; -} - -function normalizeJointEntry(joint) { - if (!joint || typeof joint !== "object") { - return null; - } - - const name = typeof joint.name === "string" ? joint.name : null; - if (!name) { - return null; - } - - return { - name, - type: typeof joint.type === "string" ? joint.type : null, - range: normalizeNumberArray(joint.range), - axis: normalizeNumberArray(joint.axis), - qpos0: - normalizeNumberArray(joint.qpos0) ?? - normalizeNumberArray(joint.initial_qpos) ?? - normalizeNumberArray(joint.initialQpos), - }; -} - -function normalizeSnapshot(snapshot) { - const rawBodies = Array.isArray(snapshot?.per_body) - ? snapshot.per_body - : Array.isArray(snapshot?.perBody) - ? snapshot.perBody - : Array.isArray(snapshot?.bodies) - ? snapshot.bodies - : []; - - const rawJoints = Array.isArray(snapshot?.per_joint) - ? snapshot.per_joint - : Array.isArray(snapshot?.perJoint) - ? snapshot.perJoint - : Array.isArray(snapshot?.joints) - ? snapshot.joints - : []; - - const perBody = rawBodies.map(normalizeBodyEntry).filter(Boolean); - const perJoint = rawJoints.map(normalizeJointEntry).filter(Boolean); - - return { - body_count: - toNumber(snapshot?.body_count) ?? - toNumber(snapshot?.bodyCount) ?? - perBody.length, - joint_count: - toNumber(snapshot?.joint_count) ?? - toNumber(snapshot?.jointCount) ?? - perJoint.length, - geom_count: - toNumber(snapshot?.geom_count) ?? - toNumber(snapshot?.geomCount) ?? - perBody.reduce((sum, body) => sum + (body.geom_count ?? 0), 0), - total_mass: - toNumber(snapshot?.total_mass) ?? - toNumber(snapshot?.totalMass) ?? - perBody.reduce((sum, body) => sum + (body.mass ?? 0), 0), - per_body: perBody, - per_joint: perJoint, - }; -} - -function buildTruthSummary(entry) { - const perBody = Array.isArray(entry.per_body) ? entry.per_body.map(normalizeBodyEntry).filter(Boolean) : []; - const perJoint = Array.isArray(entry.per_joint) ? entry.per_joint.map(normalizeJointEntry).filter(Boolean) : []; - const visualGeomCount = perBody.reduce( - (sum, body) => sum + (body.visual_geom_count ?? 0), - 0, - ); - const collisionGeomCount = perBody.reduce( - (sum, body) => sum + (body.collision_geom_count ?? 0), - 0, - ); - const inertiaTraceTotal = perBody.reduce((sum, body) => { - if (!Array.isArray(body.inertia)) { - return sum; - } - return sum + body.inertia.reduce((inner, value) => inner + value, 0); - }, 0); - - return { - body_count: toNumber(entry.body_count) ?? perBody.length, - joint_count: toNumber(entry.joint_count) ?? perJoint.length, - geom_count: - toNumber(entry.geom_count) ?? - perBody.reduce((sum, body) => sum + (body.geom_count ?? 0), 0), - total_mass: - toNumber(entry.total_mass) ?? - perBody.reduce((sum, body) => sum + (body.mass ?? 0), 0), - visual_geom_count_total: visualGeomCount, - collision_geom_count_total: collisionGeomCount, - inertia_trace_total: inertiaTraceTotal, - per_body: perBody, - per_joint: perJoint, - body_names: perBody.map((body) => body.name).sort(), - joint_names: perJoint.map((joint) => joint.name).sort(), - }; -} - -function buildSnapshotSummary(snapshot) { - const normalized = normalizeSnapshot(snapshot); - const visualGeomCount = normalized.per_body.reduce( - (sum, body) => sum + (body.visual_geom_count ?? 0), - 0, - ); - const collisionGeomCount = normalized.per_body.reduce( - (sum, body) => sum + (body.collision_geom_count ?? 0), - 0, - ); - const inertiaTraceTotal = normalized.per_body.reduce((sum, body) => { - if (!Array.isArray(body.inertia)) { - return sum; - } - return sum + body.inertia.reduce((inner, value) => inner + value, 0); - }, 0); - - return { - body_count: normalized.body_count, - joint_count: normalized.joint_count, - geom_count: normalized.geom_count, - total_mass: normalized.total_mass, - visual_geom_count_total: visualGeomCount, - collision_geom_count_total: collisionGeomCount, - inertia_trace_total: inertiaTraceTotal, - per_body: normalized.per_body, - per_joint: normalized.per_joint, - body_names: normalized.per_body.map((body) => body.name).sort(), - joint_names: normalized.per_joint.map((joint) => joint.name).sort(), - }; -} - -function numbersClose(left, right, tolerance) { - return Math.abs(left - right) <= tolerance; -} - -function arraysClose(left, right, tolerance) { - if (!Array.isArray(left) && !Array.isArray(right)) { - return true; - } - if (!Array.isArray(left) || !Array.isArray(right)) { - return false; - } - if (left.length !== right.length) { - return false; - } - for (let index = 0; index < left.length; index += 1) { - if (!numbersClose(left[index], right[index], tolerance)) { - return false; - } - } - return true; -} - -function compareScalar(diffFields, field, truthValue, snapshotValue, tolerance) { - if (truthValue == null && snapshotValue == null) { - return; - } - - if (typeof truthValue === "number" || typeof snapshotValue === "number") { - if (truthValue == null || snapshotValue == null) { - diffFields.push({ - field, - truth: truthValue, - snapshot: snapshotValue, - }); - return; - } - - if (!numbersClose(truthValue, snapshotValue, tolerance)) { - diffFields.push({ - field, - truth: truthValue, - snapshot: snapshotValue, - delta: snapshotValue - truthValue, - }); - } - return; - } - - if (Array.isArray(truthValue) || Array.isArray(snapshotValue)) { - if (!arraysClose(truthValue, snapshotValue, tolerance)) { - diffFields.push({ - field, - truth: truthValue, - snapshot: snapshotValue, - }); - } - return; - } - - if (truthValue !== snapshotValue) { - diffFields.push({ - field, - truth: truthValue, - snapshot: snapshotValue, - }); - } -} - -function mapByName(entries) { - const mapped = new Map(); - for (const entry of entries) { - if (entry?.name) { - mapped.set(entry.name, entry); - } - } - return mapped; -} - -function compareNamedEntries(diffFields, prefix, truthEntries, snapshotEntries, tolerance) { - const truthMap = mapByName(truthEntries); - const snapshotMap = mapByName(snapshotEntries); - - for (const name of truthMap.keys()) { - if (!snapshotMap.has(name)) { - diffFields.push({ - field: `${prefix}.${name}`, - truth: truthMap.get(name), - snapshot: null, - reason: "missing_in_snapshot", - }); - } - } - - for (const name of snapshotMap.keys()) { - if (!truthMap.has(name)) { - diffFields.push({ - field: `${prefix}.${name}`, - truth: null, - snapshot: snapshotMap.get(name), - reason: "extra_in_snapshot", - }); - } - } - - for (const [name, truthEntry] of truthMap.entries()) { - const snapshotEntry = snapshotMap.get(name); - if (!snapshotEntry) { - continue; - } - - for (const key of Object.keys(truthEntry)) { - if (key === "name") { - continue; - } - compareScalar( - diffFields, - `${prefix}.${name}.${key}`, - truthEntry[key], - snapshotEntry[key], - tolerance, - ); - } - } -} - -function computeDiffFields(truthSummary, snapshotSummary, tolerance) { - const diffFields = []; - - compareScalar(diffFields, "body_count", truthSummary.body_count, snapshotSummary.body_count, tolerance); - compareScalar(diffFields, "joint_count", truthSummary.joint_count, snapshotSummary.joint_count, tolerance); - compareScalar(diffFields, "geom_count", truthSummary.geom_count, snapshotSummary.geom_count, tolerance); - compareScalar(diffFields, "total_mass", truthSummary.total_mass, snapshotSummary.total_mass, tolerance); - compareScalar( - diffFields, - "visual_geom_count_total", - truthSummary.visual_geom_count_total, - snapshotSummary.visual_geom_count_total, - tolerance, - ); - compareScalar( - diffFields, - "collision_geom_count_total", - truthSummary.collision_geom_count_total, - snapshotSummary.collision_geom_count_total, - tolerance, - ); - compareScalar( - diffFields, - "inertia_trace_total", - truthSummary.inertia_trace_total, - snapshotSummary.inertia_trace_total, - tolerance, - ); - compareScalar( - diffFields, - "body_names", - truthSummary.body_names, - snapshotSummary.body_names, - tolerance, - ); - compareScalar( - diffFields, - "joint_names", - truthSummary.joint_names, - snapshotSummary.joint_names, - tolerance, - ); - - compareNamedEntries( - diffFields, - "per_body", - truthSummary.per_body, - snapshotSummary.per_body, - tolerance, - ); - compareNamedEntries( - diffFields, - "per_joint", - truthSummary.per_joint, - snapshotSummary.per_joint, - tolerance, - ); - - return diffFields; -} - -function buildModelErrorResults(modelDir, entries, zipPath, error) { - return { - model_dir: modelDir, - zip_path: zipPath, - import_ok: false, - import_error: error, - xml_results: entries.map((entry) => { - const compileError = - !entry.compile_ok && entry.error - ? `compile_ok=false in truth manifest: ${entry.error}` - : null; - - return { - model_dir: modelDir, - xml_file: entry.xml_file, - load_ok: false, - error: compileError ?? error, - snapshot_summary: null, - truth_summary: buildTruthSummary(entry), - diff_fields: [ - { - field: entry.compile_ok ? "model_import" : "compile_ok", - truth: entry.compile_ok ? "imported" : false, - snapshot: entry.compile_ok ? "failed" : null, - reason: compileError ?? error, - }, - ], - }; - }), - }; -} - -async function runModelRegression(browser, siteUrl, modelDir, entries, zipPath, options) { - console.log(`\n[${modelDir}] importing ${path.relative(process.cwd(), zipPath)}`); - const compileOkEntries = entries.filter((entry) => entry.compile_ok); - const skippedEntries = entries.filter((entry) => !entry.compile_ok); - const { page, consoleMessages, pageErrors } = await createRegressionPage( - browser, - siteUrl, - options.timeoutMs, - ); - - try { - try { - await importZipIntoPage(page, zipPath, options.timeoutMs); - } catch (error) { - const diagnostics = [ - `Import failed for model_dir=${modelDir}: ${String(error?.stack || error?.message || error)}`, - ...pageErrors.snapshot(), - ...consoleMessages.snapshot(), - ] - .filter(Boolean) - .join("\n"); - return buildModelErrorResults(modelDir, entries, zipPath, diagnostics); - } - - const xmlResults = []; - - for (const entry of compileOkEntries) { - const truthSummary = buildTruthSummary(entry); - let loadOk = false; - let errorMessage = null; - let snapshotSummary = null; - let diffFields = []; - - console.log(`[${modelDir}] xml=${entry.xml_file}`); - - try { - const loadResult = await loadXmlWithDebug( - page, - modelDir, - entry.xml_file, - options.timeoutMs, - ); - const snapshot = await snapshotWithDebug( - page, - modelDir, - entry.xml_file, - options.timeoutMs, - loadResult?.snapshot ?? null, - ); - snapshotSummary = buildSnapshotSummary(snapshot); - diffFields = computeDiffFields( - truthSummary, - snapshotSummary, - options.floatTolerance, - ); - loadOk = true; - } catch (error) { - errorMessage = String(error?.stack || error?.message || error); - } - - if (!loadOk) { - const diagnostics = [...pageErrors.snapshot(), ...consoleMessages.snapshot()] - .filter(Boolean) - .slice(-20); - if (diagnostics.length > 0) { - errorMessage = `${errorMessage}\n${diagnostics.join("\n")}`; - } - } - - xmlResults.push({ - model_dir: modelDir, - xml_file: entry.xml_file, - xml_relpath: entry.xml_relpath ?? null, - load_ok: loadOk, - error: errorMessage, - snapshot_summary: snapshotSummary, - truth_summary: truthSummary, - diff_fields: diffFields, - }); - } - - for (const entry of skippedEntries) { - xmlResults.push({ - model_dir: modelDir, - xml_file: entry.xml_file, - xml_relpath: entry.xml_relpath ?? null, - load_ok: false, - error: `compile_ok=false in truth manifest: ${entry.error ?? "unknown compile error"}`, - snapshot_summary: null, - truth_summary: buildTruthSummary(entry), - diff_fields: [ - { - field: "compile_ok", - truth: false, - snapshot: null, - reason: entry.error ?? "compile_ok=false", - }, - ], - }); - } - - return { - model_dir: modelDir, - zip_path: zipPath, - import_ok: true, - import_error: null, - xml_results: xmlResults, - }; - } finally { - try { - await page.close(); - } catch { - // ignore - } - } -} - -function summarizeRun(results) { - const modelCount = results.length; - const xmlResults = results.flatMap((result) => result.xml_results); - const loadOkCount = xmlResults.filter((result) => result.load_ok).length; - const loadErrorCount = xmlResults.length - loadOkCount; - const diffCount = xmlResults.reduce( - (sum, result) => sum + (Array.isArray(result.diff_fields) ? result.diff_fields.length : 0), - 0, - ); - const importErrorCount = results.filter((result) => !result.import_ok).length; - - return { - model_count: modelCount, - xml_count: xmlResults.length, - load_ok_count: loadOkCount, - load_error_count: loadErrorCount, - import_error_count: importErrorCount, - diff_count: diffCount, - }; -} - -async function safelyCloseBrowser(browser) { - if (!browser) { - return; - } - - try { - await browser.close(); - } catch { - try { - const processHandle = browser.process?.(); - processHandle?.kill("SIGKILL"); - } catch { - // ignore - } - } -} - -async function main() { - const options = parseArgs(process.argv.slice(2)); - const truthManifest = await readJson(options.truthPath); - assertTruthManifest(truthManifest); - - const groupedEntries = groupTruthEntries(truthManifest, options.modelDirs); - if (groupedEntries.size === 0) { - fail("No truth entries matched the requested model_dir filters."); - } - - const zipCandidates = await listZipCandidates(options.regressionRoot); - const siteHandle = await ensureSite(options.siteUrl, options); - const browser = await launchBrowser(options); - - const cleanupHandlers = [ - async () => safelyCloseBrowser(browser), - async () => siteHandle.stop(), - ]; - - const terminate = async (signal) => { - console.error(`\nReceived ${signal}, cleaning up...`); - for (const handler of cleanupHandlers) { - try { - await handler(); - } catch { - // ignore - } - } - process.exit(1); - }; - - process.once("SIGINT", () => void terminate("SIGINT")); - process.once("SIGTERM", () => void terminate("SIGTERM")); - - const modelResults = []; - - try { - for (const [modelDir, entries] of groupedEntries.entries()) { - let zipPath = null; - try { - zipPath = await resolveZipPath( - modelDir, - entries, - options.regressionRoot, - zipCandidates, - ); - } catch (error) { - modelResults.push( - buildModelErrorResults( - modelDir, - entries, - zipPath, - String(error?.stack || error?.message || error), - ), - ); - continue; - } - - const result = await runModelRegression( - browser, - siteHandle.siteUrl, - modelDir, - entries, - zipPath, - options, - ); - modelResults.push(result); - } - } finally { - await safelyCloseBrowser(browser); - await siteHandle.stop(); - } - - const output = { - generated_at: new Date().toISOString(), - generator: "scripts/regression/run_menagerie_browser_regression.mjs", - site_url: siteHandle.siteUrl, - site_started_by_script: siteHandle.startedByScript, - truth_path: options.truthPath, - output_path: options.outputPath, - regression_root: options.regressionRoot, - model_dirs: [...groupedEntries.keys()], - summary: summarizeRun(modelResults), - results: modelResults, - }; - - await writeJsonAtomic(options.outputPath, output); - console.log(`\nWrote browser regression results to ${options.outputPath}`); -} - -main().catch((error) => { - console.error(error?.stack || error?.message || String(error)); - process.exit(1); -}); diff --git a/scripts/regression/run_shadow_hand_hover_regression.mjs b/scripts/regression/run_shadow_hand_hover_regression.mjs index 913deb844..1fb1fdb22 100644 --- a/scripts/regression/run_shadow_hand_hover_regression.mjs +++ b/scripts/regression/run_shadow_hand_hover_regression.mjs @@ -28,37 +28,17 @@ const DEFAULT_VIEWPORT = { const FIXED_SAMPLE_POINTS = [{ name: 'empty_top_left', dx: 40, dy: 40, expected: null }]; const EXPECTED_FRONT_TARGETS = [ - 'lh_forearm_geom_1:visual:undefined', - 'lh_palm:visual:undefined', - 'lh_lfmiddle:visual:undefined', + 'lh_forearm_geom_1:visual:', + 'lh_palm:visual:', + 'lh_lfmiddle:visual:', ]; -const TRANSIENT_LOADING_TEXTS = [ - 'Loading robot...', - 'Preparing scene...', - 'Streaming scene meshes', - 'Checking USD stage path', - 'Preloading USD dependencies', - 'Initializing USD renderer', - 'Applying scene fixes', - 'Resolving robot metadata', - 'Finalizing scene', - '加载机器人中...', - '正在准备场景', - '正在流式加载场景网格', - '正在检查 USD 场景路径', - '正在预加载 USD 依赖', - '正在初始化 USD 渲染器', - '正在应用场景修正', - '正在解析机器人元数据', - '正在完成场景收尾', -]; - -const TERMINAL_LOADING_ERROR_TEXTS = [ - 'Load failed', - 'Preview unavailable', - '加载失败', - '当前文件无法预览', +const PROJECTED_TARGET_PROBE_OFFSETS = [ + [0, 0], + [4, 0], + [-4, 0], + [0, 4], + [0, -4], ]; function fail(message) { @@ -140,7 +120,9 @@ function parseArgs(argv) { } } - options.siteUrl = new URL(options.siteUrl).toString(); + const normalizedSiteUrl = new URL(options.siteUrl); + normalizedSiteUrl.searchParams.set('regressionDebug', '1'); + options.siteUrl = normalizedSiteUrl.toString(); return options; } @@ -439,20 +421,52 @@ async function createPage(browser, siteUrl, timeoutMs) { waitUntil: 'domcontentloaded', timeout: timeoutMs, }); + await waitForDebugApi(page, timeoutMs); return { page, consoleMessages, pageErrors }; } -async function waitForBodyText(page, text, timeoutMs) { +async function waitForDebugApi(page, timeoutMs) { + await page.waitForFunction(() => Boolean(globalThis.window && window.__URDF_STUDIO_DEBUG__), { + timeout: timeoutMs, + }); + + try { + await page.evaluate(async () => { + const api = window.__URDF_STUDIO_DEBUG__; + const candidateNames = ['ping', 'healthCheck', 'healthcheck', 'ready']; + for (const name of candidateNames) { + if (typeof api?.[name] === 'function') { + await api[name](); + return; + } + } + }); + } catch { + // ping is optional + } +} + +async function waitForExpectedRobot(page, robotName, timeoutMs) { await retryPageAction( () => page.waitForFunction( - (expectedText) => document.body?.innerText?.includes(expectedText), + (expectedRobotName) => { + const api = window.__URDF_STUDIO_DEBUG__; + const snapshot = api?.getRegressionSnapshot?.(); + const documentLoadState = api?.getDocumentLoadState?.(); + const storeRobotName = snapshot?.store?.name ?? null; + const runtimeRobotName = snapshot?.runtime?.name ?? null; + return ( + documentLoadState?.status === 'ready' && + (storeRobotName === expectedRobotName || runtimeRobotName === expectedRobotName) + ); + }, { timeout: Math.min(timeoutMs, 5_000) }, - text, + robotName, ), timeoutMs, - `body text "${text}"`, + `robot "${robotName}" to load`, ); } @@ -530,6 +544,21 @@ async function clickLabelByText(page, text, timeoutMs) { } } +async function clickLabelByAnyText(page, candidateTexts, timeoutMs) { + let lastError = null; + + for (const text of candidateTexts) { + try { + await clickLabelByText(page, text, timeoutMs); + return; + } catch (error) { + lastError = error; + } + } + + throw lastError ?? new Error(`Could not click any label matching: ${candidateTexts.join(', ')}`); +} + async function importFixtureZip(page, zipPath, timeoutMs) { await page.waitForSelector('input[type="file"]', { timeout: timeoutMs }); const handles = await page.$$('input[type="file"]'); @@ -608,13 +637,14 @@ async function importFixtureFolder(page, fixtureDir, timeoutMs) { async function readHoveredSelection(page) { return await retryPageAction( () => - page.evaluate(async () => { - const mod = await import('/src/store/selectionStore.ts'); - const hovered = mod.useSelectionStore.getState().hoveredSelection; - if (!hovered?.type) { + page.evaluate(() => { + const hovered = + window.__URDF_STUDIO_DEBUG__?.getRegressionSnapshot()?.interaction?.hoveredSelection; + if (!hovered?.type || !hovered?.id) { return null; } - return `${hovered.id}:${hovered.subType}:${hovered.objectIndex}`; + + return `${hovered.id}:${hovered.subType}:${hovered.objectIndex ?? 'undefined'}`; }), 10_000, 'reading hovered selection', @@ -624,25 +654,48 @@ async function readHoveredSelection(page) { async function readSelectionState(page) { return await retryPageAction( () => - page.evaluate(async () => { - const mod = await import('/src/store/selectionStore.ts'); - const selection = mod.useSelectionStore.getState().selection; - if (!selection?.type) { - return null; - } - - return { - type: selection.type, - id: selection.id, - subType: selection.subType ?? null, - objectIndex: selection.objectIndex ?? null, - }; - }), + page.evaluate( + () => window.__URDF_STUDIO_DEBUG__?.getRegressionSnapshot()?.interaction?.selection ?? null, + ), 10_000, 'reading selection state', ); } +async function setViewerToolMode(page, toolMode, timeoutMs) { + await retryPageAction( + () => + page.evaluate( + (nextToolMode) => window.__URDF_STUDIO_DEBUG__?.setViewerToolMode(nextToolMode), + toolMode, + ), + timeoutMs, + `setting viewer tool mode ${toolMode}`, + ); + + await retryPageAction( + () => + page.waitForFunction( + (expectedToolMode) => + window.__URDF_STUDIO_DEBUG__?.getRegressionSnapshot()?.viewer?.toolMode === + expectedToolMode, + { timeout: Math.min(timeoutMs, 5_000) }, + toolMode, + ), + timeoutMs, + `waiting for tool mode ${toolMode}`, + ); +} + +async function getProjectedInteractionTargets(page, timeoutMs) { + return await retryPageAction( + () => + page.evaluate(() => window.__URDF_STUDIO_DEBUG__?.getProjectedInteractionTargets?.() ?? []), + timeoutMs, + 'reading projected interaction targets', + ); +} + async function captureCanvasBox(page) { return await retryPageAction( () => @@ -668,22 +721,32 @@ async function readSceneStatus(page, robotName) { return await retryPageAction( () => page.evaluate( - ({ expectedRobotName, transientTexts, errorTexts }) => { + ({ expectedRobotName }) => { + const api = window.__URDF_STUDIO_DEBUG__; + const snapshot = api?.getRegressionSnapshot?.() ?? null; + const documentLoadState = api?.getDocumentLoadState?.() ?? null; const bodyText = document.body?.innerText ?? ''; const canvas = document.querySelector('canvas'); + const storeRobotName = snapshot?.store?.name ?? null; + const runtimeRobotName = snapshot?.runtime?.name ?? null; + const documentStatus = documentLoadState?.status ?? null; + const documentError = documentLoadState?.error ?? null; return { - hasRobot: bodyText.includes(expectedRobotName), + hasRobot: + storeRobotName === expectedRobotName || runtimeRobotName === expectedRobotName, hasCanvas: canvas instanceof HTMLCanvasElement, - activeLoadingTexts: transientTexts.filter((text) => bodyText.includes(text)), - activeErrorTexts: errorTexts.filter((text) => bodyText.includes(text)), + activeLoadingTexts: + documentStatus === 'loading' || documentStatus === 'hydrating' + ? [documentStatus] + : [], + activeErrorTexts: + documentStatus === 'error' ? [documentError || 'document-load-error'] : [], + documentStatus, + documentFileName: documentLoadState?.fileName ?? null, bodyExcerpt: bodyText.slice(0, 2000), }; }, - { - expectedRobotName: robotName, - transientTexts: TRANSIENT_LOADING_TEXTS, - errorTexts: TERMINAL_LOADING_ERROR_TEXTS, - }, + { expectedRobotName: robotName }, ), 10_000, `reading scene status for ${robotName}`, @@ -743,19 +806,66 @@ async function samplePoint(page, canvasBox, point) { }; } -async function runGridScan(page, canvasBox) { +async function runGridScan(page, canvasBox, timeoutMs) { const records = []; + const recordPoint = async (x, y) => { + if ( + x < canvasBox.x + 1 || + x > canvasBox.x + canvasBox.width - 1 || + y < canvasBox.y + 1 || + y > canvasBox.y + canvasBox.height - 1 + ) { + return; + } + + await page.mouse.move(x, y); + await delay(80); + const hovered = await readHoveredSelection(page); + if (hovered) { + records.push({ + dx: x - canvasBox.x, + dy: y - canvasBox.y, + x, + y, + hovered, + }); + } + }; for (let dy = 40; dy <= Math.round(canvasBox.height) - 40; dy += 24) { for (let dx = 40; dx <= Math.round(canvasBox.width) - 40; dx += 24) { const x = Math.round(canvasBox.x + dx); const y = Math.round(canvasBox.y + dy); - await page.mouse.move(x, y); - await delay(25); - const hovered = await readHoveredSelection(page); - if (hovered) { - records.push({ dx, dy, x, y, hovered }); - } + await recordPoint(x, y); + } + } + + const projectedTargets = await getProjectedInteractionTargets(page, timeoutMs); + const sampledTargetKeys = new Set(); + const probeTargets = [...projectedTargets] + .filter((target) => target?.type === 'link' && target?.subType === 'visual') + .sort( + (left, right) => + (right.projectedArea ?? 0) - (left.projectedArea ?? 0) || + String(left.id ?? '').localeCompare(String(right.id ?? '')), + ); + + for (const target of probeTargets) { + const targetKey = `${target.id}:${target.subType}:${target.objectIndex ?? 'undefined'}`; + if (sampledTargetKeys.has(targetKey)) { + continue; + } + sampledTargetKeys.add(targetKey); + + for (const [offsetX, offsetY] of PROJECTED_TARGET_PROBE_OFFSETS) { + await recordPoint( + Math.round((target.clientX ?? 0) + offsetX), + Math.round((target.clientY ?? 0) + offsetY), + ); + } + + if (sampledTargetKeys.size >= 24) { + break; } } @@ -800,6 +910,15 @@ async function clickCanvasPoint(page, point) { await page.mouse.click(point.x, point.y, { delay: 20 }); } +async function primeCanvasInteraction(page, canvasBox) { + const centerPoint = { + x: Math.round(canvasBox.x + canvasBox.width / 2), + y: Math.round(canvasBox.y + canvasBox.height / 2), + }; + await clickCanvasPoint(page, centerPoint); + await delay(120); +} + async function findLinkSelectionCandidate(page, records) { const triedTargets = new Set(); @@ -826,11 +945,16 @@ async function findLinkSelectionCandidate(page, records) { } function summarizeExpectedTargets(grid, targets) { - const summaryByTarget = new Map(grid.uniqueHoveredTargets.map((entry) => [entry.target, entry])); - - const foundTargets = targets.map((target) => summaryByTarget.get(target)).filter(Boolean); - - const missingTargets = targets.filter((target) => !summaryByTarget.has(target)); + const foundTargets = targets + .map( + (target) => + grid.uniqueHoveredTargets.find((entry) => entry.target.startsWith(target)) ?? null, + ) + .filter(Boolean); + + const missingTargets = targets.filter( + (target) => !grid.uniqueHoveredTargets.some((entry) => entry.target.startsWith(target)), + ); return { foundTargets, @@ -854,9 +978,9 @@ function assertRegressionResults(result) { } const topTarget = result.grid.uniqueHoveredTargets[0]?.target ?? null; - if (topTarget !== 'lh_forearm_geom_1:visual:undefined') { + if (!topTarget?.startsWith('lh_forearm_geom_1:visual:')) { failures.push( - `Expected dominant hovered target to be lh_forearm_geom_1:visual:undefined, got ${topTarget ?? 'null'}`, + `Expected dominant hovered target to be lh_forearm_geom_1:visual:*, got ${topTarget ?? 'null'}`, ); } @@ -913,39 +1037,42 @@ async function main() { await importFixtureFolder(page, options.fixtureDir, options.timeoutMs); try { - await waitForBodyText(page, expectedRobotName, 30_000); + await waitForExpectedRobot(page, expectedRobotName, 30_000); } catch (folderImportError) { await page.goto(options.siteUrl, { waitUntil: 'domcontentloaded', timeout: options.timeoutMs, }); + await waitForDebugApi(page, options.timeoutMs); await importFixtureZip(page, options.zipPath, options.timeoutMs); - await waitForBodyText(page, expectedRobotName, options.timeoutMs); + await waitForExpectedRobot(page, expectedRobotName, options.timeoutMs); } await waitForSceneToSettle(page, expectedRobotName, options.timeoutMs); await waitForSceneToSettle(page, expectedRobotName, options.timeoutMs); - await clickLabelByText(page, 'Show Geometry', options.timeoutMs); + await clickLabelByAnyText(page, ['Show Visual', 'Show Geometry'], options.timeoutMs); await waitForSceneToSettle(page, expectedRobotName, options.timeoutMs); await clickElementByText(page, 'button', 'Auto Fit', options.timeoutMs); await waitForSceneToSettle(page, expectedRobotName, options.timeoutMs, 800); + await setViewerToolMode(page, 'select', options.timeoutMs); const canvasBox = await captureCanvasBox(page); if (!canvasBox) { fail('Could not locate canvas bounding box.'); } + await primeCanvasInteraction(page, canvasBox); const samples = []; for (const point of FIXED_SAMPLE_POINTS) { samples.push(await samplePoint(page, canvasBox, point)); } - const grid = await runGridScan(page, canvasBox); + const grid = await runGridScan(page, canvasBox, options.timeoutMs); const targetPresence = summarizeExpectedTargets(grid, EXPECTED_FRONT_TARGETS); const clickSelectionCandidate = await findLinkSelectionCandidate(page, grid.records); const postClickGrid = clickSelectionCandidate - ? await runGridScan(page, canvasBox) + ? await runGridScan(page, canvasBox, options.timeoutMs) : { records: [], hoveredCount: 0, diff --git a/scripts/regression/validate_unitree_ros_usd_export_benchmark.mjs b/scripts/regression/validate_unitree_ros_usd_export_benchmark.mjs new file mode 100644 index 000000000..a00f93558 --- /dev/null +++ b/scripts/regression/validate_unitree_ros_usd_export_benchmark.mjs @@ -0,0 +1,925 @@ +#!/usr/bin/env node + +import fs from 'node:fs/promises'; +import path from 'node:path'; +import process from 'node:process'; +import { spawn } from 'node:child_process'; +import { setTimeout as delay } from 'node:timers/promises'; +import puppeteer from 'puppeteer'; + +const DEFAULT_SITE_URL = 'http://127.0.0.1:4173'; +const DEFAULT_SITE_TIMEOUT_MS = 120_000; +const DEFAULT_OPERATION_TIMEOUT_MS = 240_000; +const DEFAULT_OUTPUT_PATH = path.resolve('tmp/regression/unitree_ros_usd_export_benchmark.json'); +const DEFAULT_START_COMMAND = (host, port) => `npm run dev -- --host ${host} --port ${port}`; +const DEFAULT_EXECUTABLE_CANDIDATES = [ + process.env.CHROME_PATH, + '/usr/bin/google-chrome', + '/usr/bin/google-chrome-stable', + '/usr/bin/chromium-browser', + '/usr/bin/chromium', +].filter(Boolean); +const ASSET_EXTENSIONS = new Set([ + '.dae', + '.obj', + '.stl', + '.gltf', + '.glb', + '.png', + '.jpg', + '.jpeg', + '.bmp', + '.gif', + '.webp', + '.mtl', +]); +const IDENTITY_TRANSFORM = Object.freeze({ + position: { x: 0, y: 0, z: 0 }, + rotation: { r: 0, p: 0, y: 0 }, +}); +const SAMPLE_DEFINITIONS = [ + { + id: 'b2', + label: 'b2_description', + mode: 'single', + exportName: 'b2_description', + maxTotalMs: 11_000, + maxSceneMs: 8_000, + inputs: [ + { + urdfRelative: 'test/unitree_ros/robots/b2_description/urdf/b2_description.urdf', + assetRootRelative: 'test/unitree_ros/robots/b2_description', + }, + ], + }, + { + id: 'g1-dual-arm', + label: 'g1_dual_arm', + mode: 'single', + exportName: 'g1_dual_arm', + maxTotalMs: 10_000, + maxSceneMs: 8_000, + inputs: [ + { + urdfRelative: 'test/unitree_ros/robots/g1_description/g1_dual_arm.urdf', + assetRootRelative: 'test/unitree_ros/robots/g1_description', + }, + ], + }, + { + id: 'assembly', + label: 'b2 + g1_dual_arm assembly', + mode: 'assembly', + exportName: 'b2_g1_dual_arm_assembly', + maxTotalMs: 12_000, + maxSceneMs: 8_500, + inputs: [ + { + urdfRelative: 'test/unitree_ros/robots/b2_description/urdf/b2_description.urdf', + assetRootRelative: 'test/unitree_ros/robots/b2_description', + componentId: 'comp_b2', + rootName: 'b2', + transform: IDENTITY_TRANSFORM, + }, + { + urdfRelative: 'test/unitree_ros/robots/g1_description/g1_dual_arm.urdf', + assetRootRelative: 'test/unitree_ros/robots/g1_description', + componentId: 'comp_g1_dual_arm', + rootName: 'g1_dual_arm', + transform: { + position: { x: 2.5, y: 0, z: 0 }, + rotation: { r: 0, p: 0, y: 0 }, + }, + }, + ], + }, +]; + +const HELP_TEXT = `Usage: + node scripts/regression/validate_unitree_ros_usd_export_benchmark.mjs [options] + +Options: + --site-url Dev site URL. Default: ${DEFAULT_SITE_URL} + --output Output JSON path. Default: ${DEFAULT_OUTPUT_PATH} + --model Restrict to one sample id. Repeatable. Valid: ${SAMPLE_DEFINITIONS.map((sample) => sample.id).join(', ')} + --site-timeout-ms Site startup timeout. Default: ${DEFAULT_SITE_TIMEOUT_MS} + --timeout-ms Per-sample timeout. Default: ${DEFAULT_OPERATION_TIMEOUT_MS} + --start-command Override auto-start command. + --chrome-path Browser executable path. + --no-start Fail if the site is offline. + --no-assert Do not fail on threshold regressions. + --headed Launch headed browser. + --help Show this help. +`; + +function normalizePath(value) { + return String(value || '') + .replace(/\\/g, '/') + .replace(/^\/+/, ''); +} + +function fail(message) { + throw new Error(message); +} + +function parseInteger(value, flagName) { + const parsed = Number.parseInt(value, 10); + if (!Number.isFinite(parsed) || parsed < 0) { + fail(`Invalid value for ${flagName}: ${value}`); + } + return parsed; +} + +function parseArgs(argv) { + const options = { + siteUrl: DEFAULT_SITE_URL, + outputPath: DEFAULT_OUTPUT_PATH, + models: [], + siteTimeoutMs: DEFAULT_SITE_TIMEOUT_MS, + timeoutMs: DEFAULT_OPERATION_TIMEOUT_MS, + startCommand: null, + chromePath: null, + noStart: false, + assertThresholds: true, + headed: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + const nextValue = () => { + const value = argv[index + 1]; + if (value == null) { + fail(`Missing value for ${arg}`); + } + index += 1; + return value; + }; + + switch (arg) { + case '--site-url': + options.siteUrl = nextValue(); + break; + case '--output': + options.outputPath = path.resolve(nextValue()); + break; + case '--model': + options.models.push(nextValue()); + break; + case '--site-timeout-ms': + options.siteTimeoutMs = parseInteger(nextValue(), '--site-timeout-ms'); + break; + case '--timeout-ms': + options.timeoutMs = parseInteger(nextValue(), '--timeout-ms'); + break; + case '--start-command': + options.startCommand = nextValue(); + break; + case '--chrome-path': + options.chromePath = path.resolve(nextValue()); + break; + case '--no-start': + options.noStart = true; + break; + case '--no-assert': + options.assertThresholds = false; + break; + case '--headed': + options.headed = true; + break; + case '--help': + case '-h': + console.log(HELP_TEXT); + process.exit(0); + break; + default: + fail(`Unknown argument: ${arg}`); + } + } + + options.models = [...new Set(options.models.map((value) => value.trim()).filter(Boolean))]; + return options; +} + +async function writeJsonAtomic(filePath, value) { + await fs.mkdir(path.dirname(filePath), { recursive: true }); + const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`; + await fs.writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8'); + await fs.rename(tempPath, filePath); +} + +async function fileExists(filePath) { + try { + await fs.access(filePath); + return true; + } catch { + return false; + } +} + +async function fetchWithTimeout(url, timeoutMs) { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + try { + return await fetch(url, { + signal: controller.signal, + redirect: 'follow', + cache: 'no-store', + }); + } finally { + clearTimeout(timeoutId); + } +} + +async function isSiteReachable(siteUrl, timeoutMs) { + try { + const response = await fetchWithTimeout(siteUrl, Math.min(timeoutMs, 10_000)); + return response.ok; + } catch { + return false; + } +} + +async function waitForSite(siteUrl, timeoutMs) { + const startedAt = Date.now(); + while (Date.now() - startedAt < timeoutMs) { + if (await isSiteReachable(siteUrl, timeoutMs)) { + return true; + } + await delay(1_000); + } + return false; +} + +function deriveStartCommand(siteUrl, explicitCommand) { + if (explicitCommand) { + return explicitCommand; + } + const url = new URL(siteUrl); + const host = url.hostname || '127.0.0.1'; + const port = url.port || '4173'; + return DEFAULT_START_COMMAND(host, port); +} + +async function isSourceImportsReachable(siteUrl, timeoutMs) { + try { + const probeUrl = new URL( + '/src/features/file-io/utils/usdExportCoordinator.ts', + siteUrl, + ).toString(); + const response = await fetchWithTimeout(probeUrl, Math.min(timeoutMs, 10_000)); + return response.ok; + } catch { + return false; + } +} + +async function ensureSiteAvailable(options) { + if (await isSiteReachable(options.siteUrl, options.siteTimeoutMs)) { + if (await isSourceImportsReachable(options.siteUrl, options.siteTimeoutMs)) { + return { startedProcess: null }; + } + + fail( + `Site is reachable but does not expose direct /src module imports: ${options.siteUrl}. ` + + 'Use a Vite dev server, not preview.', + ); + } + + if (options.noStart) { + fail(`Site is offline and --no-start was provided: ${options.siteUrl}`); + } + + const startCommand = deriveStartCommand(options.siteUrl, options.startCommand); + const child = spawn(startCommand, { + cwd: process.cwd(), + stdio: ['ignore', 'pipe', 'pipe'], + shell: true, + env: { + ...process.env, + CHOKIDAR_USEPOLLING: process.env.CHOKIDAR_USEPOLLING || '1', + CHOKIDAR_INTERVAL: process.env.CHOKIDAR_INTERVAL || '200', + }, + }); + child.stdout.on('data', (chunk) => process.stderr.write(chunk)); + child.stderr.on('data', (chunk) => process.stderr.write(chunk)); + + const ready = await waitForSite(options.siteUrl, options.siteTimeoutMs); + if (!ready) { + child.kill('SIGTERM'); + fail(`Timed out waiting for site to become reachable: ${options.siteUrl}`); + } + + if (!(await isSourceImportsReachable(options.siteUrl, options.siteTimeoutMs))) { + child.kill('SIGTERM'); + fail( + `Started site but direct /src module imports are still unavailable: ${options.siteUrl}. ` + + 'This benchmark requires a Vite dev server.', + ); + } + + return { startedProcess: child }; +} + +function resolveExecutablePath(explicitChromePath) { + if (explicitChromePath) { + return explicitChromePath; + } + return DEFAULT_EXECUTABLE_CANDIDATES.find(Boolean) ?? null; +} + +async function collectAssetFiles(rootDir, baseDir = rootDir) { + const results = []; + const entries = await fs.readdir(rootDir, { withFileTypes: true }); + entries.sort((left, right) => left.name.localeCompare(right.name)); + + for (const entry of entries) { + const absolutePath = path.join(rootDir, entry.name); + if (entry.isDirectory()) { + results.push(...(await collectAssetFiles(absolutePath, baseDir))); + continue; + } + + if (!entry.isFile()) { + continue; + } + + const extension = path.extname(entry.name).toLowerCase(); + if (!ASSET_EXTENSIONS.has(extension)) { + continue; + } + + results.push({ + absolutePath, + relativePath: path.relative(baseDir, absolutePath).split(path.sep).join('/'), + }); + } + + return results; +} + +const assetDescriptorCache = new Map(); + +async function buildAssetDescriptors(assetRootRelative) { + const normalizedRoot = normalizePath(assetRootRelative); + const cached = assetDescriptorCache.get(normalizedRoot); + if (cached) { + return cached; + } + + const assetRootAbsolute = path.resolve(normalizedRoot); + if (!(await fileExists(assetRootAbsolute))) { + fail(`Asset root does not exist: ${assetRootRelative}`); + } + + const packageName = path.basename(assetRootAbsolute); + const assetFiles = await collectAssetFiles(assetRootAbsolute); + const descriptors = assetFiles.map(({ absolutePath, relativePath }) => ({ + urlPath: `/${normalizePath(path.relative(process.cwd(), absolutePath))}`, + aliases: [ + relativePath, + `${packageName}/${relativePath}`, + `package://${packageName}/${relativePath}`, + ], + })); + + assetDescriptorCache.set(normalizedRoot, descriptors); + return descriptors; +} + +async function resolveSamples(options) { + const allSamples = await Promise.all( + SAMPLE_DEFINITIONS.map(async (sample) => ({ + ...sample, + inputs: await Promise.all( + sample.inputs.map(async (input) => ({ + ...input, + urdfUrlPath: `/${normalizePath(input.urdfRelative)}`, + assetDescriptors: await buildAssetDescriptors(input.assetRootRelative), + })), + ), + })), + ); + + if (options.models.length === 0) { + return allSamples; + } + + const selected = new Set(options.models.map((value) => value.toLowerCase())); + const filtered = allSamples.filter((sample) => selected.has(sample.id.toLowerCase())); + if (filtered.length === 0) { + fail(`No sample matched --model filters: ${options.models.join(', ')}`); + } + return filtered; +} + +async function openFreshPage(browser, siteUrl, timeoutMs) { + let lastError = null; + + for (let attempt = 0; attempt < 3; attempt += 1) { + const page = await browser.newPage(); + await page.setViewport({ width: 1440, height: 1024, deviceScaleFactor: 1 }); + await page.setCacheEnabled(false); + page.setDefaultTimeout(timeoutMs); + + try { + await page.goto(siteUrl, { waitUntil: 'domcontentloaded', timeout: timeoutMs }); + await delay(500); + return page; + } catch (error) { + lastError = error; + await page.close().catch(() => {}); + + const message = error instanceof Error ? error.message : String(error); + if (!/frame got detached|Navigating frame was detached/i.test(message) || attempt === 2) { + throw error; + } + + await delay(750); + } + } + + throw lastError ?? new Error(`Unable to open benchmark page: ${siteUrl}`); +} + +async function assertSourceImportsAvailable(page) { + const probe = await page.evaluate(async () => { + try { + await import('/src/features/file-io/utils/usdExportCoordinator.ts'); + return { ok: true, message: null }; + } catch (error) { + return { + ok: false, + message: error instanceof Error ? error.message : String(error), + }; + } + }); + + if (!probe?.ok) { + fail( + `The target site does not support direct /src module imports. Use a Vite dev server, not preview. Details: ${probe?.message || 'unknown error'}`, + ); + } +} + +function isRetryableRuntimeError(value) { + const message = String(value || ''); + return /Execution context was destroyed|frame got detached|Navigating frame was detached/i.test( + message, + ); +} + +async function warmupSourceImports(browser, siteUrl, timeoutMs) { + let lastError = null; + + for (let attempt = 0; attempt < 3; attempt += 1) { + const page = await openFreshPage(browser, siteUrl, timeoutMs); + try { + await page.evaluate(async () => { + await Promise.all([ + import('/src/core/parsers/urdf/parser/index.ts'), + import('/src/features/file-io/utils/usdExportCoordinator.ts'), + import('/src/core/robot/assemblyComponentPreparation.ts'), + import('/src/core/robot/assemblyTransforms.ts'), + ]); + return true; + }); + return; + } catch (error) { + lastError = error; + if ( + !isRetryableRuntimeError(error instanceof Error ? error.message : String(error)) || + attempt === 2 + ) { + throw error; + } + await delay(750); + } finally { + await page.close().catch(() => {}); + } + } + + throw lastError ?? new Error('Unable to warm up source imports for USD export benchmark.'); +} + +function createRingBuffer(limit = 100) { + const values = []; + return { + push(value) { + values.push(value); + if (values.length > limit) { + values.splice(0, values.length - limit); + } + }, + snapshot() { + return [...values]; + }, + }; +} + +async function benchmarkSample(page, sample) { + return await page.evaluate(async (sampleInput) => { + const normalizePath = (value) => + String(value || '') + .replace(/\\/g, '/') + .replace(/^\/+/, ''); + const toUrl = (value) => { + if (/^https?:\/\//i.test(value)) { + return value; + } + return new URL(value.startsWith('/') ? value : `/${normalizePath(value)}`, location.origin) + .href; + }; + const toRobotState = (robotData) => ({ + ...robotData, + selection: { + type: 'link', + id: robotData.rootLinkId ?? null, + }, + }); + const meshType = 'mesh'; + const identityTransform = { + position: { x: 0, y: 0, z: 0 }, + rotation: { r: 0, p: 0, y: 0 }, + }; + const collectMeshReferences = (robotData) => { + const unique = new Set(); + const visitVisual = (visual) => { + if ( + !visual || + String(visual.type || '') + .trim() + .toLowerCase() !== meshType + ) { + return; + } + const meshPath = String(visual.meshPath || visual.filename || '').trim(); + if (meshPath) { + unique.add(meshPath); + } + }; + + for (const link of Object.values(robotData.links || {})) { + visitVisual(link.visual); + visitVisual(link.collision); + for (const collisionBody of link.collisionBodies || []) { + visitVisual(collisionBody); + } + } + + return [...unique].sort((left, right) => left.localeCompare(right)); + }; + + const [parserModule, exportModule, assemblyPreparationModule, assemblyTransformsModule] = + await Promise.all([ + import('/src/core/parsers/urdf/parser/index.ts'), + import('/src/features/file-io/utils/usdExportCoordinator.ts'), + import('/src/core/robot/assemblyComponentPreparation.ts'), + import('/src/core/robot/assemblyTransforms.ts'), + ]); + + const { parseURDF } = parserModule; + const { exportRobotToUsd } = exportModule; + const { namespaceAssemblyRobotData } = assemblyPreparationModule; + const { buildExportableAssemblyRobotData } = assemblyTransformsModule; + + const blobPromiseCache = new Map(); + const fetchBlob = async (url) => { + if (blobPromiseCache.has(url)) { + return await blobPromiseCache.get(url); + } + + const request = fetch(url, { cache: 'no-store' }).then(async (response) => { + if (!response.ok) { + throw new Error(`Failed to fetch asset ${url}: ${response.status}`); + } + return await response.blob(); + }); + blobPromiseCache.set(url, request); + return await request; + }; + + const buildExtraMeshFiles = async (assetDescriptors) => { + const extraMeshFiles = new Map(); + + for (const descriptor of assetDescriptors) { + const blob = await fetchBlob(toUrl(descriptor.urlPath)); + descriptor.aliases.forEach((alias) => { + const normalizedAlias = normalizePath(alias); + if (normalizedAlias && !extraMeshFiles.has(normalizedAlias)) { + extraMeshFiles.set(normalizedAlias, blob); + } + }); + } + + return extraMeshFiles; + }; + + const mergeExtraMeshFiles = (maps) => { + const merged = new Map(); + maps.forEach((map) => { + map.forEach((blob, key) => { + if (!merged.has(key)) { + merged.set(key, blob); + } + }); + }); + return merged; + }; + + const loadedInputs = []; + for (const input of sampleInput.inputs) { + const urdfResponse = await fetch(toUrl(input.urdfUrlPath), { cache: 'no-store' }); + if (!urdfResponse.ok) { + throw new Error(`Failed to fetch URDF ${input.urdfUrlPath}: ${urdfResponse.status}`); + } + + const urdfText = await urdfResponse.text(); + const parsedRobot = parseURDF(urdfText); + if (!parsedRobot) { + throw new Error(`Failed to parse URDF ${input.urdfUrlPath}`); + } + + loadedInputs.push({ + input, + robot: parsedRobot, + extraMeshFiles: await buildExtraMeshFiles(input.assetDescriptors), + meshReferences: collectMeshReferences(parsedRobot), + }); + } + + const uniqueMeshReferences = new Set(); + loadedInputs.forEach((entry) => { + entry.meshReferences.forEach((meshReference) => uniqueMeshReferences.add(meshReference)); + }); + + let exportRobot; + let extraMeshFiles; + + if (sampleInput.mode === 'assembly') { + const components = Object.create(null); + loadedInputs.forEach((entry) => { + components[entry.input.componentId] = { + id: entry.input.componentId, + name: entry.input.rootName, + sourceFile: entry.input.urdfUrlPath, + robot: namespaceAssemblyRobotData(entry.robot, { + componentId: entry.input.componentId, + rootName: entry.input.rootName, + }), + transform: entry.input.transform || identityTransform, + visible: true, + }; + }); + + exportRobot = buildExportableAssemblyRobotData({ + name: sampleInput.exportName, + transform: identityTransform, + components, + bridges: {}, + }); + extraMeshFiles = mergeExtraMeshFiles(loadedInputs.map((entry) => entry.extraMeshFiles)); + } else { + exportRobot = loadedInputs[0].robot; + extraMeshFiles = loadedInputs[0].extraMeshFiles; + } + + const robotState = toRobotState(exportRobot); + const phaseStarts = Object.create(null); + const phaseEnds = Object.create(null); + const phaseLastSeen = Object.create(null); + const totalStart = performance.now(); + let progressEventCount = 0; + + const payload = await exportRobotToUsd({ + robot: robotState, + exportName: sampleInput.exportName, + assets: {}, + extraMeshFiles, + meshCompression: { + enabled: true, + quality: 50, + }, + fileFormat: 'usda', + layoutProfile: 'isaacsim', + onProgress: (progress) => { + const now = performance.now(); + const phase = String(progress.phase || ''); + if (!phase) { + return; + } + + if (phaseStarts[phase] == null) { + phaseStarts[phase] = now; + } + phaseLastSeen[phase] = now; + progressEventCount += 1; + + if (Number(progress.total) > 0 && Number(progress.completed) >= Number(progress.total)) { + phaseEnds[phase] = now; + } + }, + }); + + const totalMs = Math.round(performance.now() - totalStart); + const phaseMs = {}; + for (const phase of ['links', 'geometry', 'scene', 'assets']) { + if (phaseStarts[phase] == null) { + continue; + } + + const stop = phaseEnds[phase] ?? phaseLastSeen[phase] ?? performance.now(); + phaseMs[phase] = Math.round(stop - phaseStarts[phase]); + } + + return { + totalMs, + phaseMs, + progressEventCount, + archiveFileCount: payload.archiveFiles.size, + rootLayerPath: payload.rootLayerPath, + contentLength: payload.content.length, + linkCount: Object.keys(robotState.links || {}).length, + jointCount: Object.keys(robotState.joints || {}).length, + inputModelCount: loadedInputs.length, + uniqueMeshCount: uniqueMeshReferences.size, + assetFileCount: loadedInputs.reduce( + (count, entry) => count + entry.input.assetDescriptors.length, + 0, + ), + }; + }, sample); +} + +function buildSampleResult(sample, benchmark, consoleErrors) { + const failures = []; + if (!benchmark || typeof benchmark.totalMs !== 'number') { + failures.push('missing benchmark payload'); + } + + const sceneMs = Number(benchmark?.phaseMs?.scene ?? NaN); + if (Number.isFinite(benchmark?.totalMs) && benchmark.totalMs > sample.maxTotalMs) { + failures.push(`total ${benchmark.totalMs}ms > budget ${sample.maxTotalMs}ms`); + } + if (Number.isFinite(sceneMs) && sceneMs > sample.maxSceneMs) { + failures.push(`scene ${sceneMs}ms > budget ${sample.maxSceneMs}ms`); + } + if (!Number.isFinite(sceneMs)) { + failures.push('missing scene phase timing'); + } + if (!Number.isFinite(benchmark?.archiveFileCount) || benchmark.archiveFileCount <= 0) { + failures.push('archive file count is empty'); + } + if (!Number.isFinite(benchmark?.contentLength) || benchmark.contentLength <= 0) { + failures.push('USD root layer content is empty'); + } + + return { + id: sample.id, + label: sample.label, + exportName: sample.exportName, + budgets: { + maxTotalMs: sample.maxTotalMs, + maxSceneMs: sample.maxSceneMs, + }, + pass: failures.length === 0, + failures, + consoleErrorCount: consoleErrors.length, + consoleErrors, + ...benchmark, + }; +} + +async function stopStartedProcess(startedProcess) { + if (!startedProcess) { + return; + } + + if (startedProcess.exitCode != null || startedProcess.signalCode != null) { + return; + } + + startedProcess.kill('SIGTERM'); + await delay(500); +} + +async function main() { + const options = parseArgs(process.argv.slice(2)); + const samples = await resolveSamples(options); + const site = await ensureSiteAvailable(options); + const executablePath = resolveExecutablePath(options.chromePath); + const browser = await puppeteer.launch({ + headless: !options.headed, + ...(executablePath ? { executablePath } : {}), + }); + + const results = []; + + try { + await warmupSourceImports(browser, options.siteUrl, options.timeoutMs); + + for (const sample of samples) { + process.stdout.write(`[usd-export-bench] sample=${sample.id}\n`); + let result = null; + + for (let attempt = 0; attempt < 2; attempt += 1) { + const page = await openFreshPage(browser, options.siteUrl, options.timeoutMs); + const consoleErrors = createRingBuffer(50); + + page.on('console', (message) => { + if (message.type() === 'error') { + consoleErrors.push(message.text()); + } + }); + + try { + await assertSourceImportsAvailable(page); + + const benchmark = await benchmarkSample(page, sample); + result = buildSampleResult(sample, benchmark, consoleErrors.snapshot()); + if (benchmark.runtimeError) { + result.failures.unshift(`runtime error: ${benchmark.runtimeError}`); + result.pass = false; + } + } catch (error) { + result = buildSampleResult( + sample, + { + totalMs: NaN, + phaseMs: {}, + progressEventCount: 0, + archiveFileCount: 0, + rootLayerPath: null, + contentLength: 0, + linkCount: 0, + jointCount: 0, + inputModelCount: sample.inputs.length, + uniqueMeshCount: 0, + assetFileCount: sample.inputs.reduce( + (count, input) => count + input.assetDescriptors.length, + 0, + ), + runtimeError: error instanceof Error ? error.message : String(error), + }, + consoleErrors.snapshot(), + ); + result.failures.unshift(`runtime error: ${result.runtimeError}`); + result.pass = false; + + if (!isRetryableRuntimeError(result.runtimeError) || attempt === 1) { + break; + } + + await delay(750); + continue; + } finally { + await page.close().catch(() => {}); + } + + break; + } + + results.push(result); + process.stdout.write( + `[usd-export-bench] total=${Number.isFinite(result.totalMs) ? result.totalMs : 'n/a'}ms ` + + `scene=${result.phaseMs?.scene ?? 'n/a'}ms pass=${result.pass}\n`, + ); + } + } finally { + await browser.close().catch(() => {}); + await stopStartedProcess(site.startedProcess); + } + + const passCount = results.filter((result) => result.pass).length; + const summary = { + generatedAtUtc: new Date().toISOString(), + workspace: process.cwd(), + siteUrl: options.siteUrl, + sampleCount: results.length, + passCount, + failCount: results.length - passCount, + samples: results, + }; + + await writeJsonAtomic(options.outputPath, summary); + process.stdout.write(`[usd-export-bench] output=${options.outputPath}\n`); + + if (options.assertThresholds && summary.failCount > 0) { + fail( + `USD export benchmark regression detected: ${JSON.stringify( + results + .filter((result) => !result.pass) + .map((result) => ({ + id: result.id, + failures: result.failures, + })), + null, + 2, + )}`, + ); + } +} + +main().catch((error) => { + console.error(error instanceof Error ? error.stack || error.message : error); + process.exitCode = 1; +}); diff --git a/scripts/regression/validate_unitree_selected_browser.mjs b/scripts/regression/validate_unitree_selected_browser.mjs index d8310b477..0d85c8c34 100755 --- a/scripts/regression/validate_unitree_selected_browser.mjs +++ b/scripts/regression/validate_unitree_selected_browser.mjs @@ -86,10 +86,18 @@ async function ensureSite() { throw new Error(`Timed out waiting for preview at ${SITE_BASE_URL}`); } +function hasResolvedRobotData(result) { + return ( + result?.workerResolveEntry?.status === 'resolved' || + result?.runtimeResolveEntry?.status === 'resolved' + ); +} + function validateResult(result) { return Boolean( result?.loaded === true && result?.runtimePresent === true && + hasResolvedRobotData(result) && result?.stageReady === true && result?.stagePreparationMode === 'worker' && result?.metadataSourcePass === true, @@ -103,6 +111,8 @@ function summarizeFailures(report) { sampleId: result.sampleId, loaded: result.loaded, runtimePresent: result.runtimePresent, + workerResolveStatus: result.workerResolveEntry?.status ?? null, + runtimeResolveStatus: result.runtimeResolveEntry?.status ?? null, stageReady: result.stageReady, stagePreparationMode: result.stagePreparationMode, metadataSource: result.metadataSource, diff --git a/skills-lock.json b/skills-lock.json new file mode 100644 index 000000000..a2533026f --- /dev/null +++ b/skills-lock.json @@ -0,0 +1,40 @@ +{ + "version": 1, + "skills": { + "deploy-to-vercel": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "03e0eaaa9bf13ba1e7ffa387f5893de6f324c0868c627001f179395a8feaa7c9" + }, + "vercel-cli-with-tokens": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "1e1664e75dcab248050faac99db1aaef1456b7945266215ccbab68ddf85b2b79" + }, + "vercel-composition-patterns": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "575757e3e25761c8c562d6e395d29f0b76c98b1273c0bd72d88e6ab1bc9c7d42" + }, + "vercel-react-best-practices": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "43ca6c197f8ec13055079da7053232d477068b03ca443f9ac1bc819b95cdd432" + }, + "vercel-react-native-skills": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "41d24eafa7c3d82e270439808f7cfbc4d51aeb2d14f2809a2267c16275784d06" + }, + "vercel-react-view-transitions": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "c8952fc9127fa0564d0cb71dccb4215a5608ac933b5831104f09173a97a46f85" + }, + "web-design-guidelines": { + "source": "vercel-labs/agent-skills", + "sourceType": "github", + "computedHash": "f3bc47f890f42a44db1007ab390709ec368e4b8c089baee6b0007182236ac474" + } + } +} diff --git a/snapshot-default-page.png b/snapshot-default-page.png new file mode 100644 index 000000000..25c4ca71b Binary files /dev/null and b/snapshot-default-page.png differ diff --git a/src/app/App.tsx b/src/app/App.tsx index e3b516d93..f5dd83397 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -8,7 +8,6 @@ import { Providers } from './Providers'; import { AppLayout } from './AppLayout'; import { SettingsModal } from './components/SettingsModal'; import { LazyOverlayFallback } from './components/LazyOverlayFallback'; -import { ImportPreparationOverlay } from './components/ImportPreparationOverlay'; import { useAppShellState, useFileImport, @@ -21,13 +20,22 @@ import { resolveCurrentUsdExportMode } from './utils/currentUsdExportMode'; import { buildRobotLoadSupportContextKey, preserveDocumentLoadProgressForSameFile, + shouldReuseResolvedMjcfViewerRuntime, shouldCommitResolvedRobotSelection, shouldSkipRedundantRobotReload, } from './utils/documentLoadFlow'; import { peekPreResolvedRobotImport } from './utils/preResolvedRobotImportCache'; import { prewarmUsdSelectionInBackground } from './utils/usdSelectionPrewarm'; import { resolveAppModeAfterRobotContentChange } from './utils/contentChangeAppMode'; -import { buildStandaloneImportAssetWarning } from './utils/importPackageAssetReferences'; +import { + mapRobotImportProgressToDocumentLoadPercent, + resolveBootstrapDocumentLoadPhase, + resolveRobotImportCompletedDocumentLoadPercent, +} from './utils/documentLoadProgress'; +import { + buildStandaloneImportAssetWarning, + collectStandaloneImportSupportAssetPaths, +} from './utils/importPackageAssetReferences'; import { useRobotStore, useUIStore, @@ -35,30 +43,88 @@ import { useAssetsStore, useAssemblyStore, } from '@/store'; -import type { RobotFile, RobotState, UrdfLink, UrdfJoint } from '@/types'; +import type { InspectionReport, RobotFile, RobotState } from '@/types'; +import type { HeaderAction } from './components/header/types'; + +/** Render slots: allows external repos to inject extra modals and overlays */ +export interface AppExtensionSlots { + /** Rendered after core built-in modals, before toast */ + renderModals?: () => React.ReactNode; + /** Rendered after toast (highest z-index layer) */ + renderTopOverlays?: () => React.ReactNode; +} + +/** Config extension: allows external repos to inject header actions etc. */ +export interface AppExtensionConfig { + headerQuickAction?: HeaderAction; + headerSecondaryAction?: HeaderAction; +} + +/** Core internal actions exposed to external consumers */ +export interface AppExposedActions { + importFiles: (files: FileList | File[]) => void; + openLibraryExport: (file: RobotFile) => void; + openAIInspection: () => void; + openAIConversation: () => void; + openIkTool: () => void; + openCollisionOptimizer: () => void; + openTool: (key: string) => void; + exportProjectBlob: () => Promise; +} + +interface AppContentProps { + extensions?: { + slots?: AppExtensionSlots; + config?: AppExtensionConfig; + }; + /** Core calls this on mount to expose internal handlers to the external host */ + onExposeActions?: (actions: AppExposedActions) => void; +} import type { RobotImportResult } from '@/core/parsers/importRobotFile'; +import { resolveMJCFSource } from '@/core/parsers/mjcf/mjcfSourceResolver'; import { translations, type Language } from '@/shared/i18n'; -import { isLibraryRobotExportableFormat } from '@/shared/utils'; +import { isAssetLibraryOnlyFormat, isLibraryRobotExportableFormat } from '@/shared/utils'; import type { ExportDialogConfig } from '@/features/file-io/components/ExportDialog/ExportDialog'; import type { ExportProgressState } from '@/features/file-io/types'; -import { getUsdStageExportHandler } from '@/features/urdf-viewer/utils/usdStageExport'; +import { getUsdStageExportHandler } from '@/features/editor'; import type { ImportPreparationOverlayState } from './hooks/useFileImport'; +import { consumeHandoffImportFromUrl } from './handoff/bootstrap'; +import { + deletePendingHandoffImport, + pruneExpiredPendingHandoffImports, + readPendingHandoffImport, +} from './handoff/storage'; import { installRegressionDebugApi, setRegressionAppHandlers, setRegressionBeforeUnloadPromptSuppressed, } from '@/shared/debug/regressionBridge'; import { markUnsavedChangesBaselineSaved } from './utils/unsavedChangesBaseline'; +import type { + AIConversationFocusedIssue, + AIConversationLaunchContext, + AIConversationMode, + AIConversationSelection, +} from '@/features/ai-assistant/types'; import { toDocumentLoadLifecycleState } from '@/store/assetsStore'; -const loadAIModalModule = () => import('@/features/ai-assistant'); +const loadAIInspectionModalModule = () => + import('@/features/ai-assistant/components/AIInspectionModal'); +const loadAIConversationModalModule = () => + import('@/features/ai-assistant/components/AIConversationModal'); const loadExportDialogModule = () => import('@/features/file-io'); const loadDisconnectedWorkspaceUrdfExportDialogModule = () => import('@/features/file-io/components/DisconnectedWorkspaceUrdfExportDialog'); const loadExportProgressDialogModule = () => import('@/features/file-io/components/ExportProgressDialog'); -const AIModal = lazy(() => loadAIModalModule().then((module) => ({ default: module.AIModal }))); +const AIInspectionModal = lazy(() => + loadAIInspectionModalModule().then((module) => ({ default: module.AIInspectionModal })), +); + +const AIConversationModal = lazy(() => + loadAIConversationModalModule().then((module) => ({ default: module.AIConversationModal })), +); const DisconnectedWorkspaceUrdfExportDialog = lazy(() => loadDisconnectedWorkspaceUrdfExportDialogModule().then((module) => ({ default: module.DisconnectedWorkspaceUrdfExportDialog, @@ -74,76 +140,109 @@ const ExportDialog = lazy(() => loadExportDialogModule().then((module) => ({ default: module.ExportDialog })), ); -interface AIApplyChangesPayload { - name?: string; - links?: Record; - joints?: Record; - rootLinkId?: string; +function cloneAISnapshot(value: T): T { + if (typeof structuredClone === 'function') { + return structuredClone(value); + } + + return JSON.parse(JSON.stringify(value)) as T; } -function validateAIApplyPayload(data: AIApplyChangesPayload): - | { - ok: true; - value: Required> & - Pick; - } - | { ok: false; reason: 'aiNoDataToApply' | 'aiNoLinksGenerated' } { - if (!data || typeof data !== 'object') { - return { ok: false, reason: 'aiNoDataToApply' }; +function resolveConversationSelectedEntity(robotSnapshot: RobotState) { + if (!robotSnapshot.selection.type || !robotSnapshot.selection.id) { + return null; } - if (!data.links || Object.keys(data.links).length === 0) { - return { ok: false, reason: 'aiNoLinksGenerated' }; + if (robotSnapshot.selection.type !== 'link' && robotSnapshot.selection.type !== 'joint') { + return null; } - if (!data.joints || typeof data.joints !== 'object') { - return { ok: false, reason: 'aiNoDataToApply' }; - } + return { + type: robotSnapshot.selection.type, + id: robotSnapshot.selection.id, + }; +} - if (!data.rootLinkId || !data.links[data.rootLinkId]) { - return { ok: false, reason: 'aiNoDataToApply' }; - } +function createConversationLaunchContext({ + sessionId, + mode, + robotSnapshot, + inspectionReportSnapshot = null, + selectedEntity = null, + focusedIssue = null, +}: { + sessionId: number; + mode: AIConversationMode; + robotSnapshot: RobotState; + inspectionReportSnapshot?: InspectionReport | null; + selectedEntity?: AIConversationSelection | null; + focusedIssue?: AIConversationFocusedIssue | null; +}): AIConversationLaunchContext { + const nextRobotSnapshot = cloneAISnapshot(robotSnapshot); + const nextFocusedIssue = focusedIssue ? cloneAISnapshot(focusedIssue) : null; return { - ok: true, - value: { - name: data.name, - links: data.links, - joints: data.joints, - rootLinkId: data.rootLinkId, - }, + sessionId, + mode, + robotSnapshot: nextRobotSnapshot, + inspectionReportSnapshot: inspectionReportSnapshot + ? cloneAISnapshot(inspectionReportSnapshot) + : null, + selectedEntity: selectedEntity + ? cloneAISnapshot(selectedEntity) + : resolveConversationSelectedEntity(nextRobotSnapshot), + focusedIssue: nextFocusedIssue, }; } -function AIModalConnector({ +function AIInspectionConnector({ isOpen, onClose, lang, - onApplyChanges, + onOpenConversationWithReport, }: { isOpen: boolean; onClose: () => void; lang: Language; - onApplyChanges: (data: AIApplyChangesPayload) => void; + onOpenConversationWithReport: ( + report: InspectionReport, + robotSnapshot: RobotState, + options?: { + selectedEntity?: AIConversationSelection | null; + focusedIssue?: AIConversationFocusedIssue | null; + }, + ) => void; }) { const { sidebarTab } = useUIStore( useShallow((state) => ({ sidebarTab: state.sidebarTab, })), ); - const { selection, setSelection, focusOn } = useSelectionStore( + const { selection, setSelection, focusOn, pulseSelection } = useSelectionStore( useShallow((state) => ({ selection: state.selection, setSelection: state.setSelection, focusOn: state.focusOn, + pulseSelection: state.pulseSelection, })), ); - const { robotName, robotLinks, robotJoints, rootLinkId } = useRobotStore( + const { + robotName, + robotLinks, + robotJoints, + rootLinkId, + robotMaterials, + robotClosedLoopConstraints, + inspectionContext, + } = useRobotStore( useShallow((state) => ({ robotName: state.name, robotLinks: state.links, robotJoints: state.joints, rootLinkId: state.rootLinkId, + robotMaterials: state.materials, + robotClosedLoopConstraints: state.closedLoopConstraints, + inspectionContext: state.inspectionContext, })), ); const { assemblyState, getMergedRobotData } = useAssemblyStore( @@ -152,7 +251,6 @@ function AIModalConnector({ getMergedRobotData: state.getMergedRobotData, })), ); - const motorLibrary = useAssetsStore((state) => state.motorLibrary); const mergedWorkspaceRobot = useMemo(() => { if (!assemblyState || sidebarTab !== 'workspace') { @@ -175,22 +273,59 @@ function AIModalConnector({ links: robotLinks, joints: robotJoints, rootLinkId, + materials: robotMaterials, + closedLoopConstraints: robotClosedLoopConstraints, + inspectionContext, selection, }; - }, [mergedWorkspaceRobot, robotJoints, robotLinks, robotName, rootLinkId, selection]); + }, [ + mergedWorkspaceRobot, + robotJoints, + robotLinks, + robotName, + rootLinkId, + robotMaterials, + robotClosedLoopConstraints, + inspectionContext, + selection, + ]); return ( - { setSelection({ type, id }); + pulseSelection({ type, id }); focusOn(id); }} + onOpenConversationWithReport={onOpenConversationWithReport} + /> + ); +} + +function AIConversationConnector({ + isOpen, + onClose, + lang, + launchContext, + onStartNewConversation, +}: { + isOpen: boolean; + onClose: () => void; + lang: Language; + launchContext: AIConversationLaunchContext | null; + onStartNewConversation: (launchContext: AIConversationLaunchContext) => void; +}) { + return ( + ); } @@ -279,7 +414,35 @@ function waitForNextPaint(): Promise { type ExportDialogTarget = { type: 'current' } | { type: 'library-file'; file: RobotFile }; -function AppContent() { +function resolveCurrentAIRobotSnapshot(): RobotState { + const { sidebarTab } = useUIStore.getState(); + const { selection } = useSelectionStore.getState(); + const { assemblyState, getMergedRobotData } = useAssemblyStore.getState(); + const robotState = useRobotStore.getState(); + + if (assemblyState && sidebarTab === 'workspace') { + const mergedWorkspaceRobot = getMergedRobotData(); + if (mergedWorkspaceRobot) { + return cloneAISnapshot({ + ...mergedWorkspaceRobot, + selection, + }); + } + } + + return cloneAISnapshot({ + name: robotState.name, + links: robotState.links, + joints: robotState.joints, + rootLinkId: robotState.rootLinkId, + materials: robotState.materials, + closedLoopConstraints: robotState.closedLoopConstraints, + inspectionContext: robotState.inspectionContext, + selection, + }); +} + +export function AppContent({ extensions, onExposeActions }: AppContentProps = {}) { useUnsavedChangesPrompt(); // Refs for file inputs @@ -289,8 +452,13 @@ function AppContent() { ((file: RobotFile, options?: { forceReload?: boolean }) => Promise | void) | null >(null); const loadRequestIdRef = useRef(0); + const aiConversationSessionIdRef = useRef(0); + const handoffBootstrapStartedRef = useRef(false); + const [shouldRenderAIInspectionModal, setShouldRenderAIInspectionModal] = useState(false); + const [shouldRenderAIConversationModal, setShouldRenderAIConversationModal] = useState(false); + const [aiConversationLaunchContext, setAIConversationLaunchContext] = + useState(null); const lastLoadSupportContextKeyRef = useRef(null); - const [shouldRenderAIModal, setShouldRenderAIModal] = useState(false); const [exportDialogTarget, setExportDialogTarget] = useState({ type: 'current', }); @@ -340,8 +508,13 @@ function AppContent() { toast, closeToast, showToast, - isAIModalOpen, - setIsAIModalOpen, + isAIInspectionOpen, + setIsAIInspectionOpen, + isAIConversationOpen, + setIsAIConversationOpen, + setAILaunchMode, + openAIInspection, + openAIConversation, isCodeViewerOpen, setIsCodeViewerOpen, isExportDialogOpen, @@ -384,7 +557,8 @@ function AppContent() { ? 'checking-path' : 'preparing-scene', message: null, - progressPercent: null, + progressMode: 'percent', + progressPercent: resolveRobotImportCompletedDocumentLoadPercent(file.format), loadedCount: null, totalCount: null, }, @@ -424,11 +598,22 @@ function AppContent() { ); const commitResolvedFileSelection = useCallback( - (file: RobotFile) => { - setViewerReloadKey((value) => value + 1); + (file: RobotFile, options?: { reloadViewer?: boolean }) => { + const assetLibraryOnlyFile = isAssetLibraryOnlyFormat(file.format); + const originalFileFormat = + file.format === 'urdf' || + file.format === 'mjcf' || + file.format === 'usd' || + file.format === 'xacro' || + file.format === 'sdf' + ? file.format + : null; + if (options?.reloadViewer !== false) { + setViewerReloadKey((value) => value + 1); + } setSelectedFile(file); - setOriginalUrdfContent(file.format === 'mesh' ? '' : file.content); - setOriginalFileFormat(file.format === 'mesh' ? null : file.format); + setOriginalUrdfContent(assetLibraryOnlyFile ? '' : file.content); + setOriginalFileFormat(originalFileFormat); setSelection({ type: null, id: null }); const currentAppMode = useUIStore.getState().appMode; const nextAppMode = resolveAppModeAfterRobotContentChange(currentAppMode); @@ -468,16 +653,26 @@ function AppContent() { return; } + const importedAssetPaths = collectStandaloneImportSupportAssetPaths( + liveAssetsState.assets, + liveAssetsState.availableFiles, + ); const standaloneImportAssetWarning = buildStandaloneImportAssetWarning( file, - Object.keys(liveAssetsState.assets), + importedAssetPaths, + { + allFileContents: liveAssetsState.allFileContents, + sourcePath: file.name, + }, ); if (standaloneImportAssetWarning) { const assetLabel = standaloneImportAssetWarning.missingAssetPaths.length > 3 ? `${standaloneImportAssetWarning.missingAssetPaths.slice(0, 3).join(', ')}, …` : standaloneImportAssetWarning.missingAssetPaths.join(', '); - const message = t.importPackageAssetBundleHint.replace('{assets}', assetLabel); + const message = t.importPackageAssetBundleHint + .replace('{packages}', assetLabel) + .replace('{assets}', assetLabel); setDocumentLoadState({ status: 'error', fileName: file.name, @@ -488,6 +683,31 @@ function AppContent() { return; } + const currentResolvedMjcfSource = + currentSelectedFile?.format === 'mjcf' + ? resolveMJCFSource(currentSelectedFile, liveAssetsState.availableFiles) + : null; + const nextResolvedMjcfSource = + file.format === 'mjcf' ? resolveMJCFSource(file, liveAssetsState.availableFiles) : null; + const shouldReloadViewer = + options?.forceReload || + !shouldReuseResolvedMjcfViewerRuntime({ + currentSelectedFile, + nextFile: file, + currentResolvedSource: currentResolvedMjcfSource + ? { + effectiveFileName: currentResolvedMjcfSource.effectiveFile.name, + content: currentResolvedMjcfSource.content, + } + : null, + nextResolvedSource: nextResolvedMjcfSource + ? { + effectiveFileName: nextResolvedMjcfSource.effectiveFile.name, + content: nextResolvedMjcfSource.content, + } + : null, + }); + setDocumentLoadState( preserveDocumentLoadProgressForSameFile({ currentState: liveAssetsState.documentLoadState, @@ -496,9 +716,10 @@ function AppContent() { fileName: file.name, format: file.format, error: null, - phase: file.format === 'usd' ? 'checking-path' : 'preparing-scene', + phase: resolveBootstrapDocumentLoadPhase(file.format), message: null, - progressPercent: null, + progressMode: 'percent', + progressPercent: 0, loadedCount: null, totalCount: null, }, @@ -516,17 +737,70 @@ function AppContent() { if (shouldCommitResolvedRobotSelection(preResolvedImportResult)) { lastLoadSupportContextKeyRef.current = nextLoadSupportContextKey; - commitResolvedFileSelection(file); + commitResolvedFileSelection(file, { reloadViewer: shouldReloadViewer }); } applyResolvedRobotImport(file, preResolvedImportResult); + if ( + !shouldReloadViewer && + preResolvedImportResult.status === 'ready' && + file.format === 'mjcf' + ) { + setDocumentLoadState({ + status: 'ready', + fileName: file.name, + format: file.format, + error: null, + phase: 'ready', + message: null, + progressMode: 'percent', + progressPercent: 100, + loadedCount: null, + totalCount: null, + }); + } return; } - const importResultPromise = resolveRobotFileDataWithWorker(file, { - availableFiles: liveAssetsState.availableFiles, - assets: liveAssetsState.assets, - usdRobotData: liveAssetsState.getUsdPreparedExportCache(file.name)?.robotData ?? null, - }); + const importResultPromise = resolveRobotFileDataWithWorker( + file, + { + availableFiles: liveAssetsState.availableFiles, + assets: liveAssetsState.assets, + usdRobotData: liveAssetsState.getUsdPreparedExportCache(file.name)?.robotData ?? null, + }, + { + onProgress: (progress) => { + if (requestId !== loadRequestIdRef.current) { + return; + } + + const currentDocumentLoadState = useAssetsStore.getState().documentLoadState; + const mappedProgressPercent = mapRobotImportProgressToDocumentLoadPercent( + file.format, + progress, + ); + const nextProgressPercent = + currentDocumentLoadState.fileName === file.name && + (currentDocumentLoadState.status === 'loading' || + currentDocumentLoadState.status === 'hydrating') + ? Math.max(currentDocumentLoadState.progressPercent ?? 0, mappedProgressPercent) + : mappedProgressPercent; + + setDocumentLoadState({ + status: 'loading', + fileName: file.name, + format: file.format, + error: null, + phase: resolveBootstrapDocumentLoadPhase(file.format), + message: progress.message ?? null, + progressMode: 'percent', + progressPercent: nextProgressPercent, + loadedCount: null, + totalCount: null, + }); + }, + }, + ); await waitForNextPaint(); @@ -558,9 +832,23 @@ function AppContent() { if (shouldCommitResolvedRobotSelection(importResult)) { lastLoadSupportContextKeyRef.current = nextLoadSupportContextKey; - commitResolvedFileSelection(file); + commitResolvedFileSelection(file, { reloadViewer: shouldReloadViewer }); } applyResolvedRobotImport(file, importResult); + if (!shouldReloadViewer && importResult.status === 'ready' && file.format === 'mjcf') { + setDocumentLoadState({ + status: 'ready', + fileName: file.name, + format: file.format, + error: null, + phase: 'ready', + message: null, + progressMode: 'percent', + progressPercent: 100, + loadedCount: null, + totalCount: null, + }); + } }, [ applyResolvedRobotImport, @@ -598,6 +886,7 @@ function AppContent() { setRegressionAppHandlers({ getAvailableFiles: () => useAssetsStore.getState().availableFiles, getSelectedFile: () => useAssetsStore.getState().selectedFile, + getDocumentLoadState: () => useAssetsStore.getState().documentLoadState, getRobotState: () => ({ name: useRobotStore.getState().name, links: useRobotStore.getState().links, @@ -706,33 +995,44 @@ function AppContent() { ]); // AI changes handler - const handleApplyAIChanges = useCallback( - (data: AIApplyChangesPayload) => { - const validated = validateAIApplyPayload(data); - if (validated.ok === false) { - showToast(t[validated.reason], 'info'); - return; - } - - const currentRobot = useRobotStore.getState(); - setRobot({ - name: validated.value.name?.trim() || currentRobot.name, - links: validated.value.links, - joints: validated.value.joints, - rootLinkId: validated.value.rootLinkId, - }); - setAppMode(resolveAppModeAfterRobotContentChange(useUIStore.getState().appMode)); - }, - [setAppMode, setRobot, showToast, t], - ); - useImportInputBinding({ importInputRef, importFolderInputRef, onImport: handleImport, }); - const handleOpenAIModal = useCallback(() => { + useEffect(() => { + if (typeof window === 'undefined') { + return; + } + + void pruneExpiredPendingHandoffImports().catch((error) => { + console.error('Failed to prune expired handoff imports:', error); + }); + + if (handoffBootstrapStartedRef.current) { + return; + } + + handoffBootstrapStartedRef.current = true; + + void consumeHandoffImportFromUrl({ + currentUrl: window.location.href, + sessionStorage: window.sessionStorage, + loadRecord: readPendingHandoffImport, + deleteRecord: deletePendingHandoffImport, + importArchive: (files) => handleImport(files), + replaceUrl: (nextUrl) => { + const currentUrl = window.location.href; + if (nextUrl !== currentUrl) { + window.history.replaceState(window.history.state, '', nextUrl); + } + }, + logger: console, + }); + }, [handleImport]); + + const ensureAIEntryAvailable = useCallback(() => { const liveAssetsState = useAssetsStore.getState(); const currentSelectedFile = liveAssetsState.selectedFile; const currentDocumentLoadState = liveAssetsState.documentLoadState; @@ -743,12 +1043,122 @@ function AppContent() { if (isSelectedUsdHydrating) { showToast(t.usdLoadInProgress, 'info'); + return false; + } + return true; + }, [showToast, t.usdLoadInProgress]); + + const createConversationLaunchContextFromSnapshot = useCallback( + ( + mode: AIConversationMode, + robotSnapshot: RobotState, + inspectionReportSnapshot: InspectionReport | null = null, + options: { + selectedEntity?: AIConversationSelection | null; + focusedIssue?: AIConversationFocusedIssue | null; + } = {}, + ) => { + aiConversationSessionIdRef.current += 1; + return createConversationLaunchContext({ + sessionId: aiConversationSessionIdRef.current, + mode, + robotSnapshot, + inspectionReportSnapshot, + selectedEntity: options.selectedEntity, + focusedIssue: options.focusedIssue, + }); + }, + [], + ); + + const handleOpenAIInspection = useCallback(() => { + if (!ensureAIEntryAvailable()) { return; } - setShouldRenderAIModal(true); - void loadAIModalModule(); - setIsAIModalOpen(true); - }, [setIsAIModalOpen, showToast, t.usdLoadInProgress]); + + setShouldRenderAIInspectionModal(true); + void loadAIInspectionModalModule(); + openAIInspection(); + }, [ensureAIEntryAvailable, openAIInspection]); + + const handleOpenAIConversation = useCallback(() => { + if (!ensureAIEntryAvailable()) { + return; + } + + if (aiConversationLaunchContext?.mode === 'general') { + setShouldRenderAIConversationModal(true); + void loadAIConversationModalModule(); + openAIConversation(); + return; + } + + const launchContext = createConversationLaunchContextFromSnapshot( + 'general', + resolveCurrentAIRobotSnapshot(), + ); + + setAIConversationLaunchContext(launchContext); + setShouldRenderAIConversationModal(true); + void loadAIConversationModalModule(); + openAIConversation(); + }, [ + aiConversationLaunchContext, + createConversationLaunchContextFromSnapshot, + ensureAIEntryAvailable, + openAIConversation, + ]); + + const handleOpenConversationWithReport = useCallback( + ( + report: InspectionReport, + robotSnapshot: RobotState, + options: { + selectedEntity?: AIConversationSelection | null; + focusedIssue?: AIConversationFocusedIssue | null; + } = {}, + ) => { + if (!ensureAIEntryAvailable()) { + return; + } + + const launchContext = createConversationLaunchContextFromSnapshot( + 'inspection-followup', + robotSnapshot, + report, + options, + ); + + setAIConversationLaunchContext(launchContext); + setShouldRenderAIConversationModal(true); + void loadAIConversationModalModule(); + setIsAIConversationOpen(true); + setAILaunchMode('conversation'); + }, + [ + createConversationLaunchContextFromSnapshot, + ensureAIEntryAvailable, + setAILaunchMode, + setIsAIConversationOpen, + ], + ); + + const handleStartNewAIConversation = useCallback( + (currentLaunchContext: AIConversationLaunchContext) => { + const nextLaunchContext = createConversationLaunchContextFromSnapshot( + currentLaunchContext.mode, + currentLaunchContext.robotSnapshot, + currentLaunchContext.inspectionReportSnapshot ?? null, + { + selectedEntity: currentLaunchContext.selectedEntity, + focusedIssue: currentLaunchContext.focusedIssue, + }, + ); + + setAIConversationLaunchContext(nextLaunchContext); + }, + [createConversationLaunchContextFromSnapshot], + ); const handleOpenExportDialog = useCallback(() => { void loadExportDialogModule(); @@ -765,6 +1175,34 @@ function AppContent() { [setIsExportDialogOpen], ); + // Expose internal actions to external consumers (ref keeps the reference fresh) + const layoutActionsRef = useRef<{ + openIkTool: () => void; + openCollisionOptimizer: () => void; + openTool: (key: string) => void; + }>({ openIkTool: () => {}, openCollisionOptimizer: () => {}, openTool: () => {} }); + + const handleExportProjectBlob = useCallback(async (): Promise => { + const result = await runProjectExport({ skipDownload: true }); + return result.blob; + }, [runProjectExport]); + + const exposedActionsRef = useRef(null); + exposedActionsRef.current = { + importFiles: handleImport, + openLibraryExport: handleOpenLibraryExportDialog, + openAIInspection: handleOpenAIInspection, + openAIConversation: handleOpenAIConversation, + openIkTool: () => layoutActionsRef.current.openIkTool(), + openCollisionOptimizer: () => layoutActionsRef.current.openCollisionOptimizer(), + openTool: (key: string) => layoutActionsRef.current.openTool(key), + exportProjectBlob: handleExportProjectBlob, + }; + + useEffect(() => { + onExposeActions?.(exposedActionsRef.current!); + }, [onExposeActions]); + const handleConfirmDisconnectedWorkspaceUrdfExport = useCallback(async () => { if (!disconnectedWorkspaceUrdfDialog) { return; @@ -817,12 +1255,15 @@ function AppContent() { handleImport(files as any)} + onFileDrop={(files) => { + void handleImport(files); + }} onOpenExport={handleOpenExportDialog} onOpenLibraryExport={handleOpenLibraryExportDialog} onExportProject={handleExportProject} showToast={showToast} - onOpenAI={handleOpenAIModal} + onOpenAIInspection={handleOpenAIInspection} + onOpenAIConversation={handleOpenAIConversation} isCodeViewerOpen={isCodeViewerOpen} setIsCodeViewerOpen={setIsCodeViewerOpen} onOpenSettings={() => openSettings()} @@ -830,20 +1271,39 @@ function AppContent() { setViewConfig={setViewConfig} onLoadRobot={handleLoadRobot} viewerReloadKey={viewerReloadKey} + importPreparationOverlay={importPreparationOverlay} + headerQuickAction={extensions?.config?.headerQuickAction} + headerSecondaryAction={extensions?.config?.headerSecondaryAction} + onExposeLayoutActions={(actions) => { + layoutActionsRef.current = actions; + }} /> {/* Modals */} - {shouldRenderAIModal && ( + {shouldRenderAIInspectionModal && ( + }> + {/* Keep the modal mounted after first open so inspection results survive close/reopen. */} + { + setIsAIInspectionOpen(false); + }} + lang={lang} + onOpenConversationWithReport={handleOpenConversationWithReport} + /> + + )} + {shouldRenderAIConversationModal && ( }> - { - setIsAIModalOpen(false); - setShouldRenderAIModal(false); + setIsAIConversationOpen(false); }} lang={lang} - onApplyChanges={handleApplyAIChanges} + launchContext={aiConversationLaunchContext} + onStartNewConversation={handleStartNewAIConversation} /> )} @@ -926,15 +1386,8 @@ function AppContent() { )} - {importPreparationOverlay && ( - - )} + {/* Extension slot: external modal layer */} + {extensions?.slots?.renderModals?.()} {/* Toast */} {toast.show && ( @@ -971,6 +1424,9 @@ function AppContent() { )} + + {/* Extension slot: top overlay layer (highest z-index) */} + {extensions?.slots?.renderTopOverlays?.()} ); } diff --git a/src/app/AppLayout.tsx b/src/app/AppLayout.tsx index 839dbf615..b9fc31267 100644 --- a/src/app/AppLayout.tsx +++ b/src/app/AppLayout.tsx @@ -5,6 +5,7 @@ import React, { useRef, useCallback, useEffect, useMemo, useState } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { Header } from './components/Header'; +import { IkToolPanel } from './components/IkToolPanel'; import { AppLayoutOverlays } from './components/AppLayoutOverlays'; import { ConnectedDocumentLoadingOverlay } from './components/ConnectedDocumentLoadingOverlay'; import { FileDropOverlay } from './components/FileDropOverlay'; @@ -17,45 +18,35 @@ import { } from './utils/overlayLoaders'; import { preloadSourceCodeEditorRuntime } from './utils/sourceCodeEditorLoader'; import type { HeaderAction } from './components/header/types'; +import { setOptionsPanelVisibility } from './components/header/viewMenuState.js'; import { TreeEditor } from '@/features/robot-tree'; import { PropertyEditor } from '@/features/property-editor'; -import { - getCurrentUsdViewerSceneSnapshot, - prepareUsdExportCacheFromSnapshot, - prepareUsdPreparedExportCacheWithWorker, - resolveUsdExportResolution, - type ToolMode, - type ViewerDocumentLoadEvent, - type ViewerRobotDataResolution, -} from '@/features/urdf-viewer'; +import { type ToolMode } from '@/features/editor'; import { useAppLayoutEffects, useAssemblyComponentPreparation, useCollisionOptimizationWorkflow, + useEditableSourceCodeApply, useEditableSourcePatches, useLibraryFileActions, + usePreviewFileWithFeedback, usePreparedUsdViewerAssets, useSourceCodeEditorWarmup, + useToolItems, + useUsdDocumentLifecycle, + useWorkspaceAssemblyRenderFailureNotice, useViewerOrchestration, useWorkspaceMutations, useWorkspaceOverlayActions, useWorkspaceModeTransitions, useWorkspaceSourceSync, + useWorkspaceViewerSelectionBridge, } from './hooks'; import { - parseEditableRobotSourceWithWorker, - resolveRobotFileDataWithWorker, -} from './hooks/robotImportWorkerBridge'; -import { - createGeneratedWorkspaceUrdfFile, - createRobotSourceSnapshot, getViewerSourceFile, - isGeneratedWorkspaceUrdfFileName, - resolveWorkspaceGeneratedUrdfRobotData, - shouldPromptGenerateWorkspaceUrdfOnStructureSwitch, - shouldReseedSingleComponentAssemblyFromActiveFile, shouldUseEmptyRobotForUsdHydration, } from './hooks/workspaceSourceSyncUtils'; +import type { ImportPreparationOverlayState } from './hooks/useFileImport'; import { useUIStore, useSelectionStore, @@ -65,68 +56,25 @@ import { useAssemblySelectionStore, useCollisionTransformStore, } from '@/store'; -import { generateURDF } from '@/core/parsers'; -import { analyzeAssemblyConnectivity } from '@/core/robot'; -import { buildExportableAssemblyRobotData } from '@/core/robot/assemblyTransforms'; -import { rewriteRobotMeshPathsForSource } from '@/core/parsers/meshPathUtils'; -import type { - BridgeJoint, - InteractionSelection, - RobotData, - RobotFile, - UrdfJoint, - UrdfLink, - UsdSceneSnapshot, -} from '@/types'; +import type { BridgeJoint, RobotFile, UrdfJoint, UrdfLink } from '@/types'; import { translations } from '@/shared/i18n'; import type { SnapshotCaptureOptions } from '@/shared/components/3d'; import { normalizeMergedAppMode } from '@/shared/utils/appMode'; -import { ROBOT_IMPORT_ACCEPT_ATTRIBUTE } from '@/shared/utils'; -import { createRobotSemanticSnapshot } from '@/shared/utils/robot/semanticSnapshot'; -import { recordUsdStageLoadDebug } from '@/shared/debug/usdStageLoadDebug'; -import { registerPendingUsdCacheFlusher } from './utils/pendingUsdCache'; -import { shouldApplyUsdStageHydration } from './utils/usdStageHydration'; -import { buildUsdHydrationPersistencePlan } from './utils/usdHydrationPersistence'; -import { - shouldIgnoreStaleViewerDocumentLoadEvent, - shouldIgnoreViewerLoadRegressionAfterReadySameFile, -} from './utils/documentLoadFlow'; -import { scheduleFailFastInDev } from '@/core/utils/runtimeDiagnostics'; -import type { DocumentLoadState, DocumentLoadStatus } from '@/store/assetsStore'; +import { hasSingleDofJoints } from '@/shared/utils/jointTypes'; +import { isAssetLibraryOnlyFormat, ROBOT_IMPORT_ACCEPT_ATTRIBUTE } from '@/shared/utils'; import { toDocumentLoadLifecycleState } from '@/store/assetsStore'; -import { BRIDGE_PREVIEW_ID, resolveAssemblyViewerComponentSelection } from '@/features/assembly'; import { markUnsavedChangesBaselineSaved } from './utils/unsavedChangesBaseline'; -import { buildStandaloneImportAssetWarning } from './utils/importPackageAssetReferences'; import { buildPropertyEditorSelectionContext } from './utils/propertyEditorSelectionContext'; - -interface UsdPersistenceBaseline { - fileName: string | null; - robotSnapshot: string | null; - fallbackSceneSnapshot: UsdSceneSnapshot | null; - hadPreparedExportCache: boolean; - hadSceneSnapshot: boolean; -} - -const EMPTY_USD_PERSISTENCE_BASELINE: UsdPersistenceBaseline = { - fileName: null, - robotSnapshot: null, - fallbackSceneSnapshot: null, - hadPreparedExportCache: false, - hadSceneSnapshot: false, -}; +import { resolveDocumentLoadingOverlayTargetFileName } from './utils/documentLoadProgress'; +import { clearIkDragHelperSelection } from './utils/ikDragSession'; +import { resolveIkToolSelectionState } from './utils/ikToolSelectionState'; +import { resolveAssemblyRootComponentSelectionAvailability } from './utils/assemblyRootComponentSelection'; interface ProModeRoundtripSession { baselineSnapshot: string; generatedFileName: string | null; } -function normalizeUsdPersistenceFileName(path: string | null | undefined): string { - return String(path || '') - .trim() - .replace(/^\/+/, '') - .split('?')[0]; -} - interface AppLayoutProps { // Import handlers (passed from App) importInputRef: React.RefObject; @@ -138,7 +86,8 @@ interface AppLayoutProps { // Toast handler showToast: (message: string, type?: 'info' | 'success') => void; // Modal handlers - onOpenAI: () => void; + onOpenAIInspection: () => void; + onOpenAIConversation: () => void; isCodeViewerOpen: boolean; setIsCodeViewerOpen: (open: boolean) => void; onOpenSettings: () => void; @@ -148,20 +97,25 @@ interface AppLayoutProps { viewConfig: { showToolbar: boolean; showOptionsPanel: boolean; - showVisualizerOptionsPanel: boolean; showJointPanel: boolean; }; setViewConfig: React.Dispatch< React.SetStateAction<{ showToolbar: boolean; showOptionsPanel: boolean; - showVisualizerOptionsPanel: boolean; showJointPanel: boolean; }> >; // Robot file handling onLoadRobot: (file: RobotFile) => void; viewerReloadKey: number; + importPreparationOverlay?: ImportPreparationOverlayState | null; + /** Called once layout handlers are ready, so the parent can expose them externally */ + onExposeLayoutActions?: (actions: { + openIkTool: () => void; + openCollisionOptimizer: () => void; + openTool: (key: string) => void; + }) => void; } export function AppLayout({ @@ -172,7 +126,8 @@ export function AppLayout({ onOpenLibraryExport, onExportProject, showToast, - onOpenAI, + onOpenAIInspection, + onOpenAIConversation, isCodeViewerOpen, setIsCodeViewerOpen, onOpenSettings, @@ -182,20 +137,31 @@ export function AppLayout({ setViewConfig, onLoadRobot, viewerReloadKey, + importPreparationOverlay = null, + onExposeLayoutActions, }: AppLayoutProps) { // UI Store (grouped with useShallow to reduce subscriptions) - const { appMode, lang, theme, sidebar, toggleSidebar, sidebarTab, sourceCodeAutoApply } = - useUIStore( - useShallow((state) => ({ - appMode: state.appMode, - lang: state.lang, - theme: state.theme, - sidebar: state.sidebar, - toggleSidebar: state.toggleSidebar, - sidebarTab: state.sidebarTab, - sourceCodeAutoApply: state.sourceCodeAutoApply, - })), - ); + const { + appMode, + lang, + theme, + sidebar, + toggleSidebar, + sidebarTab, + sourceCodeAutoApply, + setViewOption, + } = useUIStore( + useShallow((state) => ({ + appMode: state.appMode, + lang: state.lang, + theme: state.theme, + sidebar: state.sidebar, + toggleSidebar: state.toggleSidebar, + sidebarTab: state.sidebarTab, + sourceCodeAutoApply: state.sourceCodeAutoApply, + setViewOption: state.setViewOption, + })), + ); const mergedAppMode = normalizeMergedAppMode(appMode); const t = translations[lang]; @@ -315,7 +281,6 @@ export function AppLayout({ removeComponent, addBridge, removeBridge, - getMergedRobotData, updateComponentName, updateComponentTransform, updateComponentRobot, @@ -330,7 +295,6 @@ export function AppLayout({ removeComponent: state.removeComponent, addBridge: state.addBridge, removeBridge: state.removeBridge, - getMergedRobotData: state.getMergedRobotData, updateComponentName: state.updateComponentName, updateComponentTransform: state.updateComponentTransform, updateComponentRobot: state.updateComponentRobot, @@ -343,17 +307,16 @@ export function AppLayout({ ((options?: Partial) => Promise) | null >(null); const transformPendingRef = useRef(false); - const editableSourceParseRequestRef = useRef(0); const pendingUsdAssemblyFileRef = useRef(null); - const pendingUsdHydrationFileRef = useRef(null); - const usdPersistenceBaselineRef = useRef(EMPTY_USD_PERSISTENCE_BASELINE); - const usdPreparedExportCacheRequestIdRef = useRef(0); const proModeRoundtripSessionRef = useRef(null); const [pendingViewerToolMode, setPendingViewerToolMode] = useState(null); + const [ikDragActive, setIkDragActive] = useState(false); + const [workspaceTransformPending, setWorkspaceTransformPending] = useState(false); const [isBridgeModalOpen, setIsBridgeModalOpen] = useState(false); const [isCollisionOptimizerOpen, setIsCollisionOptimizerOpen] = useState(false); const [isSnapshotDialogOpen, setIsSnapshotDialogOpen] = useState(false); const [isSnapshotCapturing, setIsSnapshotCapturing] = useState(false); + const [isIkToolPanelOpen, setIsIkToolPanelOpen] = useState(false); const [shouldRenderBridgeModal, setShouldRenderBridgeModal] = useState(false); const [bridgePreview, setBridgePreview] = useState(null); const clearSelection = useCallback(() => { @@ -368,15 +331,6 @@ export function AppLayout({ documentLoadFileName: documentLoadLifecycleState.fileName, }); - useEffect(() => { - if (!isSelectedUsdHydrating || selectedFile?.format !== 'usd') { - pendingUsdHydrationFileRef.current = null; - return; - } - - pendingUsdHydrationFileRef.current = selectedFile.name; - }, [isSelectedUsdHydrating, selectedFile]); - const { assemblyComponentPreparationOverlay, prepareAssemblyComponentForInsert, @@ -396,159 +350,6 @@ export function AppLayout({ setSelection, }); - const queueUsdPreparedExportCacheBuild = useCallback( - (args: { - fileName: string; - sceneSnapshot: UsdSceneSnapshot; - resolution: ViewerRobotDataResolution; - robotSnapshot: string; - }) => { - const requestId = ++usdPreparedExportCacheRequestIdRef.current; - - void prepareUsdPreparedExportCacheWithWorker(args.sceneSnapshot, args.resolution) - .then((preparedCache) => { - if (requestId !== usdPreparedExportCacheRequestIdRef.current) { - return; - } - - const liveAssetsState = useAssetsStore.getState(); - liveAssetsState.setUsdPreparedExportCache(args.fileName, preparedCache); - usdPersistenceBaselineRef.current = { - fileName: normalizeUsdPersistenceFileName(args.fileName), - robotSnapshot: args.robotSnapshot, - fallbackSceneSnapshot: args.sceneSnapshot, - hadPreparedExportCache: Boolean(preparedCache), - hadSceneSnapshot: true, - }; - }) - .catch((error) => { - if (requestId !== usdPreparedExportCacheRequestIdRef.current) { - return; - } - - const reason = error instanceof Error ? error.message : String(error); - scheduleFailFastInDev( - 'AppLayout:prepareUsdPreparedExportCacheWithWorker', - new Error(`Failed to prepare USD export cache for "${args.fileName}": ${reason}`, { - cause: error, - }), - ); - }); - }, - [], - ); - - const flushPendingUsdCache = useCallback(() => { - const liveAssetsState = useAssetsStore.getState(); - const currentSelectedFile = liveAssetsState.selectedFile; - if (!currentSelectedFile || currentSelectedFile.format !== 'usd') { - return; - } - - const normalizedSelectedFileName = normalizeUsdPersistenceFileName(currentSelectedFile.name); - const baseline = usdPersistenceBaselineRef.current; - if ( - !baseline.fileName || - baseline.fileName !== normalizedSelectedFileName || - !baseline.robotSnapshot - ) { - return; - } - - const liveRobotState = useRobotStore.getState(); - const currentRobotData: RobotData = { - name: liveRobotState.name, - links: liveRobotState.links, - joints: liveRobotState.joints, - rootLinkId: liveRobotState.rootLinkId, - materials: liveRobotState.materials, - closedLoopConstraints: liveRobotState.closedLoopConstraints, - }; - const currentRobotSnapshot = createRobotSemanticSnapshot(currentRobotData); - const hasSemanticEdits = currentRobotSnapshot !== baseline.robotSnapshot; - - if (!hasSemanticEdits) { - if (!baseline.hadSceneSnapshot) { - liveAssetsState.setUsdSceneSnapshot(currentSelectedFile.name, null); - } - if (!baseline.hadPreparedExportCache) { - liveAssetsState.setUsdPreparedExportCache(currentSelectedFile.name, null); - } - return; - } - - const sceneSnapshot = getCurrentUsdViewerSceneSnapshot({ - stageSourcePath: currentSelectedFile.name, - }); - - if (!sceneSnapshot) { - scheduleFailFastInDev( - 'AppLayout:flushPendingUsdCache', - new Error( - `Missing live USD scene snapshot for "${currentSelectedFile.name}" while semantic edits are pending.`, - ), - 'warn', - ); - liveAssetsState.setUsdSceneSnapshot(currentSelectedFile.name, null); - liveAssetsState.setUsdPreparedExportCache(currentSelectedFile.name, null); - usdPersistenceBaselineRef.current = { - fileName: normalizedSelectedFileName, - robotSnapshot: currentRobotSnapshot, - fallbackSceneSnapshot: null, - hadPreparedExportCache: false, - hadSceneSnapshot: false, - }; - return; - } - - liveAssetsState.setUsdSceneSnapshot(currentSelectedFile.name, sceneSnapshot); - - const resolution = resolveUsdExportResolution(sceneSnapshot, { - fileName: currentSelectedFile.name, - }); - if (!resolution) { - liveAssetsState.setUsdPreparedExportCache(currentSelectedFile.name, null); - usdPersistenceBaselineRef.current = { - fileName: normalizedSelectedFileName, - robotSnapshot: currentRobotSnapshot, - fallbackSceneSnapshot: sceneSnapshot, - hadPreparedExportCache: false, - hadSceneSnapshot: true, - }; - return; - } - - liveAssetsState.setUsdPreparedExportCache(currentSelectedFile.name, null); - usdPersistenceBaselineRef.current = { - fileName: normalizedSelectedFileName, - robotSnapshot: currentRobotSnapshot, - fallbackSceneSnapshot: sceneSnapshot, - hadPreparedExportCache: false, - hadSceneSnapshot: true, - }; - queueUsdPreparedExportCacheBuild({ - fileName: currentSelectedFile.name, - sceneSnapshot, - resolution, - robotSnapshot: currentRobotSnapshot, - }); - }, [queueUsdPreparedExportCacheBuild]); - - useEffect(() => { - registerPendingUsdCacheFlusher(flushPendingUsdCache); - return () => { - registerPendingUsdCacheFlusher(null); - }; - }, [flushPendingUsdCache]); - - useEffect(() => { - if (selectedFile?.format === 'usd') { - return; - } - - usdPersistenceBaselineRef.current = EMPTY_USD_PERSISTENCE_BASELINE; - }, [selectedFile?.format]); - const { emptyRobot, robot, @@ -563,9 +364,7 @@ export function AppLayout({ viewerSourceFormat, viewerSourceFilePath, workspaceViewerMjcfSourceFile, - sourceCodeContent, - sourceCodeDocumentFlavor, - sourceCodeFileName, + sourceCodeDocuments, filePreview, previewRobot, previewFileName, @@ -576,8 +375,8 @@ export function AppLayout({ assemblyRevision, assemblyBridgePreview: bridgePreview, assemblySelection, + workspaceTransformPending, sidebarTab, - getMergedRobotData, selection, robotName, robotLinks, @@ -599,44 +398,24 @@ export function AppLayout({ setOriginalUrdfContent, }); - const workspaceAssemblyRenderFailureRef = useRef(null); - useEffect(() => { - if (!shouldRenderAssembly || !workspaceAssemblyRenderFailureReason) { - workspaceAssemblyRenderFailureRef.current = null; - return; + if (!shouldRenderAssembly) { + setWorkspaceTransformPending(false); } + }, [shouldRenderAssembly]); - if (workspaceAssemblyRenderFailureRef.current === workspaceAssemblyRenderFailureReason) { - return; - } - - workspaceAssemblyRenderFailureRef.current = workspaceAssemblyRenderFailureReason; - - const message = - workspaceAssemblyRenderFailureReason === 'missing-viewer-merged-robot-data' - ? t.workspaceAssemblyRenderFailedViewerData - : t.workspaceAssemblyRenderFailedMergedData; - - scheduleFailFastInDev( - `[Workspace] Failed to build renderable assembly robot data: ${workspaceAssemblyRenderFailureReason}`, - { - assemblyRevision, - componentCount: assemblyState ? Object.keys(assemblyState.components).length : 0, - selectedFile: selectedFile?.name ?? null, - }, - ); - showToast(message, 'info'); - }, [ + useWorkspaceAssemblyRenderFailureNotice({ assemblyRevision, assemblyState, + labels: { + workspaceAssemblyRenderFailedMergedData: t.workspaceAssemblyRenderFailedMergedData, + workspaceAssemblyRenderFailedViewerData: t.workspaceAssemblyRenderFailedViewerData, + }, selectedFile, shouldRenderAssembly, showToast, - t.workspaceAssemblyRenderFailedMergedData, - t.workspaceAssemblyRenderFailedViewerData, workspaceAssemblyRenderFailureReason, - ]); + }); const previewFile = previewFileName ? (availableFiles.find((file) => file.name === previewFileName) ?? null) @@ -700,325 +479,73 @@ export function AppLayout({ pendingUsdAssemblyFileRef, proModeRoundtripSessionRef, }); - - const handleRobotDataResolved = useCallback( - (result: ViewerRobotDataResolution) => { - const liveAssetsState = useAssetsStore.getState(); - const normalizedStageSourcePath = String(result.stageSourcePath || '').replace(/^\/+/, ''); - const emitCommitWorkerRobotData = ( - status: 'resolved' | 'rejected', - detail: Record, - ) => { - const sourceFileName = - normalizedStageSourcePath || - String(liveAssetsState.selectedFile?.name || selectedFile?.name || '').replace( - /^\/+/, - '', - ); - if (!sourceFileName) { - return; - } - - recordUsdStageLoadDebug({ - sourceFileName, - step: 'commit-worker-robot-data', - status, - timestamp: Date.now(), - detail, - }); - }; - const resolvedSelectedFile = - liveAssetsState.selectedFile ?? - (normalizedStageSourcePath - ? (liveAssetsState.availableFiles.find( - (file) => - file.format === 'usd' && - String(file.name || '').replace(/^\/+/, '') === normalizedStageSourcePath, - ) ?? null) - : null) ?? - selectedFile; - - if (!resolvedSelectedFile) { - emitCommitWorkerRobotData('rejected', { - reason: 'selected-file-unavailable', - stageSourcePath: normalizedStageSourcePath || null, - }); - return; - } - - const normalizedSelectedFileName = String(resolvedSelectedFile.name || '').replace( - /^\/+/, - '', - ); - if ( - normalizedSelectedFileName && - normalizedStageSourcePath && - normalizedSelectedFileName !== normalizedStageSourcePath - ) { - emitCommitWorkerRobotData('rejected', { - reason: 'selected-file-mismatch', - selectedFileName: normalizedSelectedFileName, - stageSourcePath: normalizedStageSourcePath, - }); - return; - } - - if (resolvedSelectedFile.format === 'usd') { - const existingSceneSnapshot = liveAssetsState.getUsdSceneSnapshot( - resolvedSelectedFile.name, - ); - const existingPreparedExportCache = liveAssetsState.getUsdPreparedExportCache( - resolvedSelectedFile.name, - ); - const resolvedRobotSnapshot = createRobotSemanticSnapshot(result.robotData); - const hydrationPersistencePlan = buildUsdHydrationPersistencePlan({ - resolution: result, - existingSceneSnapshot, - existingPreparedExportCache, - }); - const shouldBuildPreparedHydrationExportCache = Boolean( - hydrationPersistencePlan.shouldSeedPreparedExportCache && - hydrationPersistencePlan.sceneSnapshot, - ); - - if ( - hydrationPersistencePlan.shouldSeedSceneSnapshot && - hydrationPersistencePlan.sceneSnapshot - ) { - liveAssetsState.setUsdSceneSnapshot( - resolvedSelectedFile.name, - hydrationPersistencePlan.sceneSnapshot, - ); - } - if (shouldBuildPreparedHydrationExportCache && hydrationPersistencePlan.sceneSnapshot) { - liveAssetsState.setUsdPreparedExportCache(resolvedSelectedFile.name, null); - queueUsdPreparedExportCacheBuild({ - fileName: resolvedSelectedFile.name, - sceneSnapshot: hydrationPersistencePlan.sceneSnapshot, - resolution: result, - robotSnapshot: resolvedRobotSnapshot, - }); - } - - usdPersistenceBaselineRef.current = { - fileName: normalizedSelectedFileName, - robotSnapshot: resolvedRobotSnapshot, - fallbackSceneSnapshot: hydrationPersistencePlan.sceneSnapshot as UsdSceneSnapshot | null, - hadPreparedExportCache: shouldBuildPreparedHydrationExportCache - ? false - : Boolean(existingPreparedExportCache), - hadSceneSnapshot: Boolean(hydrationPersistencePlan.sceneSnapshot), - }; - } - - const pendingHydrationFileName = - pendingUsdHydrationFileRef.current ?? - (liveAssetsState.documentLoadState.status === 'hydrating' - ? liveAssetsState.documentLoadState.fileName - : null); - - const shouldApplyResolvedRobotData = - resolvedSelectedFile.format !== 'usd' || - shouldApplyUsdStageHydration({ - pendingFileName: pendingHydrationFileName, - selectedFileName: resolvedSelectedFile.name, - stageSourcePath: result.stageSourcePath, - }); - - if (shouldApplyResolvedRobotData) { - const isColdUsdHydration = - resolvedSelectedFile.format === 'usd' && - pendingHydrationFileName === resolvedSelectedFile.name; - setRobot( - result.robotData, - resolvedSelectedFile.format === 'usd' - ? isColdUsdHydration - ? { resetHistory: true, label: 'Hydrate USD stage' } - : { skipHistory: true, label: 'Hydrate USD stage' } - : undefined, - ); - setSelection({ type: null, id: null }); - if (isColdUsdHydration) { - markUnsavedChangesBaselineSaved('robot'); - } - if ( - resolvedSelectedFile.format === 'usd' && - pendingUsdHydrationFileRef.current === resolvedSelectedFile.name - ) { - pendingUsdHydrationFileRef.current = null; - } - if (resolvedSelectedFile.format === 'usd') { - emitCommitWorkerRobotData('resolved', { - selectedFileName: normalizedSelectedFileName, - stageSourcePath: normalizedStageSourcePath || null, - linkCount: Object.keys(result.robotData.links || {}).length, - jointCount: Object.keys(result.robotData.joints || {}).length, - linkIdByPathCount: Object.keys(result.linkIdByPath || {}).length, - childLinkPathByJointIdCount: Object.keys(result.childLinkPathByJointId || {}).length, - metadataSource: result.usdSceneSnapshot?.robotMetadataSnapshot?.source ?? null, - commitMode: isColdUsdHydration ? 'reset-history' : 'skip-history', - }); - } - } else if (resolvedSelectedFile.format === 'usd') { - emitCommitWorkerRobotData('rejected', { - reason: 'hydration-gated', - selectedFileName: normalizedSelectedFileName, - pendingHydrationFileName, - stageSourcePath: normalizedStageSourcePath || null, - }); - } - - const pendingUsdAssemblyFile = pendingUsdAssemblyFileRef.current; - if ( - pendingUsdAssemblyFile && - resolvedSelectedFile.format === 'usd' && - pendingUsdAssemblyFile.name === resolvedSelectedFile.name - ) { - pendingUsdAssemblyFileRef.current = null; - void insertAssemblyComponentIntoWorkspace(pendingUsdAssemblyFile, { - preResolvedRobotData: result.robotData, - }) - .then((component) => { - showToast(t.addedComponent.replace('{name}', component.name), 'success'); - updateProModeRoundtripBaseline( - isGeneratedWorkspaceUrdfFileName(pendingUsdAssemblyFile.name) - ? pendingUsdAssemblyFile.name - : null, - ); - }) - .catch((error) => { - scheduleFailFastInDev( - 'AppLayout:handleRobotDataResolved:prepareAssemblyComponent', - error instanceof Error - ? error - : new Error( - `Failed to prepare assembly component "${pendingUsdAssemblyFile.name}".`, - ), - ); - showToast(`Failed to add assembly component: ${pendingUsdAssemblyFile.name}`, 'info'); - }) - .finally(() => { - clearAssemblyComponentPreparationOverlay(); - }); - } - }, - [ - clearAssemblyComponentPreparationOverlay, - insertAssemblyComponentIntoWorkspace, - queueUsdPreparedExportCacheBuild, - selectedFile, - setRobot, - setSelection, - showToast, - t, - updateProModeRoundtripBaseline, - ], - ); - - const handleViewerDocumentLoadEvent = useCallback( - (event: ViewerDocumentLoadEvent) => { - const liveAssetsState = useAssetsStore.getState(); - const activeDocumentFile = previewFile ?? liveAssetsState.selectedFile; - const currentDocumentLoadState = liveAssetsState.documentLoadState; - - if (!activeDocumentFile) { - return; - } - - // A different file is staged for load but not yet committed as the active - // viewer document. Ignore progress from the still-visible old scene so the - // pending file keeps ownership of document loading state. - if ( - shouldIgnoreStaleViewerDocumentLoadEvent({ - isPreviewing: Boolean(previewFile), - activeDocumentFileName: activeDocumentFile.name, - documentLoadState: currentDocumentLoadState, - }) - ) { - return; - } - - const keepHydrating = - !previewFile && - activeDocumentFile.format === 'usd' && - currentDocumentLoadState.status === 'hydrating' && - currentDocumentLoadState.fileName === activeDocumentFile.name; - - const nextStatus: DocumentLoadStatus = - event.status === 'ready' - ? 'ready' - : event.status === 'error' - ? 'error' - : keepHydrating - ? 'hydrating' - : 'loading'; - - const nextDocumentLoadState: DocumentLoadState = { - status: nextStatus, - fileName: activeDocumentFile.name, - format: activeDocumentFile.format, - error: - event.status === 'error' - ? (event.error ?? - t.failedToParseFormat.replace('{format}', activeDocumentFile.format.toUpperCase())) - : null, - phase: event.phase ?? null, - message: event.message ?? null, - progressMode: event.progressMode ?? null, - progressPercent: event.progressPercent ?? null, - loadedCount: event.loadedCount ?? null, - totalCount: event.totalCount ?? null, - }; - - if ( - shouldIgnoreViewerLoadRegressionAfterReadySameFile({ - currentState: currentDocumentLoadState, - nextState: nextDocumentLoadState, - }) - ) { - return; - } - - if ( - currentDocumentLoadState.status !== nextDocumentLoadState.status || - currentDocumentLoadState.fileName !== nextDocumentLoadState.fileName || - currentDocumentLoadState.format !== nextDocumentLoadState.format || - currentDocumentLoadState.error !== nextDocumentLoadState.error || - currentDocumentLoadState.phase !== nextDocumentLoadState.phase || - currentDocumentLoadState.message !== nextDocumentLoadState.message || - currentDocumentLoadState.progressMode !== nextDocumentLoadState.progressMode || - currentDocumentLoadState.progressPercent !== nextDocumentLoadState.progressPercent || - currentDocumentLoadState.loadedCount !== nextDocumentLoadState.loadedCount || - currentDocumentLoadState.totalCount !== nextDocumentLoadState.totalCount - ) { - setDocumentLoadState(nextDocumentLoadState); - } - - if (!previewFile && event.status === 'error' && activeDocumentFile.format === 'usd') { - pendingUsdHydrationFileRef.current = null; - } - - if ( - event.status === 'error' && - pendingUsdAssemblyFileRef.current && - pendingUsdAssemblyFileRef.current.name === activeDocumentFile.name - ) { - pendingUsdAssemblyFileRef.current = null; - clearAssemblyComponentPreparationOverlay(); - } + const { + handleRobotDataResolved, + handleViewerDocumentLoadEvent, + handleViewerRuntimeRobotLoaded, + handleViewerRuntimeSceneReadyForDisplay, + } = useUsdDocumentLifecycle({ + clearAssemblyComponentPreparationOverlay, + insertAssemblyComponentIntoWorkspace, + isSelectedUsdHydrating, + labels: { + addedComponent: t.addedComponent, + failedToParseFormat: t.failedToParseFormat, }, - [ - clearAssemblyComponentPreparationOverlay, - previewFile, - setDocumentLoadState, - t.failedToParseFormat, - ], - ); + pendingUsdAssemblyFileRef, + previewFile, + selectedFile, + setDocumentLoadState, + setRobot, + setSelection, + showToast, + updateProModeRoundtripBaseline, + }); // Keep drag-time joint previews scoped to the active viewer runtime. Feeding them // through AppLayout forces the tree and property sidebars into high-frequency re-render. const previewContextRobot = previewRobot ?? robot; const isPreviewingWorkspaceSource = Boolean(previewRobot); + const ikToolSelectionState = useMemo( + () => + resolveIkToolSelectionState({ + selection, + ikDragActive, + robotLinks: previewContextRobot.links, + robotJoints: previewContextRobot.joints, + rootLinkId: previewContextRobot.rootLinkId, + }), + [ + ikDragActive, + previewContextRobot.joints, + previewContextRobot.links, + previewContextRobot.rootLinkId, + selection, + ], + ); + const selectedIkLinkId = ikToolSelectionState.selectedLinkId; + const selectedIkLinkLabel = useMemo(() => { + if (!selectedIkLinkId) { + return null; + } + + return ( + previewContextRobot.links[selectedIkLinkId]?.name ?? + robotLinks[selectedIkLinkId]?.name ?? + selectedIkLinkId + ); + }, [previewContextRobot.links, robotLinks, selectedIkLinkId]); + const currentIkLinkLabel = useMemo(() => { + if (!ikToolSelectionState.currentLinkId) { + return null; + } + + return ( + previewContextRobot.links[ikToolSelectionState.currentLinkId]?.name ?? + robotLinks[ikToolSelectionState.currentLinkId]?.name ?? + ikToolSelectionState.currentLinkId + ); + }, [ikToolSelectionState.currentLinkId, previewContextRobot.links, robotLinks]); const propertyEditorSelectionContext = useMemo( () => buildPropertyEditorSelectionContext(previewContextRobot, assemblyState), [assemblyState, previewContextRobot], @@ -1039,83 +566,27 @@ export function AppLayout({ setHoveredSelection, focusOn, }); - const trySelectViewerAssemblyComponent = useCallback( - (nextSelection: { - type: Exclude; - id: string; - subType?: 'visual' | 'collision'; - objectIndex?: number; - }) => { - if (nextSelection.type === 'tendon') { - return false; - } - - if (!shouldRenderAssembly || !assemblyState) { - return false; - } - - const componentId = resolveAssemblyViewerComponentSelection(assemblyState, nextSelection, { - hasInteractionGuard: Boolean(useSelectionStore.getState().interactionGuard), - }); - if (!componentId) { - return false; - } - - setSelection({ type: null, id: null }); - selectComponent(componentId); - return true; - }, - [assemblyState, selectComponent, setSelection, shouldRenderAssembly], - ); - const handleViewerSelectWithBridgePreview = useCallback( - (...args: Parameters) => { - const [type, id, subType] = args; - if (type === 'joint' && id === BRIDGE_PREVIEW_ID) { - return; - } - - if (type !== 'tendon' && trySelectViewerAssemblyComponent({ type, id, subType })) { - return; - } - - clearAssemblySelection(); - handleViewerSelect(...args); - }, - [clearAssemblySelection, handleViewerSelect, trySelectViewerAssemblyComponent], - ); - const handleSelectWithAssemblyClear = useCallback( - (...args: Parameters) => { - clearAssemblySelection(); - handleSelect(...args); - }, - [clearAssemblySelection, handleSelect], - ); - const handleSelectGeometryWithAssemblyClear = useCallback( - (...args: Parameters) => { - clearAssemblySelection(); - handleSelectGeometry(...args); - }, - [clearAssemblySelection, handleSelectGeometry], - ); - const handleViewerMeshSelectWithAssemblyClear = useCallback( - (...args: Parameters) => { - const [linkId, _jointId, objectIndex, objectType] = args; - if ( - trySelectViewerAssemblyComponent({ - type: 'link', - id: linkId, - subType: objectType, - objectIndex, - }) - ) { - return; - } - - clearAssemblySelection(); - handleViewerMeshSelect(...args); - }, - [clearAssemblySelection, handleViewerMeshSelect, trySelectViewerAssemblyComponent], - ); + const { + handleWorkspaceTransformPendingChange, + handleViewerSelectWithBridgePreview, + handleSelectWithAssemblyClear, + handleSelectGeometryWithAssemblyClear, + handleViewerMeshSelectWithAssemblyClear, + } = useWorkspaceViewerSelectionBridge({ + assemblyState, + canSelectAssemblyRootComponent: resolveAssemblyRootComponentSelectionAvailability({ + shouldRenderAssembly, + sourceSceneAssemblyComponentId, + }), + clearAssemblySelection, + handleSelect, + handleSelectGeometry, + handleTransformPendingChange, + handleViewerMeshSelect, + handleViewerSelect, + selectComponent, + setWorkspaceTransformPending, + }); const setPendingCollisionTransform = useCollisionTransformStore( (state) => state.setPendingCollisionTransform, @@ -1139,6 +610,10 @@ export function AppLayout({ setAllFileContents, showToast, }); + const jointPanelAvailable = useMemo( + () => hasSingleDofJoints((previewRobot ?? viewerRobot)?.joints), + [previewRobot?.joints, viewerRobot?.joints], + ); const { handleNameChange, @@ -1181,7 +656,7 @@ export function AppLayout({ setSelection, setPendingCollisionTransform, clearPendingCollisionTransform, - handleTransformPendingChange, + handleTransformPendingChange: handleWorkspaceTransformPendingChange, }); const { @@ -1217,7 +692,6 @@ export function AppLayout({ handleBridgePreviewChange, handleCreateBridgeCommit, handleOpenCollisionOptimizer, - handleOpenMeasureTool, } = useWorkspaceOverlayActions({ getUsdPreparedExportCache, onLoadRobot, @@ -1259,108 +733,100 @@ export function AppLayout({ t, }); - const syncSelectedEditableFileContent = useCallback( - (file: RobotFile, content: string) => { - if (selectedFile?.name === file.name && selectedFile.content !== content) { - setSelectedFile({ - ...selectedFile, - content, - }); - } + const { handleCodeChange } = useEditableSourceCodeApply({ + allFileContents, + availableFiles, + selectedFile, + setAllFileContents, + setAvailableFiles, + setOriginalUrdfContent, + setRobot, + setSelectedFile, + }); + const sourceCodeEditorDocuments = useMemo( + () => + sourceCodeDocuments.map((document) => ({ + id: document.id, + code: document.content, + fileName: document.fileName, + tabLabel: document.tabLabel, + filePath: document.filePath ?? undefined, + documentFlavor: document.documentFlavor, + readOnly: document.readOnly, + validationEnabled: document.validationEnabled, + onCodeChange: (newCode: string) => handleCodeChange(newCode, document.changeTarget), + onDownload: document.readOnly + ? undefined + : () => { + markUnsavedChangesBaselineSaved('robot'); + }, + })), + [handleCodeChange, sourceCodeDocuments], + ); + + const handleSnapshot = useCallback(() => { + setIsSnapshotDialogOpen(true); + }, []); + + const handleSetIkDragActive = useCallback( + (active: boolean) => { + setIkDragActive(active); - if (availableFiles.some((entry) => entry.name === file.name && entry.content !== content)) { - setAvailableFiles( - availableFiles.map((entry) => (entry.name === file.name ? { ...entry, content } : entry)), - ); + if (active) { + setViewOption('showIkHandles', true); + return; } - if (allFileContents[file.name] !== content) { - setAllFileContents({ - ...allFileContents, - [file.name]: content, - }); + setViewOption('showIkHandles', false); + setIsIkToolPanelOpen(false); + const clearedSelection = clearIkDragHelperSelection(selection); + if (clearedSelection) { + setSelection(clearedSelection); } }, - [ - allFileContents, - availableFiles, - selectedFile, - setAllFileContents, - setAvailableFiles, - setSelectedFile, - ], + [selection, setSelection, setViewOption], ); - const handleCodeChange = useCallback( - async (newCode: string): Promise => { - if (!selectedFile || selectedFile.format === 'usd') { - return false; - } + const handleOpenIkTool = useCallback(() => { + setViewConfig((prev) => ({ ...prev, showToolbar: true })); + handleSetIkDragActive(true); + setIsIkToolPanelOpen(true); + }, [handleSetIkDragActive, setViewConfig]); - const sourceFile = selectedFile; - const requestId = ++editableSourceParseRequestRef.current; - - try { - const parsedState = await parseEditableRobotSourceWithWorker({ - file: sourceFile, - content: newCode, - availableFiles, - allFileContents, - }); - - if (requestId !== editableSourceParseRequestRef.current) { - return false; - } - - if (useAssetsStore.getState().selectedFile?.name !== sourceFile.name) { - return false; - } - - const newState = parsedState - ? rewriteRobotMeshPathsForSource(parsedState, sourceFile.name) - : null; - - if (!newState) { - return false; - } + const { items: toolboxItems, openTool } = useToolItems({ + t, + openAIInspection: onOpenAIInspection, + openAIConversation: onOpenAIConversation, + openIkTool: handleOpenIkTool, + openCollisionOptimizer: handleOpenCollisionOptimizer, + }); - if (sourceFile.format === 'xacro') { - syncSelectedEditableFileContent(sourceFile, newCode); - setOriginalUrdfContent(generateURDF(newState, { preserveMeshPaths: true })); - } + // Expose layout-level handlers to the parent + useEffect(() => { + onExposeLayoutActions?.({ + openIkTool: handleOpenIkTool, + openCollisionOptimizer: handleOpenCollisionOptimizer, + openTool, + }); + }, [onExposeLayoutActions, handleOpenIkTool, handleOpenCollisionOptimizer, openTool]); - const newData = { - name: newState.name, - links: newState.links, - joints: newState.joints, - rootLinkId: newState.rootLinkId, - materials: newState.materials, - }; - setRobot(newData); - return true; - } catch (error) { - if (requestId !== editableSourceParseRequestRef.current) { - return false; - } + const handleSetDetailOptionsPanelVisibility = useCallback( + (show: boolean) => { + setViewConfig((prev) => setOptionsPanelVisibility(prev, show)); + }, + [setViewConfig], + ); - console.error('[AppLayout] Failed to parse editable source in worker:', error); - return false; + const handleIkDragActiveChange = useCallback( + (active: boolean) => { + if (active) { + setViewConfig((prev) => ({ ...prev, showToolbar: true })); } + handleSetIkDragActive(active); }, - [ - allFileContents, - availableFiles, - selectedFile, - setOriginalUrdfContent, - setRobot, - syncSelectedEditableFileContent, - ], + [handleSetIkDragActive, setViewConfig], ); - const handleSnapshot = useCallback(() => { - setIsSnapshotDialogOpen(true); - }, []); - const handleCaptureSnapshot = useCallback( async (options: SnapshotCaptureOptions) => { if (!snapshotActionRef.current) { @@ -1410,123 +876,21 @@ export function AppLayout({ onPreloadError: handleSourceCodeEditorPreloadError, }); - const handlePreviewFileWithFeedback = useCallback( - (file: RobotFile) => { - const standaloneImportAssetWarning = buildStandaloneImportAssetWarning( - file, - Object.keys(assets), - ); - if (standaloneImportAssetWarning) { - const assetLabel = - standaloneImportAssetWarning.missingAssetPaths.length > 3 - ? `${standaloneImportAssetWarning.missingAssetPaths.slice(0, 3).join(', ')}, …` - : standaloneImportAssetWarning.missingAssetPaths.join(', '); - const warningMessage = t.importPackageAssetBundleHint.replace('{assets}', assetLabel); - - setDocumentLoadState({ - status: 'error', - fileName: file.name, - format: file.format, - error: warningMessage, - phase: null, - message: null, - progressPercent: null, - loadedCount: null, - totalCount: null, - }); - showToast(warningMessage, 'info'); - return; - } - - setDocumentLoadState({ - status: 'loading', - fileName: file.name, - format: file.format, - error: null, - phase: file.format === 'usd' ? 'checking-path' : 'preparing-scene', - message: null, - progressPercent: null, - loadedCount: null, - totalCount: null, - }); - handlePreviewFile(file); - - void resolveRobotFileDataWithWorker(file, { - availableFiles, - assets, - allFileContents, - usdRobotData: getUsdPreparedExportCache(file.name)?.robotData ?? null, - }) - .then((previewResult) => { - if (previewResult.status === 'ready') { - return; - } - - if (previewResult.status === 'needs_hydration') { - setDocumentLoadState({ - status: 'ready', - fileName: file.name, - format: file.format, - error: null, - phase: null, - message: t.usdPreviewRequiresOpen, - progressPercent: null, - loadedCount: null, - totalCount: null, - }); - showToast(t.usdPreviewRequiresOpen, 'info'); - return; - } - - if (previewResult.reason === 'source_only_fragment') { - setDocumentLoadState({ - status: 'ready', - fileName: file.name, - format: file.format, - error: null, - phase: null, - message: t.xacroSourceOnlyPreviewHint, - progressPercent: null, - loadedCount: null, - totalCount: null, - }); - showToast(t.xacroSourceOnlyPreviewHint, 'info'); - return; - } - - setDocumentLoadState({ - status: 'error', - fileName: file.name, - format: file.format, - error: t.failedToParseFormat.replace('{format}', file.format.toUpperCase()), - }); - showToast(t.failedToParseFormat.replace('{format}', file.format.toUpperCase()), 'info'); - }) - .catch((error) => { - console.error( - `[AppLayout] Failed to resolve preview robot data for "${file.name}".`, - error, - ); - setDocumentLoadState({ - status: 'error', - fileName: file.name, - format: file.format, - error: t.failedToParseFormat.replace('{format}', file.format.toUpperCase()), - }); - showToast(t.failedToParseFormat.replace('{format}', file.format.toUpperCase()), 'info'); - }); + const { handlePreviewFileWithFeedback } = usePreviewFileWithFeedback({ + allFileContents, + assets, + availableFiles, + getUsdPreparedExportCache, + handlePreviewFile, + labels: { + failedToParseFormat: t.failedToParseFormat, + importPackageAssetBundleHint: t.importPackageAssetBundleHint, + usdPreviewRequiresOpen: t.usdPreviewRequiresOpen, + xacroSourceOnlyPreviewHint: t.xacroSourceOnlyPreviewHint, }, - [ - allFileContents, - assets, - availableFiles, - getUsdPreparedExportCache, - handlePreviewFile, - setDocumentLoadState, - showToast, - t, - ], - ); + setDocumentLoadState, + showToast, + }); return (
importFolderInputRef.current?.click()} onOpenExport={onOpenExport} onExportProject={onExportProject} - onOpenAI={onOpenAI} - onOpenMeasureTool={handleOpenMeasureTool} + toolboxItems={toolboxItems} onOpenCodeViewer={handleOpenCodeViewer} onPrefetchCodeViewer={handlePrefetchCodeViewer} onOpenSettings={onOpenSettings} quickAction={headerQuickAction} secondaryAction={headerSecondaryAction} onSnapshot={handleSnapshot} - onOpenCollisionOptimizer={handleOpenCollisionOptimizer} viewConfig={viewConfig} + viewAvailability={{ jointPanel: jointPanelAvailable }} setViewConfig={setViewConfig} /> + handleIkDragActiveChange(false)} + /> + {/* Main Workspace */}
- {/* Viewer Container */} -
+ {/* Viewer Container — z-0 stacking context keeps floating panels below sidebars (z-20); + overflow-hidden prevents panels from being dragged outside the 3D view area. */} +
setViewConfig((prev) => ({ ...prev, showToolbar: show }))} showOptionsPanel={viewConfig.showOptionsPanel} - setShowOptionsPanel={(show) => - setViewConfig((prev) => ({ ...prev, showOptionsPanel: show })) - } - showVisualizerOptionsPanel={viewConfig.showVisualizerOptionsPanel} - setShowVisualizerOptionsPanel={(show) => - setViewConfig((prev) => ({ ...prev, showVisualizerOptionsPanel: show })) - } - showJointPanel={viewConfig.showJointPanel} + setShowOptionsPanel={handleSetDetailOptionsPanelVisibility} + showJointPanel={viewConfig.showJointPanel && jointPanelAvailable} setShowJointPanel={(show) => setViewConfig((prev) => ({ ...prev, showJointPanel: show })) } @@ -1654,6 +1021,8 @@ export function AppLayout({ })} onRobotDataResolved={handleRobotDataResolved} onDocumentLoadEvent={handleViewerDocumentLoadEvent} + onRuntimeRobotLoaded={handleViewerRuntimeRobotLoaded} + onRuntimeSceneReadyForDisplay={handleViewerRuntimeSceneReadyForDisplay} jointAngleState={jointAngleState} jointMotionState={jointMotionState} onJointChange={handleJointChange} @@ -1661,7 +1030,7 @@ export function AppLayout({ selection={robot.selection} focusTarget={focusTarget} isMeshPreview={selectedFile?.format === 'mesh'} - onTransformPendingChange={handleTransformPendingChange} + onTransformPendingChange={handleWorkspaceTransformPendingChange} onCollisionTransform={handleCollisionTransform} assemblyState={assemblyState} assemblyWorkspaceActive={shouldRenderAssembly} @@ -1672,6 +1041,7 @@ export function AppLayout({ onBridgeTransform={handleBridgeTransform} filePreview={filePreview} onClosePreview={handleClosePreview} + ikDragActive={ikDragActive} pendingViewerToolMode={pendingViewerToolMode} onConsumePendingViewerToolMode={() => setPendingViewerToolMode(null)} viewerReloadKey={viewerReloadKey} @@ -1679,14 +1049,30 @@ export function AppLayout({ /> + {importPreparationOverlay ? ( + + ) : null}
markUnsavedChangesBaselineSaved('robot')} onCloseCodeViewer={() => setIsCodeViewerOpen(false)} theme={theme} - selectedFileName={sourceCodeFileName} - robotName={robot.name} lang={lang} loadingEditorLabel={t.loadingEditor} isCollisionOptimizerOpen={isCollisionOptimizerOpen} diff --git a/src/app/components/AppLayoutOverlays.tsx b/src/app/components/AppLayoutOverlays.tsx index e30954515..fbb8e2360 100644 --- a/src/app/components/AppLayoutOverlays.tsx +++ b/src/app/components/AppLayoutOverlays.tsx @@ -5,10 +5,6 @@ import { loadBridgeCreateModalModule, loadCollisionOptimizationDialogModule, } from '@/app/utils/overlayLoaders'; -import { - isSourceCodeDocumentReadOnly, - type SourceCodeDocumentFlavor, -} from '@/app/utils/sourceCodeDisplay'; import type { Language } from '@/shared/i18n'; import type { BridgeJoint, InteractionSelection, Theme, UrdfJoint } from '@/types'; import type { AssemblyState } from '@/types'; @@ -17,6 +13,7 @@ import type { CollisionOptimizationSource, CollisionTargetRef, } from '@/features/property-editor'; +import type { SourceCodeEditorDocument } from '@/features/code-editor'; const SourceCodeEditor = lazy(() => loadSourceCodeEditorModule().then((module) => ({ default: module.SourceCodeEditor })), @@ -34,16 +31,10 @@ const BridgeCreateModal = lazy(() => interface AppLayoutOverlaysProps { isCodeViewerOpen: boolean; - sourceCodeContent: string; - sourceCodeDocumentFlavor: SourceCodeDocumentFlavor; - forceSourceCodeReadOnly?: boolean; + sourceCodeDocuments: SourceCodeEditorDocument[]; autoApplyEnabled?: boolean; - onCodeChange: (newCode: string) => Promise | boolean; - onSourceCodeDownload?: () => void; onCloseCodeViewer: () => void; theme: Theme; - selectedFileName?: string; - robotName: string; lang: Language; loadingEditorLabel: string; isCollisionOptimizerOpen: boolean; @@ -72,16 +63,10 @@ interface AppLayoutOverlaysProps { export function AppLayoutOverlays({ isCodeViewerOpen, - sourceCodeContent, - sourceCodeDocumentFlavor, - forceSourceCodeReadOnly = false, + sourceCodeDocuments, autoApplyEnabled = true, - onCodeChange, - onSourceCodeDownload, onCloseCodeViewer, theme, - selectedFileName, - robotName, lang, loadingEditorLabel, isCollisionOptimizerOpen, @@ -100,26 +85,16 @@ export function AppLayoutOverlays({ onPreviewBridgeChange, onCreateBridge, }: AppLayoutOverlaysProps) { - const codeEditorFileName = selectedFileName - ? selectedFileName.split('/').pop() || `${robotName}.urdf` - : `${robotName}.urdf`; - const isSourceCodeReadOnly = - forceSourceCodeReadOnly || isSourceCodeDocumentReadOnly(sourceCodeDocumentFlavor); return ( <> {isCodeViewerOpen && ( }> )} diff --git a/src/app/components/Header.test.tsx b/src/app/components/Header.test.tsx index 4ea92a933..7119a54e9 100644 --- a/src/app/components/Header.test.tsx +++ b/src/app/components/Header.test.tsx @@ -7,6 +7,8 @@ import { Box } from 'lucide-react'; import { Header } from './Header.tsx'; +const noopToolboxItems: import('./header/types').ToolboxItem[] = []; + function renderHeader() { return renderToStaticMarkup( React.createElement(Header, { @@ -14,13 +16,11 @@ function renderHeader() { onImportFolder: () => {}, onOpenExport: () => {}, onExportProject: () => {}, - onOpenAI: () => {}, - onOpenMeasureTool: () => {}, + toolboxItems: noopToolboxItems, onOpenCodeViewer: () => {}, onPrefetchCodeViewer: () => {}, onOpenSettings: () => {}, onSnapshot: () => {}, - onOpenCollisionOptimizer: () => {}, quickAction: { label: 'Quick action', icon: Box, @@ -34,7 +34,6 @@ function renderHeader() { viewConfig: { showToolbar: true, showOptionsPanel: true, - showVisualizerOptionsPanel: true, showJointPanel: true, }, setViewConfig: () => {}, diff --git a/src/app/components/Header.tsx b/src/app/components/Header.tsx index 7b0017236..73be229fc 100644 --- a/src/app/components/Header.tsx +++ b/src/app/components/Header.tsx @@ -11,7 +11,13 @@ import { useActiveHistory } from '../hooks/useActiveHistory'; import { HeaderActions } from './header/HeaderActions'; import { HeaderMenus } from './header/HeaderMenus'; import { useHeaderResponsiveLayout } from './header/useHeaderResponsiveLayout'; -import type { HeaderAction, HeaderMenuKey, HeaderViewConfig } from './header/types'; +import type { + HeaderAction, + HeaderMenuKey, + HeaderViewAvailability, + HeaderViewConfig, + ToolboxItem, +} from './header/types'; interface HeaderProps { // Import actions @@ -19,9 +25,9 @@ interface HeaderProps { onImportFolder: () => void; onOpenExport: () => void; onExportProject: () => void; - // Modal actions - onOpenAI: () => void; - onOpenMeasureTool: () => void; + // Toolbox items + toolboxItems: ToolboxItem[]; + // Other actions onOpenCodeViewer: () => void; onPrefetchCodeViewer: () => void; onOpenSettings: () => void; @@ -29,14 +35,13 @@ interface HeaderProps { secondaryAction?: HeaderAction; // Snapshot onSnapshot: () => void; - onOpenCollisionOptimizer: () => void; // View config viewConfig: { showToolbar: boolean; showOptionsPanel: boolean; - showVisualizerOptionsPanel: boolean; showJointPanel: boolean; }; + viewAvailability?: HeaderViewAvailability; setViewConfig: React.Dispatch>; } @@ -45,16 +50,15 @@ export function Header({ onImportFolder, onOpenExport, onExportProject, - onOpenAI, - onOpenMeasureTool, + toolboxItems, onOpenCodeViewer, onPrefetchCodeViewer, onOpenSettings, quickAction, secondaryAction, onSnapshot, - onOpenCollisionOptimizer, viewConfig, + viewAvailability = { jointPanel: true }, setViewConfig, }: HeaderProps) { const headerRef = React.useRef(null); @@ -82,7 +86,6 @@ export function Header({ ); const responsive = useHeaderResponsiveLayout(headerRef, responsiveOptions); const t = translations[lang]; - React.useEffect(() => { if (activeMenu === null) { return undefined; @@ -125,14 +128,13 @@ export function Header({ showUndoRedoInline={responsive.showUndoRedoInline} t={t} viewConfig={viewConfig} + viewAvailability={viewAvailability} setViewConfig={setViewConfig} onImportFile={onImportFile} onImportFolder={onImportFolder} onOpenExport={onOpenExport} onExportProject={onExportProject} - onOpenAI={onOpenAI} - onOpenMeasureTool={onOpenMeasureTool} - onOpenCollisionOptimizer={onOpenCollisionOptimizer} + toolboxItems={toolboxItems} onOpenCodeViewer={onOpenCodeViewer} onPrefetchCodeViewer={onPrefetchCodeViewer} undo={undo} diff --git a/src/app/components/IkToolPanel.test.tsx b/src/app/components/IkToolPanel.test.tsx new file mode 100644 index 000000000..880e40c6c --- /dev/null +++ b/src/app/components/IkToolPanel.test.tsx @@ -0,0 +1,214 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import React, { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; +import { JSDOM } from 'jsdom'; + +import { translations } from '@/shared/i18n'; +import { IkToolPanel } from './IkToolPanel'; + +type TestRoot = { + dom: JSDOM; + container: HTMLDivElement; + root: Root; +}; + +function installDom() { + const dom = new JSDOM('', { + url: 'http://localhost/', + pretendToBeVisual: true, + }); + + (globalThis as { window?: Window }).window = dom.window as unknown as Window; + (globalThis as { document?: Document }).document = dom.window.document; + Object.defineProperty(globalThis, 'navigator', { + value: dom.window.navigator, + configurable: true, + }); + Object.defineProperty(globalThis, 'localStorage', { + value: dom.window.localStorage, + configurable: true, + }); + + const matchMediaStub = (query: string): MediaQueryList => + ({ + matches: false, + media: query, + onchange: null, + addListener: () => {}, + removeListener: () => {}, + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => false, + }) as MediaQueryList; + + (globalThis as { matchMedia?: typeof window.matchMedia }).matchMedia = matchMediaStub; + dom.window.matchMedia = matchMediaStub; + + (globalThis as { HTMLElement?: typeof HTMLElement }).HTMLElement = dom.window.HTMLElement; + (globalThis as { HTMLInputElement?: typeof HTMLInputElement }).HTMLInputElement = + dom.window.HTMLInputElement; + (globalThis as { HTMLSelectElement?: typeof HTMLSelectElement }).HTMLSelectElement = + dom.window.HTMLSelectElement; + (globalThis as { Event?: typeof Event }).Event = dom.window.Event; + (globalThis as { MouseEvent?: typeof MouseEvent }).MouseEvent = dom.window.MouseEvent; + (globalThis as { PointerEvent?: typeof PointerEvent }).PointerEvent = + dom.window.PointerEvent ?? dom.window.MouseEvent; + (globalThis as { getComputedStyle?: typeof getComputedStyle }).getComputedStyle = + dom.window.getComputedStyle.bind(dom.window); + (globalThis as { requestAnimationFrame?: typeof requestAnimationFrame }).requestAnimationFrame = + dom.window.requestAnimationFrame.bind(dom.window); + (globalThis as { cancelAnimationFrame?: typeof cancelAnimationFrame }).cancelAnimationFrame = + dom.window.cancelAnimationFrame.bind(dom.window); + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + + return dom; +} + +function createComponentRoot(): TestRoot { + const dom = installDom(); + const container = dom.window.document.createElement('div'); + dom.window.document.body.appendChild(container); + const root = createRoot(container); + return { dom, container, root }; +} + +async function renderIkPanel( + root: Root, + overrides: Partial> = {}, +) { + await act(async () => { + root.render( + {}} + {...overrides} + />, + ); + }); +} + +test('IkToolPanel renders the selected-link status and help text without a toggle', async () => { + const { dom, container, root } = createComponentRoot(); + + await renderIkPanel(root); + + const toggle = container.querySelector('button[role="switch"]'); + assert.equal(toggle, null, 'IK drag toggle should not render'); + assert.equal(container.textContent?.includes(translations.en.ikToolboxDesc), true); + assert.equal(container.textContent?.includes(translations.en.ikToolSelectedLink), true); + assert.equal(container.textContent?.includes(translations.en.ikToolNoSelection), true); + assert.equal(container.querySelector('select'), null, 'link selector should not render'); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); + +test('IkToolPanel shows the current IK link label when one is selected', async () => { + const { dom, container, root } = createComponentRoot(); + + await renderIkPanel(root, { + selectedLinkLabel: 'tool_tip_link', + selectionStatus: 'selected', + }); + + assert.equal(container.textContent?.includes('tool_tip_link'), true); + assert.equal(container.textContent?.includes(translations.en.ikToolNoSelection), false); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); + +test('IkToolPanel shows the clicked link and a clear reason when the link cannot be dragged with IK', async () => { + const { dom, container, root } = createComponentRoot(); + + await renderIkPanel(root, { + currentLinkLabel: 'base_link', + selectionStatus: 'root_not_draggable', + }); + + assert.equal(container.textContent?.includes('base_link'), true); + assert.equal(container.textContent?.includes(translations.en.ikToolRootLinkNotDraggable), true); + assert.equal(container.textContent?.includes(translations.en.ikToolNoSelection), false); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); + +test('IkToolPanel can be repositioned by dragging its header', async () => { + const { dom, container, root } = createComponentRoot(); + + await renderIkPanel(root); + + const panelRoot = container.querySelector('.ik-tool-panel') as HTMLDivElement | null; + assert.ok(panelRoot, 'IK panel root should render'); + + panelRoot.getBoundingClientRect = () => + ({ + left: 0, + top: 0, + width: 224, + height: 180, + right: 224, + bottom: 180, + x: 0, + y: 0, + toJSON: () => ({}), + }) as DOMRect; + + const dragHeader = panelRoot.firstElementChild?.firstElementChild as HTMLDivElement | null; + assert.ok(dragHeader, 'IK panel drag header should render'); + + await act(async () => { + dragHeader.dispatchEvent( + new dom.window.MouseEvent('mousedown', { + bubbles: true, + cancelable: true, + clientX: 80, + clientY: 90, + }), + ); + }); + + await act(async () => { + dom.window.document.dispatchEvent( + new dom.window.MouseEvent('mousemove', { + bubbles: true, + cancelable: true, + clientX: 260, + clientY: 180, + buttons: 1, + }), + ); + }); + + assert.equal(panelRoot.style.left, '180px'); + assert.equal(panelRoot.style.top, '90px'); + + await act(async () => { + dom.window.document.dispatchEvent( + new dom.window.MouseEvent('mouseup', { + bubbles: true, + cancelable: true, + clientX: 260, + clientY: 180, + }), + ); + }); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); diff --git a/src/app/components/IkToolPanel.tsx b/src/app/components/IkToolPanel.tsx new file mode 100644 index 000000000..c8c9fec79 --- /dev/null +++ b/src/app/components/IkToolPanel.tsx @@ -0,0 +1,173 @@ +import React from 'react'; + +import { OptionsPanel } from '@/shared/components/Panel/OptionsPanel'; +import type { TranslationKeys } from '@/shared/i18n/types'; + +import type { IkToolSelectionStatus } from '../utils/ikToolSelectionState'; + +const MIN_VISIBLE_PANEL_WIDTH = 56; +const MIN_VISIBLE_PANEL_HEADER_HEIGHT = 40; + +function clampPanelPosition( + nextPosition: { x: number; y: number }, + panelRect: DOMRect, +): { x: number; y: number } { + const minX = Math.min(0, MIN_VISIBLE_PANEL_WIDTH - panelRect.width); + const maxX = Math.max(0, window.innerWidth - MIN_VISIBLE_PANEL_WIDTH); + const minY = 0; + const maxY = Math.max(0, window.innerHeight - MIN_VISIBLE_PANEL_HEADER_HEIGHT); + + return { + x: Math.max(minX, Math.min(nextPosition.x, maxX)), + y: Math.max(minY, Math.min(nextPosition.y, maxY)), + }; +} + +function useIkToolPanelDrag(show: boolean) { + const panelRef = React.useRef(null); + const dragOffsetRef = React.useRef<{ x: number; y: number } | null>(null); + const bodyUserSelectRef = React.useRef(''); + const bodyCursorRef = React.useRef(''); + const [position, setPosition] = React.useState<{ x: number; y: number } | null>(null); + + const handleMouseMove = React.useCallback((event: MouseEvent) => { + if (!panelRef.current || !dragOffsetRef.current) { + return; + } + + const panelRect = panelRef.current.getBoundingClientRect(); + const nextPosition = clampPanelPosition( + { + x: event.clientX - dragOffsetRef.current.x, + y: event.clientY - dragOffsetRef.current.y, + }, + panelRect, + ); + setPosition(nextPosition); + }, []); + + const handleMouseUp = React.useCallback(() => { + dragOffsetRef.current = null; + document.body.style.userSelect = bodyUserSelectRef.current; + document.body.style.cursor = bodyCursorRef.current; + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + window.removeEventListener('blur', handleMouseUp); + }, [handleMouseMove]); + + const handleMouseDown = React.useCallback( + (event: React.MouseEvent) => { + if (event.button !== 0 || !panelRef.current) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + const panelRect = panelRef.current.getBoundingClientRect(); + dragOffsetRef.current = { + x: event.clientX - panelRect.left, + y: event.clientY - panelRect.top, + }; + bodyUserSelectRef.current = document.body.style.userSelect; + bodyCursorRef.current = document.body.style.cursor; + document.body.style.userSelect = 'none'; + document.body.style.cursor = 'grabbing'; + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + window.addEventListener('blur', handleMouseUp); + }, + [handleMouseMove, handleMouseUp], + ); + + React.useEffect(() => { + if (show) { + return; + } + + setPosition(null); + handleMouseUp(); + }, [handleMouseUp, show]); + + React.useEffect( + () => () => { + handleMouseUp(); + }, + [handleMouseUp], + ); + + return { + panelRef, + position, + handleMouseDown, + }; +} + +interface IkToolPanelProps { + show: boolean; + t: TranslationKeys; + currentLinkLabel: string | null; + selectedLinkLabel: string | null; + selectionStatus: IkToolSelectionStatus; + onClose: () => void; +} + +export const IkToolPanel: React.FC = ({ + show, + t, + currentLinkLabel, + selectedLinkLabel, + selectionStatus, + onClose, +}) => { + const [isCollapsed, setIsCollapsed] = React.useState(false); + const { panelRef, position, handleMouseDown } = useIkToolPanelDrag(show); + + const selectionLabel = + selectedLinkLabel ?? + (selectionStatus !== 'idle' ? currentLinkLabel : null) ?? + t.ikToolNoSelection; + + const statusMessage = + selectionStatus === 'root_not_draggable' + ? t.ikToolRootLinkNotDraggable + : selectionStatus === 'no_variable_chain' + ? t.ikToolNoVariableChain + : null; + + return ( + setIsCollapsed((prev) => !prev)} + onMouseDown={handleMouseDown} + panelRef={panelRef} + width="14rem" + panelClassName="ik-tool-panel fixed z-50" + > +
+

{t.ikToolboxDesc}

+
+
+ {t.ikToolSelectedLink} +
+
+ {selectionLabel} +
+
+ {statusMessage ? ( +

+ {statusMessage} +

+ ) : null} +
+
+ ); +}; + +export default IkToolPanel; diff --git a/src/app/components/ImportPreparationOverlay.test.ts b/src/app/components/ImportPreparationOverlay.test.ts index 27be2cc40..860660ff0 100644 --- a/src/app/components/ImportPreparationOverlay.test.ts +++ b/src/app/components/ImportPreparationOverlay.test.ts @@ -16,5 +16,21 @@ test('ImportPreparationOverlay keeps the workspace visible while folder imports assert.match(markup, /fixed inset-x-0 bottom-4/); assert.match(markup, /pointer-events-none/); - assert.doesNotMatch(markup, /fixed inset-0 z-\[160\] flex items-center justify-center bg-black\/35/); + assert.doesNotMatch( + markup, + /fixed inset-0 z-\[160\] flex items-center justify-center bg-black\/35/, + ); +}); + +test('ImportPreparationOverlay can share the viewer corner presentation', () => { + const markup = renderToStaticMarkup( + React.createElement(ImportPreparationOverlay, { + label: 'Preparing import…', + detail: 'Scanning files', + placement: 'viewer-corner', + }), + ); + + assert.match(markup, /absolute inset-0 z-20 flex items-end justify-end p-4/); + assert.doesNotMatch(markup, /fixed inset-x-0 bottom-4/); }); diff --git a/src/app/components/ImportPreparationOverlay.tsx b/src/app/components/ImportPreparationOverlay.tsx index e007984bc..f68392719 100644 --- a/src/app/components/ImportPreparationOverlay.tsx +++ b/src/app/components/ImportPreparationOverlay.tsx @@ -1,11 +1,14 @@ import { LoadingHud } from '@/shared/components/3d'; +type ImportPreparationOverlayPlacement = 'viewport' | 'viewer-corner'; + interface ImportPreparationOverlayProps { label: string; detail?: string; progress?: number | null; statusLabel?: string | null; stageLabel?: string | null; + placement?: ImportPreparationOverlayPlacement; } export function ImportPreparationOverlay({ @@ -14,9 +17,15 @@ export function ImportPreparationOverlay({ progress = null, statusLabel = null, stageLabel = null, + placement = 'viewport', }: ImportPreparationOverlayProps) { + const wrapperClassName = + placement === 'viewer-corner' + ? 'pointer-events-none absolute inset-0 z-20 flex items-end justify-end p-4' + : 'pointer-events-none fixed inset-x-0 bottom-4 z-[160] flex justify-end px-4'; + return ( -
+
root.render(React.createElement(SettingsModal)); }); - const dragHandle = container.querySelector('.cursor-move'); + const dragHandle = container.querySelector('[data-testid="settings-drag-handle"]'); assert.ok(dragHandle, 'settings modal should render a draggable header'); await act(async () => { @@ -244,6 +244,63 @@ test('SettingsModal updates source code editor typography preferences', async () } }); +test('SettingsModal keeps import warning on the original switch control and keeps segmented surfaces consistent', async () => { + const { dom, container, root } = createComponentRoot(); + const initialState = useUIStore.getState(); + + try { + useUIStore.setState({ + isSettingsOpen: true, + settingsPos: { x: 48, y: 64 }, + lang: 'en', + theme: 'dark', + }); + + await act(async () => { + root.render(React.createElement(SettingsModal)); + }); + + const importWarningSwitch = container.querySelector( + '[role="switch"]', + ) as HTMLButtonElement | null; + assert.ok(importWarningSwitch, 'general settings should render the original switch control'); + assert.equal(importWarningSwitch.getAttribute('aria-checked'), 'true'); + + await act(async () => { + importWarningSwitch.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true })); + }); + + assert.equal(useUIStore.getState().showImportWarning, false); + assert.equal(importWarningSwitch.getAttribute('aria-checked'), 'false'); + + const englishButton = Array.from(container.querySelectorAll('button')).find( + (button) => button.textContent?.trim() === 'English', + ) as HTMLButtonElement | undefined; + assert.ok(englishButton, 'language segmented control should render the English option'); + assert.match( + englishButton.className, + /\bbg-segmented-active\b/, + 'selected language button should use the segmented active surface token', + ); + + const darkButton = Array.from(container.querySelectorAll('button')).find( + (button) => button.textContent?.trim() === 'Dark', + ) as HTMLButtonElement | undefined; + assert.ok(darkButton, 'theme segmented control should render the dark option'); + assert.match( + darkButton.className, + /\bbg-segmented-active\b/, + 'selected theme button should use the segmented active surface token', + ); + } finally { + await act(async () => { + root.unmount(); + }); + useUIStore.setState(initialState); + dom.window.close(); + } +}); + test('SettingsModal keeps the detail pane scrollable within a bounded container', async () => { const { dom, container, root } = createComponentRoot(); const initialState = useUIStore.getState(); diff --git a/src/app/components/SettingsModal.tsx b/src/app/components/SettingsModal.tsx index c93bba86e..f72d60d4d 100644 --- a/src/app/components/SettingsModal.tsx +++ b/src/app/components/SettingsModal.tsx @@ -20,7 +20,13 @@ import { import { useShallow } from 'zustand/react/shallow'; import { OptionsPanelContainer } from '@/shared/components/Panel'; -import { Button, IconButton, Select, Switch } from '@/shared/components/ui'; +import { + Button, + CompactSwitch, + IconButton, + PanelSegmentedControl, + PanelSelect, +} from '@/shared/components/ui'; import { translations } from '@/shared/i18n'; import { useUIStore, type CodeEditorFontFamily } from '@/store'; import { SettingsAboutPane } from './settings/SettingsAboutPane'; @@ -37,8 +43,6 @@ const DEFAULT_CODE_EDITOR_FONT_SIZE = 13; const SETTINGS_ICON_STROKE_WIDTH = 1.65; const SETTINGS_INLINE_BUTTON_CLASSNAME = 'h-7 rounded-[6px] border-border-black px-2.5 text-[11px] font-medium shadow-none'; -const SETTINGS_SELECT_CLASSNAME = - 'h-8 rounded-[6px] border-border-black bg-panel-bg px-2.5 pr-8 py-0 text-[12px] font-medium shadow-sm'; const SETTINGS_TEXT_ACTION_CLASSNAME = 'h-7 rounded-[6px] px-2.5 text-[11px] font-medium text-text-secondary shadow-none hover:bg-settings-muted hover:text-text-primary active:bg-settings-muted'; @@ -75,19 +79,6 @@ interface SettingsNavButtonProps { onSelect: (page: SettingsPage) => void; } -interface SettingsSegmentOption { - value: T; - label: string; - icon?: React.ReactNode; -} - -interface SettingsSegmentedControlProps { - options: ReadonlyArray>; - value: T; - onChange: (value: T) => void; - className?: string; -} - interface SettingsStepperProps { label: string; value: number; @@ -168,7 +159,7 @@ function SettingsRow({ label, children, stacked = false }: SettingsRowProps) { function ToggleRow({ label, checked, onChange }: ToggleRowProps) { return ( - + ); } @@ -198,42 +189,6 @@ function SettingsNavButton({ item, isActive, onSelect }: SettingsNavButtonProps) ); } -function SettingsSegmentedControl({ - options, - value, - onChange, - className = '', -}: SettingsSegmentedControlProps) { - return ( -
- {options.map((option) => { - const isSelected = value === option.value; - return ( - - ); - })} -
- ); -} - function SettingsStepper({ label, value, @@ -633,11 +588,10 @@ export function SettingsModal() { />
- setResolutionPreset(event.target.value)} /> - setEnvironmentPreset( event.target.value as SnapshotCaptureOptions['environmentPreset'], @@ -449,11 +387,11 @@ export function SnapshotDialog({ /> - setShadowStyle(event.target.value as SnapshotCaptureOptions['shadowStyle']) } /> - setDofMode(event.target.value as SnapshotCaptureOptions['dofMode']) } /> - setHideGrid(value)} + ariaLabel={t.snapshotHideGrid} + className="w-full justify-end" />
diff --git a/src/app/components/UnifiedViewer.tsx b/src/app/components/UnifiedViewer.tsx index d4c583865..a6eb40a36 100644 --- a/src/app/components/UnifiedViewer.tsx +++ b/src/app/components/UnifiedViewer.tsx @@ -16,20 +16,18 @@ import { WORKSPACE_CANVAS_BACKGROUND, type SnapshotCaptureAction, } from '@/shared/components/3d'; -import { useVisualizerController } from '@/features/visualizer/hooks/useVisualizerController'; -import { useURDFViewerController } from '@/features/urdf-viewer/hooks/useURDFViewerController'; -import { resolveDefaultViewerToolMode } from '@/features/urdf-viewer/utils/scopedToolMode'; -import type { - ViewerHelperKind, - ToolMode, - ViewerDocumentLoadEvent, - ViewerJointMotionStateValue, - ViewerRobotSourceFormat, -} from '@/features/urdf-viewer/types'; -import type { ViewerRobotDataResolution } from '@/features/urdf-viewer/utils/viewerRobotData'; +import { + useViewerController, + resolveDefaultViewerToolMode, + type ViewerHelperKind, + type ToolMode, + type ViewerDocumentLoadEvent, + type ViewerJointMotionStateValue, + type ViewerRobotSourceFormat, + type ViewerRobotDataResolution, +} from '@/features/editor'; import { resolveViewerJointScopeKey } from '@/app/utils/viewerJointScopeKey'; import { resolveUnifiedViewerForcedSessionState } from '@/app/utils/unifiedViewerForcedSessionState'; -import { resolveUnifiedViewerLoadReleaseState } from '@/app/utils/unifiedViewerLoadReleaseState'; import { captureUnifiedViewerOptionsVisibility, shouldRestoreUnifiedViewerOptionsPanel, @@ -42,11 +40,11 @@ import { syncGroupRaycastInteractivity, type RaycastableObject, } from './unified-viewer/raycastInteractivity'; +import { preloadViewerModeModules } from './unified-viewer/modeModuleLoaders'; import { - preloadViewerModeModules, - preloadVisualizerModeModules, -} from './unified-viewer/modeModuleLoaders'; -import { handleUnifiedViewerWorkspaceLeave } from '@/app/utils/unifiedViewerHoverReset'; + buildUnifiedViewerRetainedRobotScopeKey, + shouldReuseUnifiedViewerRetainedRobot, +} from '@/app/utils/unifiedViewerRetainedRobot'; import { UnifiedViewerOverlays } from './unified-viewer/UnifiedViewerOverlays'; import { UnifiedViewerSceneRoots } from './unified-viewer/UnifiedViewerSceneRoots'; import type { FilePreviewState } from './unified-viewer/types'; @@ -55,7 +53,7 @@ import { useSelectionStore } from '@/store/selectionStore'; interface UnifiedViewerProps { robot: RobotState; - visualizerRobot?: RobotState; + editorRobot?: RobotState; mode: AppMode; onSelect: ( type: Exclude, @@ -88,8 +86,6 @@ interface UnifiedViewerProps { setShowToolbar?: (show: boolean) => void; showOptionsPanel?: boolean; setShowOptionsPanel?: (show: boolean) => void; - showVisualizerOptionsPanel?: boolean; - setShowVisualizerOptionsPanel?: (show: boolean) => void; showJointPanel?: boolean; setShowJointPanel?: (show: boolean) => void; availableFiles: RobotFile[]; @@ -99,6 +95,8 @@ interface UnifiedViewerProps { sourceFile?: RobotFile | null; onRobotDataResolved?: (result: ViewerRobotDataResolution) => void; onDocumentLoadEvent?: (event: ViewerDocumentLoadEvent) => void; + onRuntimeRobotLoaded?: (robot: ThreeObject3D) => void; + onRuntimeSceneReadyForDisplay?: () => void; jointAngleState?: Record; jointMotionState?: Record; onJointChange?: (jointName: string, angle: number) => void; @@ -145,6 +143,7 @@ interface UnifiedViewerProps { ) => void; filePreview?: FilePreviewState; onClosePreview?: () => void; + ikDragActive?: boolean; pendingViewerToolMode?: ToolMode | null; onConsumePendingViewerToolMode?: () => void; viewerReloadKey?: number; @@ -153,18 +152,10 @@ interface UnifiedViewerProps { const INACTIVE_SCENE_UNMOUNT_DELAY_MS = 15_000; -type IdleScheduler = { - requestIdleCallback?: ( - callback: (deadline: { didTimeout: boolean; timeRemaining: () => number }) => void, - options?: { timeout: number }, - ) => number; - cancelIdleCallback?: (handle: number) => void; -}; - export const UnifiedViewer = React.memo( ({ robot, - visualizerRobot: visualizerRobotInput, + editorRobot: editorRobotInput, mode, onSelect, onMeshSelect, @@ -180,8 +171,6 @@ export const UnifiedViewer = React.memo( setShowToolbar, showOptionsPanel = true, setShowOptionsPanel, - showVisualizerOptionsPanel = true, - setShowVisualizerOptionsPanel, showJointPanel = true, setShowJointPanel, availableFiles, @@ -191,6 +180,8 @@ export const UnifiedViewer = React.memo( sourceFile, onRobotDataResolved, onDocumentLoadEvent, + onRuntimeRobotLoaded, + onRuntimeSceneReadyForDisplay, jointAngleState, jointMotionState, onJointChange, @@ -210,6 +201,7 @@ export const UnifiedViewer = React.memo( onBridgeTransform, filePreview, onClosePreview, + ikDragActive = false, pendingViewerToolMode = null, onConsumePendingViewerToolMode, viewerReloadKey = 0, @@ -228,38 +220,26 @@ export const UnifiedViewer = React.memo( viewerSceneMode, mountState, setMountState, - viewerSceneReady, - setViewerSceneReady, resolvedTheme, viewerOptionsVisibleRef, - visualizerOptionsVisibleRef, - previousIsViewerModeRef, - viewerPendingLoadScopeRef, - viewerReleasedLoadScopeRef, optionsVisibleAtPointerDownRef, - visualizerRobot, + editorRobot, effectiveUrdfContent, effectiveSourceFilePath, effectiveSourceFile, - activeViewportFileName, viewerResourceScope, - visualizerResourceScope, sourceSceneAssemblyComponent, sourceSceneAssemblyComponentTransform, handleSourceSceneAssemblyComponentTransform, showSourceSceneAssemblyComponentControls, - pendingViewerLoadScopeKey, - releasedViewerLoadScopeKey, viewportState, - handoffReadyState, } = useUnifiedViewerDerivedState({ mode, filePreview, pendingViewerToolMode, theme, showOptionsPanel, - showVisualizerOptionsPanel, - visualizerRobotInput, + editorRobotInput, robot, assemblyWorkspaceActive, urdfContent, @@ -277,35 +257,30 @@ export const UnifiedViewer = React.memo( const effectiveJointAngleState = isPreviewing ? undefined : jointAngleState; const effectiveJointMotionState = isPreviewing ? undefined : jointMotionState; const effectiveSyncJointChangesToApp = isPreviewing ? false : syncJointChangesToApp; - const { - viewerLoadScopeKey, - hasPendingViewerHandoffForScope, - visualizerAvailableForViewportHandoff, - startViewerViewportHandoff, - continueViewerViewportHandoff, - keepExistingViewerViewportHandoff, - displayVisualizerWhileViewerLoads, - keepViewerMountedDuringHandoff, - viewerVisible, - visualizerVisible, - shouldRenderViewerScene, - shouldRenderVisualizerScene, - activeScene, - useViewerCanvasPresentation, - visualizerRuntimeMode, - } = viewportState; + const { viewerVisible, shouldRenderViewerScene, useViewerCanvasPresentation } = viewportState; const viewerGroupRef = React.useRef(null); - const visualizerGroupRef = React.useRef(null); const viewerRaycastCacheRef = React.useRef( new WeakMap>(), ); const viewerRetainedRobotRef = React.useRef(null); + const viewerRetainedRobotScopeRef = React.useRef(null); const viewerRetainedRobotReleaseTimerRef = React.useRef(null); const viewerUnmountTimerRef = React.useRef(null); - const visualizerUnmountTimerRef = React.useRef(null); - const visualizerRaycastCacheRef = React.useRef( - new WeakMap>(), + const viewerRetainedRobotScopeKey = React.useMemo( + () => + buildUnifiedViewerRetainedRobotScopeKey({ + sourceFile: effectiveSourceFile, + sourceFilePath: effectiveSourceFilePath, + sourceFormat: viewerSourceFormat, + }), + [effectiveSourceFile, effectiveSourceFilePath, viewerSourceFormat], ); + const retainedViewerRobot = shouldReuseUnifiedViewerRetainedRobot( + viewerRetainedRobotScopeRef.current, + viewerRetainedRobotScopeKey, + ) + ? viewerRetainedRobotRef.current + : null; const clearRetainedViewerRobot = React.useCallback(() => { if (viewerRetainedRobotReleaseTimerRef.current !== null) { window.clearTimeout(viewerRetainedRobotReleaseTimerRef.current); @@ -313,21 +288,13 @@ export const UnifiedViewer = React.memo( } viewerRetainedRobotRef.current = null; + viewerRetainedRobotScopeRef.current = null; }, []); - useEffect(() => { - previousIsViewerModeRef.current = isViewerMode; - }, [isViewerMode]); - - useEffect(() => { - viewerPendingLoadScopeRef.current = handoffReadyState.pendingViewerLoadScopeKey; - setViewerSceneReady(handoffReadyState.viewerSceneReady); - }, [handoffReadyState.pendingViewerLoadScopeKey, handoffReadyState.viewerSceneReady]); - // Keep quick mode flips warm, but unmount the inactive scene once the user // settles so hidden useFrame subscriptions stop consuming work in the background. useEffect(() => { - if (viewerVisible || keepViewerMountedDuringHandoff) { + if (viewerVisible) { if (viewerUnmountTimerRef.current !== null) { window.clearTimeout(viewerUnmountTimerRef.current); viewerUnmountTimerRef.current = null; @@ -352,35 +319,7 @@ export const UnifiedViewer = React.memo( viewerUnmountTimerRef.current = null; } }; - }, [keepViewerMountedDuringHandoff, mountState.viewerMounted, viewerVisible]); - - useEffect(() => { - if (visualizerVisible) { - if (visualizerUnmountTimerRef.current !== null) { - window.clearTimeout(visualizerUnmountTimerRef.current); - visualizerUnmountTimerRef.current = null; - } - return; - } - - if (!mountState.visualizerMounted) { - return; - } - - visualizerUnmountTimerRef.current = window.setTimeout(() => { - visualizerUnmountTimerRef.current = null; - setMountState((current) => - current.visualizerMounted ? { ...current, visualizerMounted: false } : current, - ); - }, INACTIVE_SCENE_UNMOUNT_DELAY_MS); - - return () => { - if (visualizerUnmountTimerRef.current !== null) { - window.clearTimeout(visualizerUnmountTimerRef.current); - visualizerUnmountTimerRef.current = null; - } - }; - }, [mountState.visualizerMounted, visualizerVisible]); + }, [mountState.viewerMounted, viewerVisible]); useEffect( () => () => { @@ -388,30 +327,17 @@ export const UnifiedViewer = React.memo( window.clearTimeout(viewerUnmountTimerRef.current); viewerUnmountTimerRef.current = null; } - if (visualizerUnmountTimerRef.current !== null) { - window.clearTimeout(visualizerUnmountTimerRef.current); - visualizerUnmountTimerRef.current = null; - } clearRetainedViewerRobot(); }, [clearRetainedViewerRobot], ); - - const visualizerController = useVisualizerController({ - robot: visualizerRobot, - onUpdate, - mode: visualizerRuntimeMode, - assemblyWorkspaceActive, - propShowVisual: showVisual, - propSetShowVisual: setShowVisual, - }); const viewerDefaultToolMode = resolveDefaultViewerToolMode(effectiveSourceFile?.format); const viewerToolModeScopeKey = effectiveSourceFile ? `${effectiveSourceFile.format}:${effectiveSourceFile.name}` : effectiveSourceFilePath ? `inline:${effectiveSourceFilePath}` : 'inline:unified-viewer'; - const viewerController = useURDFViewerController({ + const viewerController = useViewerController({ onJointChange, syncJointChangesToApp: effectiveSyncJointChangesToApp, showJointPanel, @@ -435,7 +361,7 @@ export const UnifiedViewer = React.memo( }), defaultToolMode: viewerDefaultToolMode, toolModeScopeKey: viewerToolModeScopeKey, - closedLoopRobotState: visualizerRobot, + closedLoopRobotState: editorRobot, }); const nextForcedViewerSession = resolveUnifiedViewerForcedSessionState({ forcedViewerSession, @@ -453,40 +379,13 @@ export const UnifiedViewer = React.memo( const handleViewerDocumentLoadEvent = React.useCallback( (event: ViewerDocumentLoadEvent) => { - // `ready` means the runtime finished loading, not that the first frame has - // already painted. Releasing handoff on `ready` can still expose one blank - // frame, so only terminate immediately on hard errors. - if (event.status === 'error') { - const releaseState = resolveUnifiedViewerLoadReleaseState({ - pendingViewerLoadScopeKey: viewerPendingLoadScopeRef.current, - viewerLoadScopeKey, - }); - if (!releaseState.canReleaseViewerLoadScope) { - onDocumentLoadEvent?.(event); - return; - } - - viewerReleasedLoadScopeRef.current = releaseState.releasedViewerLoadScopeKey; - viewerPendingLoadScopeRef.current = releaseState.pendingViewerLoadScopeKey; - setViewerSceneReady(releaseState.viewerSceneReady); - } onDocumentLoadEvent?.(event); }, - [onDocumentLoadEvent, viewerLoadScopeKey], + [onDocumentLoadEvent], ); const handleViewerSceneReadyForDisplay = React.useCallback(() => { - const releaseState = resolveUnifiedViewerLoadReleaseState({ - pendingViewerLoadScopeKey: viewerPendingLoadScopeRef.current, - viewerLoadScopeKey, - }); - if (!releaseState.canReleaseViewerLoadScope) { - return; - } - - viewerReleasedLoadScopeRef.current = releaseState.releasedViewerLoadScopeKey; - viewerPendingLoadScopeRef.current = releaseState.pendingViewerLoadScopeKey; - setViewerSceneReady(releaseState.viewerSceneReady); - }, [viewerLoadScopeKey]); + onRuntimeSceneReadyForDisplay?.(); + }, [onRuntimeSceneReadyForDisplay]); const controlLayerKey = 'shared'; const workspaceEnvironment = 'studio' as const; @@ -495,18 +394,17 @@ export const UnifiedViewer = React.memo( : STUDIO_ENVIRONMENT_INTENSITY.workspace[resolvedTheme]; const showWorldOriginAxesPreference = useUIStore((state) => state.viewOptions.showAxes); const showUsageGuidePreference = useUIStore((state) => state.viewOptions.showUsageGuide); - const showWorldOriginAxes = - showWorldOriginAxesPreference && - (activeScene === 'viewer' - ? !viewerController.showOrigins - : !visualizerController.state.showOrigin); + const showWorldOriginAxes = showWorldOriginAxesPreference && !viewerController.showOrigins; - const handleWorkspacePointerDownCapture = React.useCallback(() => { - optionsVisibleAtPointerDownRef.current = captureUnifiedViewerOptionsVisibility({ - showViewerOptions: showOptionsPanel, - showVisualizerOptions: showVisualizerOptionsPanel, - }); - }, [showOptionsPanel, showVisualizerOptionsPanel]); + const handleWorkspacePointerDownCapture = React.useCallback( + (event: React.PointerEvent) => { + void event; + optionsVisibleAtPointerDownRef.current = captureUnifiedViewerOptionsVisibility({ + showViewerOptions: showOptionsPanel, + }); + }, + [showOptionsPanel], + ); // Blank-canvas clicks should clear selection, not dismiss an already-open options panel. const restoreOptionsPanelIfNeeded = React.useCallback( @@ -550,33 +448,6 @@ export const UnifiedViewer = React.memo( ); }, [restoreOptionsPanelIfNeeded, setShowOptionsPanel, viewerController]); - const handleVisualizerPointerMissed = React.useCallback(() => { - visualizerController.clearSelection(); - restoreOptionsPanelIfNeeded( - optionsVisibleAtPointerDownRef.current.visualizer, - visualizerOptionsVisibleRef, - setShowVisualizerOptionsPanel, - ); - }, [restoreOptionsPanelIfNeeded, setShowVisualizerOptionsPanel, visualizerController]); - - const visualizerSceneSignature = React.useMemo( - () => - [ - visualizerRobot.name, - visualizerRobot.rootLinkId, - Object.keys(visualizerRobot.links).length, - Object.keys(visualizerRobot.joints).length, - visualizerRuntimeMode, - ].join(':'), - [ - visualizerRobot.joints, - visualizerRobot.links, - visualizerRobot.name, - visualizerRobot.rootLinkId, - visualizerRuntimeMode, - ], - ); - useEffect(() => { const root = viewerGroupRef.current; syncGroupRaycastInteractivity(root, viewerVisible, viewerRaycastCacheRef.current); @@ -587,12 +458,7 @@ export const UnifiedViewer = React.memo( }, [viewerVisible, shouldRenderViewerScene, viewerReloadKey]); useEffect(() => { - if ( - viewerVisible || - keepViewerMountedDuringHandoff || - mountState.viewerMounted || - !viewerRetainedRobotRef.current - ) { + if (viewerVisible || mountState.viewerMounted || !viewerRetainedRobotRef.current) { if (viewerRetainedRobotReleaseTimerRef.current !== null) { window.clearTimeout(viewerRetainedRobotReleaseTimerRef.current); viewerRetainedRobotReleaseTimerRef.current = null; @@ -600,12 +466,13 @@ export const UnifiedViewer = React.memo( return; } - // Preserve the last URDF scene only while the viewer is still mounted or - // actively handoffing. After the scene has been torn down, release the - // retained graph so Three.js resources are no longer pinned by this ref. + // Preserve the last non-USD runtime only while the viewer is still mounted. + // After the scene has been torn down, release the retained graph so + // Three.js resources are no longer pinned by this ref. viewerRetainedRobotReleaseTimerRef.current = window.setTimeout(() => { viewerRetainedRobotReleaseTimerRef.current = null; viewerRetainedRobotRef.current = null; + viewerRetainedRobotScopeRef.current = null; }, 0); return () => { @@ -614,24 +481,19 @@ export const UnifiedViewer = React.memo( viewerRetainedRobotReleaseTimerRef.current = null; } }; - }, [keepViewerMountedDuringHandoff, mountState.viewerMounted, viewerVisible]); + }, [mountState.viewerMounted, viewerVisible]); useEffect(() => { - if (effectiveSourceFile?.format === 'usd' || !effectiveSourceFile) { + if ( + viewerRetainedRobotScopeRef.current !== null && + !shouldReuseUnifiedViewerRetainedRobot( + viewerRetainedRobotScopeRef.current, + viewerRetainedRobotScopeKey, + ) + ) { clearRetainedViewerRobot(); } - }, [clearRetainedViewerRobot, effectiveSourceFile]); - - useEffect(() => { - const root = visualizerGroupRef.current; - // Hidden R3F groups can still receive pointer raycasts, so explicitly disable - // the inactive scene to prevent background hover/selection from leaking across modes. - syncGroupRaycastInteractivity(root, visualizerVisible, visualizerRaycastCacheRef.current); - - return () => { - syncGroupRaycastInteractivity(root, true, visualizerRaycastCacheRef.current); - }; - }, [shouldRenderVisualizerScene, visualizerSceneSignature, visualizerVisible]); + }, [clearRetainedViewerRobot, viewerRetainedRobotScopeKey]); useEffect(() => { if (!pendingViewerToolMode || !isViewerMode) { @@ -650,49 +512,10 @@ export const UnifiedViewer = React.memo( ]); useEffect(() => { - const preloadCurrentModeModules = isViewerMode - ? preloadViewerModeModules - : preloadVisualizerModeModules; - - void preloadCurrentModeModules().catch((error) => { + void preloadViewerModeModules().catch((error) => { console.warn('[UnifiedViewer] Failed to preload active mode modules.', error); }); - }, [isViewerMode]); - - useEffect(() => { - const schedulerWindow = window as Window & IdleScheduler; - let timeoutHandle: number | null = null; - let idleHandle: number | null = null; - let cancelled = false; - - const preloadInactiveModeModules = () => { - if (cancelled) { - return; - } - const preload = isViewerMode ? preloadVisualizerModeModules : preloadViewerModeModules; - void preload().catch((error) => { - console.warn('[UnifiedViewer] Failed to prefetch inactive mode modules.', error); - }); - }; - - if (typeof schedulerWindow.requestIdleCallback === 'function') { - idleHandle = schedulerWindow.requestIdleCallback(preloadInactiveModeModules, { - timeout: 1_500, - }); - } else { - timeoutHandle = window.setTimeout(preloadInactiveModeModules, 1_500); - } - - return () => { - cancelled = true; - if (idleHandle !== null && typeof schedulerWindow.cancelIdleCallback === 'function') { - schedulerWindow.cancelIdleCallback(idleHandle); - } - if (timeoutHandle !== null) { - window.clearTimeout(timeoutHandle); - } - }; - }, [isViewerMode]); + }, []); useEffect(() => { const handleWindowBlur = () => { @@ -714,88 +537,56 @@ export const UnifiedViewer = React.memo( }, [clearHover]); const handleWorkspaceMouseLeave = React.useCallback(() => { - handleUnifiedViewerWorkspaceLeave({ - activeScene, - clearHover, - handleViewerMouseUp: viewerController.handleMouseUp, - handleVisualizerMouseUp: visualizerController.panel.handleMouseUp, - }); - }, [ - activeScene, - clearHover, - viewerController.handleMouseUp, - visualizerController.panel.handleMouseUp, - ]); + viewerController.handleMouseUp(); + clearHover(); + }, [clearHover, viewerController]); return ( { - viewerController.isOrbitDragging.current = true; - }, - onEnd: () => { - viewerController.isOrbitDragging.current = false; - }, - } - : undefined - } + orbitControlsProps={{ + minDistance: 0.05, + maxDistance: 2000, + enabled: !viewerController.isDragging, + onStart: () => { + viewerController.isOrbitDragging.current = true; + }, + onEnd: () => { + viewerController.isOrbitDragging.current = false; + }, + }} background={WORKSPACE_CANVAS_BACKGROUND} - contextLostMessage={activeScene === 'viewer' ? t.webglContextRestoring : undefined} + contextLostMessage={t.webglContextRestoring} showUsageGuide={showUsageGuidePreference} overlays={ } > @@ -806,7 +597,7 @@ export const UnifiedViewer = React.memo( viewerController={viewerController} activePreview={activePreview} viewerResourceScope={viewerResourceScope} - retainedRobot={viewerRetainedRobotRef.current} + retainedRobot={retainedViewerRobot} effectiveSourceFile={effectiveSourceFile} effectiveSourceFilePath={effectiveSourceFilePath} effectiveUrdfContent={effectiveUrdfContent} @@ -816,6 +607,8 @@ export const UnifiedViewer = React.memo( onSceneReadyForDisplay={handleViewerSceneReadyForDisplay} onRuntimeRobotLoaded={(loadedRobot) => { viewerRetainedRobotRef.current = loadedRobot; + viewerRetainedRobotScopeRef.current = viewerRetainedRobotScopeKey; + onRuntimeRobotLoaded?.(loadedRobot); }} viewerSceneMode={viewerSceneMode} selection={selection} @@ -827,30 +620,17 @@ export const UnifiedViewer = React.memo( onCollisionTransform={onCollisionTransform} isMeshPreview={isMeshPreview} viewerReloadKey={viewerReloadKey} - sourceSceneAssemblyComponent={sourceSceneAssemblyComponent} - sourceSceneAssemblyComponentTransform={sourceSceneAssemblyComponentTransform} - showSourceSceneAssemblyComponentControls={showSourceSceneAssemblyComponentControls} - onSourceSceneAssemblyComponentTransform={handleSourceSceneAssemblyComponentTransform} - t={t} - shouldRenderVisualizerScene={shouldRenderVisualizerScene} - visualizerGroupRef={visualizerGroupRef} - visualizerVisible={visualizerVisible} - visualizerRobot={visualizerRobot} - onSelect={onSelect} - onUpdate={onUpdate} - visualizerRuntimeMode={visualizerRuntimeMode} - visualizerResourceScope={visualizerResourceScope} - lang={lang} - visualizerController={visualizerController} assemblyState={assemblyState} - assemblyWorkspaceActive={assemblyWorkspaceActive} assemblySelection={assemblySelection} - sourceSceneAssemblyComponentId={sourceSceneAssemblyComponentId} onAssemblyTransform={onAssemblyTransform} onComponentTransform={onComponentTransform} onBridgeTransform={onBridgeTransform} - onTransformPendingChange={onTransformPendingChange} - isViewerMode={isViewerMode} + sourceSceneAssemblyComponent={sourceSceneAssemblyComponent} + sourceSceneAssemblyComponentTransform={sourceSceneAssemblyComponentTransform} + showSourceSceneAssemblyComponentControls={showSourceSceneAssemblyComponentControls} + onSourceSceneAssemblyComponentTransform={handleSourceSceneAssemblyComponentTransform} + t={t} + ikDragActive={ikDragActive} /> ); diff --git a/src/app/components/WorkspaceCanvas.tsx b/src/app/components/WorkspaceCanvas.tsx deleted file mode 100644 index ceaf3a187..000000000 --- a/src/app/components/WorkspaceCanvas.tsx +++ /dev/null @@ -1 +0,0 @@ -export { WorkspaceCanvas } from '@/shared/components/3d/workspace'; diff --git a/src/app/components/header/HeaderMenus.test.tsx b/src/app/components/header/HeaderMenus.test.tsx new file mode 100644 index 000000000..0da957bee --- /dev/null +++ b/src/app/components/header/HeaderMenus.test.tsx @@ -0,0 +1,84 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; + +import React from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { JSDOM } from 'jsdom'; + +import { translations } from '@/shared/i18n'; + +import { HeaderMenus } from './HeaderMenus.tsx'; + +const noopToolboxItems: import('./types').ToolboxItem[] = []; + +function renderViewMenu({ + showJointPanel = true, + jointPanelAvailable = true, +}: { + showJointPanel?: boolean; + jointPanelAvailable?: boolean; +}) { + return renderToStaticMarkup( + React.createElement(HeaderMenus, { + activeMenu: 'view', + setActiveMenu: () => {}, + showMenuLabels: true, + showSourceInline: false, + showSourceText: false, + showUndoRedoInline: false, + t: translations.en, + viewConfig: { + showToolbar: true, + showOptionsPanel: true, + showJointPanel, + }, + viewAvailability: { + jointPanel: jointPanelAvailable, + }, + setViewConfig: () => {}, + onImportFile: () => {}, + onImportFolder: () => {}, + onOpenExport: () => {}, + onExportProject: () => {}, + toolboxItems: noopToolboxItems, + onOpenCodeViewer: () => {}, + onPrefetchCodeViewer: () => {}, + undo: () => {}, + redo: () => {}, + canUndo: false, + canRedo: false, + }), + ); +} + +function getJointsPanelMenuButton(markup: string) { + const dom = new JSDOM(`${markup}`); + const buttons = Array.from(dom.window.document.querySelectorAll('button')); + const match = buttons.find((button) => button.textContent?.includes('Joints Panel')); + assert.ok(match, 'expected the view menu to render a joints panel menu item'); + return match; +} + +test('view menu shows the joints panel item as checked when the panel is available and enabled', () => { + const markup = renderViewMenu({ + showJointPanel: true, + jointPanelAvailable: true, + }); + const button = getJointsPanelMenuButton(markup); + + assert.equal(button.getAttribute('role'), 'menuitemcheckbox'); + assert.equal(button.getAttribute('aria-checked'), 'true'); + assert.equal(button.hasAttribute('disabled'), false); +}); + +test('view menu disables the joints panel item and clears its checkmark when no controllable joint exists', () => { + const markup = renderViewMenu({ + showJointPanel: true, + jointPanelAvailable: false, + }); + const button = getJointsPanelMenuButton(markup); + + assert.equal(button.getAttribute('role'), 'menuitemcheckbox'); + assert.equal(button.getAttribute('aria-checked'), 'false'); + assert.equal(button.hasAttribute('disabled'), true); +}); diff --git a/src/app/components/header/HeaderMenus.tsx b/src/app/components/header/HeaderMenus.tsx index 145a539da..b330d448c 100644 --- a/src/app/components/header/HeaderMenus.tsx +++ b/src/app/components/header/HeaderMenus.tsx @@ -1,9 +1,28 @@ -import { Briefcase, ChevronDown, Code, Download, Eye, FileText, Folder, Pencil, Redo, Undo, Upload } from 'lucide-react'; +import { + Briefcase, + ChevronDown, + Code, + Download, + Eye, + FileText, + Folder, + Pencil, + Redo, + Undo, + Upload, +} from 'lucide-react'; import { ToolboxMenu } from './ToolboxMenu'; import { HeaderButton } from './HeaderButton'; import { ViewMenuItem } from './ViewMenuItem'; -import { toggleOptionsPanels, toggleViewPanel } from './viewMenuState.js'; -import type { HeaderMenuKey, HeaderSetViewConfig, HeaderTranslations, HeaderViewConfig } from './types'; +import { toggleOptionsPanel, toggleViewPanel } from './viewMenuState.js'; +import type { + HeaderMenuKey, + HeaderSetViewConfig, + HeaderTranslations, + HeaderViewAvailability, + HeaderViewConfig, + ToolboxItem, +} from './types'; interface HeaderMenusProps { activeMenu: HeaderMenuKey; @@ -14,14 +33,13 @@ interface HeaderMenusProps { showUndoRedoInline: boolean; t: HeaderTranslations; viewConfig: HeaderViewConfig; + viewAvailability: HeaderViewAvailability; setViewConfig: HeaderSetViewConfig; onImportFile: () => void; onImportFolder: () => void; onOpenExport: () => void; onExportProject: () => void; - onOpenAI: () => void; - onOpenMeasureTool: () => void; - onOpenCollisionOptimizer: () => void; + toolboxItems: ToolboxItem[]; onOpenCodeViewer: () => void; onPrefetchCodeViewer: () => void; undo: () => void; @@ -43,14 +61,13 @@ export function HeaderMenus({ showUndoRedoInline, t, viewConfig, + viewAvailability, setViewConfig, onImportFile, onImportFolder, onOpenExport, onExportProject, - onOpenAI, - onOpenMeasureTool, - onOpenCollisionOptimizer, + toolboxItems, onOpenCodeViewer, onPrefetchCodeViewer, undo, @@ -58,6 +75,8 @@ export function HeaderMenus({ canUndo, canRedo, }: HeaderMenusProps) { + const jointPanelVisible = viewConfig.showJointPanel && viewAvailability.jointPanel; + const toggleMenu = (menu: Exclude) => { setActiveMenu(activeMenu === menu ? null : menu); }; @@ -67,8 +86,8 @@ export function HeaderMenus({ setActiveMenu(null); }; - const handleToggleOptionsPanels = () => { - setViewConfig((prev) => toggleOptionsPanels(prev)); + const handleToggleOptionsPanel = () => { + setViewConfig((prev) => toggleOptionsPanel(prev)); setActiveMenu(null); }; @@ -85,7 +104,11 @@ export function HeaderMenus({ > {showMenuLabels && {t.file}} - {showMenuLabels && } + {showMenuLabels && ( + + )} {activeMenu === 'file' && ( @@ -98,7 +121,10 @@ export function HeaderMenus({ >
@@ -227,7 +264,11 @@ export function HeaderMenus({ > {showMenuLabels && {t.view}} - {showMenuLabels && } + {showMenuLabels && ( + + )} {activeMenu === 'view' && ( @@ -244,14 +285,15 @@ export function HeaderMenus({ onClick={() => handleToggleViewPanel('showToolbar')} /> handleToggleViewPanel('showJointPanel')} />
diff --git a/src/app/components/header/ToolboxMenu.test.tsx b/src/app/components/header/ToolboxMenu.test.tsx new file mode 100644 index 000000000..abfe1b61f --- /dev/null +++ b/src/app/components/header/ToolboxMenu.test.tsx @@ -0,0 +1,206 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import React, { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; +import { JSDOM } from 'jsdom'; +import { ScanSearch } from 'lucide-react'; + +import { translations } from '@/shared/i18n'; +import { ToolboxMenu } from './ToolboxMenu'; +import type { ToolboxItem } from './types'; + +type TestRoot = { + dom: JSDOM; + container: HTMLDivElement; + root: Root; +}; + +function installDom() { + const dom = new JSDOM('', { + url: 'http://localhost/', + pretendToBeVisual: true, + }); + + (globalThis as { window?: Window }).window = dom.window as unknown as Window; + (globalThis as { document?: Document }).document = dom.window.document; + Object.defineProperty(globalThis, 'navigator', { + value: dom.window.navigator, + configurable: true, + }); + Object.defineProperty(globalThis, 'localStorage', { + value: dom.window.localStorage, + configurable: true, + }); + + const matchMediaStub = (query: string) => ({ + matches: false, + media: query, + onchange: null, + addListener: () => {}, + removeListener: () => {}, + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => false, + }); + + (globalThis as { matchMedia?: typeof window.matchMedia }).matchMedia = matchMediaStub; + dom.window.matchMedia = matchMediaStub; + + (globalThis as { HTMLElement?: typeof HTMLElement }).HTMLElement = dom.window.HTMLElement; + (globalThis as { Event?: typeof Event }).Event = dom.window.Event; + (globalThis as { MouseEvent?: typeof MouseEvent }).MouseEvent = dom.window.MouseEvent; + (globalThis as { PointerEvent?: typeof PointerEvent }).PointerEvent = + dom.window.PointerEvent ?? dom.window.MouseEvent; + (globalThis as { getComputedStyle?: typeof getComputedStyle }).getComputedStyle = + dom.window.getComputedStyle.bind(dom.window); + (globalThis as { requestAnimationFrame?: typeof requestAnimationFrame }).requestAnimationFrame = + dom.window.requestAnimationFrame.bind(dom.window); + (globalThis as { cancelAnimationFrame?: typeof cancelAnimationFrame }).cancelAnimationFrame = + dom.window.cancelAnimationFrame.bind(dom.window); + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + + return dom; +} + +function createComponentRoot(): TestRoot { + const dom = installDom(); + const container = dom.window.document.createElement('div'); + dom.window.document.body.appendChild(container); + const root = createRoot(container); + return { dom, container, root }; +} + +function makeTestItems(overrides?: { + onIkTool?: () => void; + onRobogoClick?: () => void; +}): ToolboxItem[] { + return [ + { + key: 'ai-inspection', + title: translations.en.aiInspection, + description: translations.en.aiInspectionDesc, + icon: , + onClick: () => {}, + tone: 'primary', + }, + { + key: 'ik-tool', + title: translations.en.ikTool, + description: translations.en.ikToolboxDesc, + icon: , + onClick: overrides?.onIkTool ?? (() => {}), + tone: 'primary', + }, + { + key: 'robogo', + title: translations.en.robogo, + description: translations.en.robogoDesc, + icon: , + onClick: overrides?.onRobogoClick ?? (() => {}), + external: true, + tone: 'logo', + }, + ]; +} + +test('Toolbox menu exposes the IK entry and triggers its action', async () => { + const { dom, container, root } = createComponentRoot(); + let closed = false; + let openIk = false; + + const items = makeTestItems({ + onIkTool: () => { + openIk = true; + }, + }); + + await act(async () => { + root.render( + { + closed = true; + }} + items={items} + />, + ); + }); + + const ikButton = container.querySelector( + `button[aria-label="${translations.en.ikTool}"]`, + ); + assert.ok(ikButton, 'IK toolbox entry should render'); + + await act(async () => { + ikButton!.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true })); + }); + + assert.equal(openIk, true); + assert.equal(closed, true); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); + +test('Toolbox menu no longer renders the measure entry', async () => { + const { dom, container, root } = createComponentRoot(); + const items = makeTestItems(); + + await act(async () => { + root.render( {}} items={items} />); + }); + + const measureButton = container.querySelector( + `button[aria-label="${translations.en.measureMode}"]`, + ); + assert.equal(measureButton, null); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); + +test('Toolbox menu exposes the RoboGo external entry and closes on click', async () => { + const { dom, container, root } = createComponentRoot(); + let closed = false; + let robogoClicked = false; + + const items = makeTestItems({ + onRobogoClick: () => { + robogoClicked = true; + }, + }); + + await act(async () => { + root.render( + { + closed = true; + }} + items={items} + />, + ); + }); + + const robogoButton = container.querySelector( + `button[aria-label="${translations.en.robogo}"]`, + ); + assert.ok(robogoButton, 'RoboGo toolbox entry should render'); + + await act(async () => { + robogoButton!.dispatchEvent(new dom.window.MouseEvent('click', { bubbles: true })); + }); + + assert.equal(closed, true); + assert.equal(robogoClicked, true); + + await act(async () => { + root.unmount(); + }); + dom.window.close(); +}); diff --git a/src/app/components/header/ToolboxMenu.tsx b/src/app/components/header/ToolboxMenu.tsx index bbeef344f..faa5c0164 100644 --- a/src/app/components/header/ToolboxMenu.tsx +++ b/src/app/components/header/ToolboxMenu.tsx @@ -1,26 +1,12 @@ import React from 'react'; -import { Activity, ArrowUpRight, Box, RefreshCw, Ruler, ScanSearch } from 'lucide-react'; +import { ArrowUpRight } from 'lucide-react'; import type { TranslationKeys } from '@/shared/i18n/types'; -import { useEffectiveTheme } from '@/shared/hooks/useEffectiveTheme'; +import type { ToolboxItem } from './types'; interface ToolboxMenuProps { t: TranslationKeys; onClose: () => void; - onOpenAI: () => void; - onOpenMeasureTool: () => void; - onOpenCollisionOptimizer: () => void; -} - -type ToolboxItemTone = 'primary' | 'neutral' | 'logo'; - -interface ToolboxItem { - key: string; - title: string; - description: string; - icon: React.ReactNode; - onClick: () => void; - external?: boolean; - tone?: ToolboxItemTone; + items: ToolboxItem[]; } function ToolboxItemCard({ @@ -28,11 +14,13 @@ function ToolboxItemCard({ isActive, onHoverStart, onHoverEnd, + onClose, }: { item: ToolboxItem; isActive: boolean; onHoverStart: (item: ToolboxItem) => void; onHoverEnd: () => void; + onClose: () => void; }) { const iconToneClassName = item.tone === 'primary' @@ -44,13 +32,16 @@ function ToolboxItemCard({ return ( + +
+ } + className="z-[110] flex flex-col overflow-hidden rounded-2xl border border-border-black bg-panel-bg text-text-primary shadow-xl dark:bg-panel-bg" + headerClassName="h-12 border-b border-border-black flex items-center justify-between px-4 bg-element-bg shrink-0" + interactionClassName="select-none" + minimizeTitle={t.minimize} + maximizeTitle={t.maximize} + restoreTitle={t.restore} + closeTitle={t.close} + controlButtonClassName="p-1.5 hover:bg-element-hover rounded-md transition-colors" + closeButtonClassName="p-1.5 text-text-tertiary hover:bg-red-500 hover:text-white rounded-md transition-colors" + rightResizeHandleClassName="absolute right-0 top-0 bottom-0 w-2 cursor-ew-resize hover:bg-system-blue/15 active:bg-system-blue/25 transition-colors z-20" + bottomResizeHandleClassName="absolute bottom-0 left-0 right-0 h-2 cursor-ns-resize hover:bg-system-blue/15 active:bg-system-blue/25 transition-colors z-20" + cornerResizeHandleClassName="absolute bottom-0 right-0 w-6 h-6 cursor-nwse-resize hover:bg-system-blue/20 active:bg-system-blue/30 transition-colors z-30 flex items-center justify-center" + cornerResizeHandle={
} + > + {!isMinimized && ( +
+
+ {messages.length === 0 ? ( +
+
+ +
+
+
{headerTitle}
+

+ {isReportFollowup ? t.askAboutReport : t.aiConversationDesc} +

+
+
+
+ +
+
+
+ {t.examples} +
+
+ {t.conversationSuggestionsHint} +
+
+
+
+ {suggestedPrompts.map((prompt) => ( + + ))} +
+
+
+
+ ) : ( +
+ {messages.map((message, index) => { + if (!isConversationChatMessage(message)) { + return ( +
+
+ + {t.newConversationDividerLabel} + +
+
+ ); + } + + const messageKey = `${message.role}-${index}`; + const isCopied = copiedMessageKey === messageKey; + const isStreamingAssistant = + message.role === 'assistant' && index === messages.length - 1 && isSending; + + return ( +
+
+
+ {isStreamingAssistant && !message.content ? ( +
+ + {t.aiAnalyzing} +
+ ) : ( + <> + + {isStreamingAssistant && message.content && ( +
+ + {t.aiAnalyzing} +
+ )} + + )} +
+ {message.content && ( +
+ +
+ )} +
+
+ ); + })} + + + )} +
+ +
+
+