From fda3b84277ee04dd79c70411cf7ceb9aa6d17cba Mon Sep 17 00:00:00 2001 From: ddcat666 <26564566+ddcat666@users.noreply.github.com> Date: Wed, 30 Sep 2026 10:32:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20Web=20=E6=A3=80=E6=9F=A5=20-=20?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E6=8B=86=E5=88=86=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=B9=B6=E4=BF=AE=E6=AD=A3=E6=BA=90=E7=A0=81=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 拆分出的前端模块未按 Prettier 格式化导致 CI 格式检查失败;源码断言改为忽略空白差异,导演台引导组件恢复别名导入。同时移除误提交的分析草稿、pnpm 锁文件和调试测试。 --- PI_AGENT_FINAL_ANALYSIS.md | 504 ------- backend/agent-runtime/pi/pnpm-lock.yaml | 1342 ----------------- .../art-critique/ai-art-critique-report.tsx | 1 - .../canvas/canvas-mention-chips.tsx | 22 +- .../components/canvas/canvas-mention-menu.tsx | 54 +- .../canvas/canvas-node-media-content.tsx | 13 +- .../director/canvas-director-workbench.tsx | 2 +- .../canvas/director/director-inspectors.tsx | 482 +++++- .../director/director-viewport-capture.ts | 16 +- .../canvas/director/director-viewport-rig.ts | 86 +- .../components/admin-announcement-results.ts | 1 - .../settings/appearance-settings-parts.tsx | 1 - .../admin/settings/storage-settings-model.ts | 1 - web/src/pages/assets/asset-library-format.ts | 1 - .../pages/create/creation-workspace-empty.tsx | 169 ++- .../create/creation-workspace-history.tsx | 214 ++- .../create/creation-workspace-messages.tsx | 384 ++++- web/src/stores/config-model-options.ts | 1 - web/src/stores/use-config-store.ts | 15 +- web/test/create-library-button.test.ts | 2 +- .../director-template-mode-wiring.test.ts | 12 +- web/test/zz-frame-check.test.ts | 11 - 22 files changed, 1197 insertions(+), 2137 deletions(-) delete mode 100644 PI_AGENT_FINAL_ANALYSIS.md delete mode 100644 backend/agent-runtime/pi/pnpm-lock.yaml delete mode 100644 web/test/zz-frame-check.test.ts diff --git a/PI_AGENT_FINAL_ANALYSIS.md b/PI_AGENT_FINAL_ANALYSIS.md deleted file mode 100644 index b2e12bf90..000000000 --- a/PI_AGENT_FINAL_ANALYSIS.md +++ /dev/null @@ -1,504 +0,0 @@ -# Pi Agent 生产问题完整分析与最优解决方案 - -## 问题清单(根据对话历史重构) - -### 已完成修复 - -#### 1. ✅ stdout/Wait 竞态(生产诊断号 ag3a1b9dedb21d5903bdfcc304c59da938) -**问题**:正常结束的 Agent 会话报 `file already closed` - -**根因**:`cmd.Wait()` 在 stdout scanner 读完前被调用,提前关闭了 pipe - -**解决方案**: -- Scanner goroutine 读到 EOF 后再调用 `Wait()` -- 遇到 `runtime_error` 时记录但继续读取,不提前返回 -- 添加带真实 Node 的回归测试 - -**影响**:消除正常结束被误判为失败的竞态 - ---- - -#### 2. ✅ 运行时启动崩溃(本地复现诊断号 agf2bae258863606bbe7056d0ac58be6b7) -**问题**:Agent 启动后 1 秒内崩溃,报 `Cannot read properties of null (reading 'startsWith')` - -**根因**: -- 第一次修复时传了 `authPath: null`,SDK 调用 `null.startsWith()` 崩溃 -- 第二次修复用 `/dev/null`,SDK 尝试创建 `/dev/null.lock` 失败(`EPERM`) - -**最优解决方案**:每次运行创建独立临时目录 -```javascript -const isolatedDir = await mkdtemp(join(tmpdir(), "agent-runtime-")); -process.env.HOME = isolatedDir; -process.env.PI_CODING_AGENT_DIR = isolatedDir; -process.on("exit", () => rmSync(isolatedDir, { recursive: true, force: true })); - -const modelRuntime = await ModelRuntime.create({ - authPath: join(isolatedDir, "auth.json"), - modelsPath: join(isolatedDir, "models.json"), -}); -``` - -**优势**: -- 每轮完全隔离,不读取宿主 `~/.pi` -- SDK 能正常创建锁文件 -- 进程退出自动清理 - ---- - -#### 3. ✅ 安全:工具白名单 + 环境隔离 -**问题**: -- SDK 默认启用 `read/bash/edit/write` 内置工具 -- 子进程继承后端全部环境变量(DATABASE_URL、密钥) -- SDK 会读取 `~/.pi/agent/auth.json`(支持 `!command` 执行 shell) -- Resource loader 会探索文件系统 - -**最优解决方案**: - -**Node 侧**: -```javascript -// 1. 只启用平台声明的工具 -const platformToolNames = tools.map(t => t.name); -sessionConfig.tools = platformToolNames; - -// 2. 严格隔离资源加载 -const resourceLoader = new DefaultResourceLoader({ - noExtensions: true, - noSkills: true, - noPromptTemplates: true, - noThemes: true, - noContextFiles: true, - settingsManager: SettingsManager.inMemory({}), -}); -``` - -**Go 侧**: -```go -// 1. 环境变量白名单 -cmd.Env = []string{ - "PATH=" + os.Getenv("PATH"), - "HOME=" + isolatedDir, - "NODE_ENV=production", - "PI_OFFLINE=1", -} - -// 2. 工具名称双重校验 -allowedTools := make(map[string]bool) -for _, tool := range state.Canonical.Tools { - if fn, ok := tool["function"].(map[string]interface{}); ok { - allowedTools[stringValue(fn["name"])] = true - } -} -if !allowedTools[call.Function.Name] { - return nil, fmt.Errorf("未声明的工具: %s", call.Function.Name) -} -``` - -**影响**: -- 关闭 SDK 内置工具 -- 子进程无法访问数据库凭证 -- 即使 Node 侧绕过,Go 侧也会拦截 - ---- - -#### 4. ✅ 媒体审批统一 -**问题**:前端说"图片、视频始终先审批",后端 auto 模式直接提交 - -**最优解决方案**: -```go -// 媒体工具在所有模式下都进入审批 -if allowed && cloudAgentWrite(call.Function.Name) && - (state.Request.PermissionMode == "request_approval" || mediaTool) && - state.Approval == nil { - // 进入审批流程 -} - -// auto 模式只豁免非媒体的画布修改 -if plan != nil && state.Request.PermissionMode == "auto" && !mediaTool { - // 直接提交 -} -``` - -更新提示词和工具说明: -- `agent-media-policy.md` 版本升到 5 -- 明确"图片、视频在所有权限模式下都先审批" -- 更新 `generate_media`、`image_layer_split` 工具描述 - -**影响**: -- 前后端行为一致 -- 避免用户扣费争议 -- auto 模式仍可快速修改文本节点和连线 - ---- - -### 暂不实现(需要更深入设计) - -#### 5. ⏸️ yingce-agent 独立容器拆分 - -**当前问题**: -- 后端没有读 `YINGCE_AGENT_URL`,仍是内嵌 Node 子进程 -- 桥接只监听 `127.0.0.1`,容器间无法通信 -- Dockerfile 复制宿主 `node_modules`,不能跨平台构建 -- 已修改 `docker-compose.deploy.yml` 要求提供镜像,会导致生产启动失败 - -**最优架构**: - -``` -┌─────────────────────┐ ┌──────────────────┐ -│ Go Backend │ │ yingce-agent │ -│ - 业务逻辑 │ ──HTTP──> │ (Node HTTP) │ -│ - 权限审批 │ <──JSON── │ - Pi runtime │ -│ - 计费扣款 │ │ - 会话管理 │ -└─────────────────────┘ └──────────────────┘ - ↓ ↓ - if YINGCE_AGENT_URL 带鉴权的网络桥接 - → HTTP client 监听 0.0.0.0:8081 - else 验证 Bridge-Token - → exec Node subprocess - 监听 127.0.0.1:随机端口 -``` - -**实现步骤**: - -**1. 网络桥接改造**: -```go -// runtime.go -func Run(ctx context.Context, request ProcessRequest, bridge Bridge) error { - // 桥接监听改为 0.0.0.0:0,支持容器间通信 - listener, err := net.Listen("tcp", "0.0.0.0:0") - - // 添加 token 验证中间件 - mux.Use(func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("Bridge-Token") != token { - http.Error(w, "Unauthorized", http.StatusUnauthorized) - return - } - next.ServeHTTP(w, r) - }) - }) -} -``` - -**2. HTTP 客户端**: -```go -// backend/internal/app/cloud_agent_runtime.go -func (s *Service) runCloudAgentModelStep(...) { - agentURL := os.Getenv("YINGCE_AGENT_URL") - - if agentURL != "" { - // 使用 HTTP 客户端 - client := yingceagent.NewClient(agentURL) - return client.Run(ctx, request) - } - - // 降级到内嵌模式 - return runtime.Run(ctx, request, bridge) -} -``` - -**3. yingce-agent HTTP 服务**: -```javascript -// yingce-agent/server.mjs -import express from 'express'; -import { spawn } from 'child_process'; - -app.post('/v1/agent/run', async (req, res) => { - const { bridgeURL, bridgeToken, sessionJSONL, prompt, tools, model } = req.body; - - // 验证请求来源(可选:mTLS 或共享密钥) - const authHeader = req.headers['authorization']; - if (!validateAuth(authHeader)) { - return res.status(401).json({ error: 'Unauthorized' }); - } - - // 启动 agent-runtime.mjs 子进程 - const child = spawn('node', ['agent-runtime.mjs'], { - env: { PATH: process.env.PATH, NODE_ENV: 'production', PI_OFFLINE: '1' } - }); - - child.stdin.write(JSON.stringify({ bridgeURL, bridgeToken, ... })); - child.stdin.end(); - - // 等待完成并返回结果 - const result = await collectOutput(child); - res.json(result); -}); -``` - -**4. Dockerfile 改造**: -```dockerfile -# 多阶段构建,自包含 node_modules -FROM node:22-alpine AS builder -WORKDIR /build -COPY backend/agent-runtime/pi/package*.json ./ -RUN npm ci --omit=dev -COPY backend/agent-runtime/pi/ ./ - -FROM node:22-alpine -WORKDIR /app -COPY --from=builder /build /app/agent-runtime -COPY yingce-agent/ /app/ -EXPOSE 8081 -CMD ["node", "server.mjs"] -``` - -**5. Compose 改造**: -```yaml -services: - yingce-agent: - image: ${CANVAS_YINGCE_AGENT_IMAGE} - environment: - MAX_CONCURRENT_SESSIONS: ${YINGCE_AGENT_MAX_SESSIONS:-10} - deploy: - replicas: ${YINGCE_AGENT_REPLICAS:-2} - # 不需要 depends_on,可以独立启动 - - backend: - environment: - YINGCE_AGENT_URL: ${YINGCE_AGENT_URL:-http://yingce-agent:8081} - # backend 自动降级,即使 yingce-agent 不可用也能用内嵌模式 -``` - -**优势**: -- **开发模式**:不设 `YINGCE_AGENT_URL`,自动用内嵌 Node 子进程 -- **生产模式**:设置环境变量,自动切换到 HTTP 客户端 -- **向后兼容**:即使 yingce-agent 故障,后端降级到内嵌模式 -- **弹性伸缩**:yingce-agent 可独立水平扩展 - -**为什么暂不实现**: -- 需要完整的网络鉴权设计 -- 需要健康检查和降级逻辑 -- 需要端到端测试验证容器间通信 -- 当前修复已解决核心问题,拆分不是紧急需求 - ---- - -#### 6. ⏸️ 恢复幂等性(反馈第 4 项) -**问题**:重启后同一 `idempotencyKey` 可能重复建单 - -**根因**:任务 ID 生成依赖内存状态(`state.Generations++`) - -**最优解决方案**: -```go -// 使用确定性 ID:用户 ID + 运行 ID + 调用索引 + 幂等键 -taskID := deterministicTaskID(userID, runID, state.CallIndex, call.Function.Arguments) - -func deterministicTaskID(userID, runID string, index int, args string) string { - hash := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%d:%s", userID, runID, index, args))) - return "ag" + hex.EncodeToString(hash[:])[:30] -} -``` - -**优势**: -- 重启后相同调用生成相同 ID -- 数据库主键冲突自动去重 -- 不需要额外的幂等表 - ---- - -#### 7. ⏸️ 媒体 waiter 恢复逻辑(反馈第 5 项) -**问题**:审批恢复后,旧 waiter 可能仍在轮询 - -**最优解决方案**: -```go -// 使用带取消的 context,checkpoint 里记录 waiter ID -type CloudAgentState struct { - MediaWaiterID string `json:"mediaWaiterId,omitempty"` -} - -func (s *Service) startApprovedCloudAgentMediaWaiter(ctx context.Context, userID, runID, taskID string) { - waiterID := generateID() - waiterCtx, cancel := context.WithCancel(ctx) - - s.mediaWaitersMu.Lock() - if existing, ok := s.mediaWaiters[runID]; ok { - existing.cancel() // 取消旧 waiter - } - s.mediaWaiters[runID] = &mediaWaiter{ctx: waiterCtx, cancel: cancel, id: waiterID} - s.mediaWaitersMu.Unlock() - - // 记录到 checkpoint - s.repo.MutateCloudAgent(userID, runID, ..., func(current *model.CloudAgentExecution, ...) { - state.MediaWaiterID = waiterID - }) - - // 轮询 - go func() { - defer cancel() - for { - select { - case <-waiterCtx.Done(): - return - case <-time.After(5 * time.Second): - // 检查任务状态 - } - } - }() -} -``` - ---- - -#### 8. ⏸️ 审批竞态(反馈第 6 项) -**问题**:审批决策与运行推进并发时可能冲突 - -**最优解决方案**:乐观锁 + 重试 -```go -func (s *Service) DecideCloudAgentApproval(userID, id, approvalID, decision string) error { - for attempt := 0; attempt < 3; attempt++ { - run, err := s.repo.CloudAgent(userID, id) - if err != nil { - return err - } - - err = s.repo.MutateCloudAgent(userID, id, run.Revision, func(current *model.CloudAgentExecution, ...) { - state, _ := cloudAgentDecode(current) - - // 检查审批是否仍然有效 - if state.Approval == nil || state.Approval.ID != approvalID { - return fmt.Errorf("approval already resolved") - } - - state.Approval.Decision = decision - state.Decisions[approvalID] = decision - return cloudAgentSave(current, state) - }) - - if err == nil || !errors.Is(err, repository.ErrRevisionConflict) { - return err - } - - time.Sleep(time.Duration(attempt+1) * 100 * time.Millisecond) - } - - return fmt.Errorf("审批决策冲突,请重试") -} -``` - ---- - -#### 9. ⏸️ Node fetch 超时(反馈第 7 项) -**问题**:默认 300s 超时对长时间媒体生成不够 - -**最优解决方案**:分层超时 -```javascript -// agent-runtime.mjs -const bridgeTimeout = request.bridgeTimeout ?? 600000; // 默认 10 分钟 -const result = await fetch(bridgeURL, { - signal: AbortSignal.timeout(bridgeTimeout), - // ... -}); -``` - -```go -// cloud_agent_runtime.go -request.BridgeTimeout = 15 * time.Minute // 媒体生成给 15 分钟 -``` - ---- - -#### 10. ⏸️ 并发上限(反馈第 10 项) -**最优解决方案**:环境变量 + 排队 -```go -var ( - maxConcurrentAgents = getEnvInt("CANVAS_MAX_CONCURRENT_AGENTS", 100) - agentSemaphore = make(chan struct{}, maxConcurrentAgents) -) - -func (s *Service) runCloudAgentModelStep(...) error { - select { - case agentSemaphore <- struct{}{}: - defer func() { <-agentSemaphore }() - case <-ctx.Done(): - return fmt.Errorf("等待 Agent 资源超时") - } - - // 执行 -} -``` - ---- - -#### 11. ⏸️ 内存上限(反馈第 11 项) -**最优解决方案**:Node 启动参数 -```go -cmd := exec.CommandContext(ctx, "node", - "--max-old-space-size=2048", // 限制 2GB - filepath.Join(runtimeDir, "agent-runtime.mjs")) -``` - ---- - -#### 12. ⏸️ Session JSONL 增量写入(反馈第 12 项) -**问题**:会话文件可能几 MB,每次全量读写低效 - -**最优解决方案**:只传增量 -```javascript -// SDK 已支持 sessionFile 增量追加 -const sessionConfig = { - sessionManager: new SessionManager({ - sessionFile: request.sessionFile, // SDK 会追加写入 - }), -}; -``` - -```go -// Go 侧只传文件路径,不传全文 -request.SessionFile = filepath.Join(sessionDir, runID+".jsonl") -// 不再设置 request.SessionJSONL -``` - ---- - -## 下一步建议 - -### 立即提交(核心修复) -1. ✅ stdout/Wait 竞态修复 -2. ✅ 运行时启动崩溃修复 -3. ✅ 安全加固(工具白名单 + 环境隔离) -4. ✅ 媒体审批统一 - -### 不要删除独立容器 -`yingce-agent/` 和 `backend/internal/agent/yingceagent/` 是要求保留的独立部署实现。 -不要因为实现不完整就删除目录或撤掉 Compose、镜像构建和 Host Updater 集成;缺的部分应补上。 -远程服务失败时不要自动退回内嵌 Node,否则同一步可能执行两次。 - -### 后续迭代(按优先级) -1. 恢复幂等性(防止重复建单) -2. 审批竞态修复(乐观锁) -3. 媒体 waiter 恢复逻辑 -4. yingce-agent 独立容器(完整实现) -5. 并发上限 + 内存上限 -6. Session JSONL 增量写入 - ---- - -## 提交清单 - -**核心修复**: -```bash -git add backend/agent-runtime/pi/agent-runtime.mjs -git add backend/internal/agent/runtime/runtime.go -git add backend/internal/agent/runtime/runtime_test.go -git add backend/internal/app/cloud_agent_pi_coordinator.go -git add backend/internal/app/cloud_agent_runtime.go -git add backend/internal/app/cloud_agent_media_test.go -git add backend/internal/app/cloud_agent_single_active_task_test.go -git add backend/internal/app/cloud_agent_tools.go -git add backend/internal/prompts/agent-media-policy.md -git add backend/internal/prompts/agent_policy_test.go -git add docs/AGENT_LOCAL_DEVELOPMENT.md -git add AGENTS.md -``` - -**排除**: -```bash -git restore backend/server -git restore .github/workflows/publish-images.yml -git restore docker-compose.deploy.yml -git restore backend/internal/hostupdate/manager_ops.go -rm -rf yingce-agent/ backend/internal/agent/yingceagent/ -rm -f AGENT_RUNTIME_*.md *.md -``` - -你想让我先撤回 yingce-agent 相关改动,还是继续完成完整的独立容器实现? diff --git a/backend/agent-runtime/pi/pnpm-lock.yaml b/backend/agent-runtime/pi/pnpm-lock.yaml deleted file mode 100644 index ea08f7b82..000000000 --- a/backend/agent-runtime/pi/pnpm-lock.yaml +++ /dev/null @@ -1,1342 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@earendil-works/pi-ai': - specifier: 0.87.1 - version: 0.87.1(ws@8.22.0) - '@earendil-works/pi-coding-agent': - specifier: 0.87.1 - version: 0.87.1(ws@8.22.0) - typebox: - specifier: 1.3.27 - version: 1.3.27 - -packages: - - '@anthropic-ai/sdk@0.124.0': - resolution: {integrity: sha512-cN5O8i9UVxHeOQAzj/XjshWXG8KiibJDw9OGpH2Z/eR3n/RBxdoLxDJOcfqAJWvjaMDFfHTBADU04hWRJVkDyA==} - hasBin: true - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - peerDependenciesMeta: - zod: - optional: true - - '@aws-sdk/client-bedrock-runtime@3.1127.0': - resolution: {integrity: sha512-IDl/lrPb90aH+pZFHGNDmgH9nAUQj5PlZH1sJ3w7RikctyjHSnY3oNjZhrLoaBoQn/rNK0zsP6OHEqEhj2tdLA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/core@3.978.1': - resolution: {integrity: sha512-LbY9aGsEiznDWmUc30Nwv3aIX/+dbwTx8KfS0yOC3NPYMO+O91e6jkT1azf34FwjOndq8/Q+RcVVZz5xnerwdg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-env@3.972.72': - resolution: {integrity: sha512-xTKO/FWJPozTIXbozVnVGoNBhaGba8TBcx+KyUjRVeOlXE+dUc7GTR1cLvu0uTdIdmemzaFbqqCshXeZA1fZew==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-http@3.972.74': - resolution: {integrity: sha512-u91E/hT8f4d1xy0Jl7VG4nVKJ3lxbrZkoBTeSVoJdWBiSEUMwMS/9+e0H/aJVQV//Lt5wuzP+E69v4aRSsNTmw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-ini@3.973.17': - resolution: {integrity: sha512-ged4KXdBkvIC81bLvNHHuQKdKak/VXhQTR1NWYTTqW0474nlmsxy9O/vlgTIohDDWH3xpBdtVMZRyjb+DnocDA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-login@3.972.79': - resolution: {integrity: sha512-L+Z85anONJd8MaiuraO4wRxATCdEejBZ3K3eymzWI5JPXa9sOS9CkIm72PBKqXKX+Z9p9NGMX5AIMXm0LEflgw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-node@3.972.84': - resolution: {integrity: sha512-oHt854odINVwzwsh+c5x69j0ajm4DbqqqVJ+O1ECsCIZeMDAbzFpXItaqP7UZstJj/ATdTk/KFSH0LaNAgV+kA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-process@3.972.72': - resolution: {integrity: sha512-rLIp2xbMjX/k9/od7APpqq1ZgXXnV0pOL1Th3ZsL8Wu0TRtBsDTVS8iPqcfRFcHakFxPvR04OSTv2ka2qOb/2A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-sso@3.973.16': - resolution: {integrity: sha512-IGihaJfFZYacJJr/odqILCoK7W/mvrZ7cuK7ECn3sAu4vLC6u0V8bS7mCGbdugJ8Aum2tnvqmx0F2MRFp2rn9g==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-web-identity@3.972.78': - resolution: {integrity: sha512-/y9WvNtlcPBGLR0qc1a+9J/xtYZfVczvLUOuXaVWylzttH7ewsxwHtjmiJSolNrVSDorIxHGHMU61CbonRkmwA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/eventstream-handler-node@3.972.35': - resolution: {integrity: sha512-a8xilRoRaalvSPZdfrs0VY3/BPc0uYhXW56Z/k6nmZ5Vk430LHeowf9APIPq8utM3tMJSNGSSuJMlB9YrPsN+A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-eventstream@3.972.30': - resolution: {integrity: sha512-B6gvZlcnRBNWraKgyEjKsbhv+VjtbyHmpU7hmtTHzXpDSxHPpCJnYLpglEOOsF+SFne20DbZC5IVC1aNr2Pb4A==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/middleware-websocket@3.972.54': - resolution: {integrity: sha512-bhESdru8u8KosziH8jcVta/NM/dNreeRz2+2aU86si5bfBYOiJ6IiqD+9U4URUcU5WJO3Yw+jO3GLDIHlscXMQ==} - engines: {node: '>= 14.0.0'} - - '@aws-sdk/nested-clients@3.997.46': - resolution: {integrity: sha512-oRxtBcka/JGHGs9l9p9IVajGoTP8vTPmoAzdHGy4Qcy9P5vPnDf6nhIeM/COQNY9k/OahImTRaLkHftoXvfcmQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/signature-v4-multi-region@3.996.47': - resolution: {integrity: sha512-Zk08macMvQTHzQJCLJVkOlviVoqwYMrpXv4lmLN7b7sAbiMoOK7Go0NYdR5UeF+MW8LIbRmwrNy9u/5VvX1U5g==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/token-providers@3.1127.0': - resolution: {integrity: sha512-Dv2TMWBshJ+tF6ahs2Sy5bh4Iabsd4GAQqVvE9XZmYmnoaVbpS2QKIKE/HRacc7bTtbjEEvP+laGzHvHlf1CiQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/token-providers@3.1138.0': - resolution: {integrity: sha512-GpyAr0DD63YOEmYFM6Df+gJuIgC92MMTiBK4FTKfxii5MJ9ge20epR7LyroulscYlG89J+ZB2ivFDPjvfQhzdw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/types@3.974.6': - resolution: {integrity: sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/xml-builder@3.972.41': - resolution: {integrity: sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==} - engines: {node: '>=20.0.0'} - - '@aws/lambda-invoke-store@0.3.0': - resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} - engines: {node: '>=18.0.0'} - - '@babel/runtime@7.29.7': - resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} - engines: {node: '>=6.9.0'} - - '@earendil-works/chord@0.87.1': - resolution: {integrity: sha512-bg7IkJGFcEaMqqYgOGUiq5Ky9RghpRfrlZ8I/v/1b4bBZ02A7t3E+6uhPRbadwWb/kWsnVFbZsqOKRN4a3LLCg==} - engines: {node: '>=22.19.0'} - - '@earendil-works/pi-agent-core@0.87.1': - resolution: {integrity: sha512-Zev3B0HK7YS5A4EZQ2XnEqiJuirx6QBiltJ+LpmjV5a/+2IU0cfKtIfnkNkORK707XOvKBY2WRtk7cAwHpbh2Q==} - engines: {node: '>=22.19.0'} - - '@earendil-works/pi-ai@0.87.1': - resolution: {integrity: sha512-X/3PfQBnnoeVdO9Cv8zHghUMglzlgNZYGNzoPnbRoGnHl3Rw3TlA2UKSUB7BRHUOxMryHXYa8dnjWZlbRheDZA==} - engines: {node: '>=22.19.0'} - hasBin: true - - '@earendil-works/pi-coding-agent@0.87.1': - resolution: {integrity: sha512-m8ArJUtVcQMSe1lLE/Ei7vX/JV7O39sWmWBsXV2NOU70F0qCp8GubA24pT3LnwTmM6LL2xV80/h6sQg85n69ew==} - engines: {node: '>=22.19.0'} - hasBin: true - - '@earendil-works/pi-telemetry@0.87.1': - resolution: {integrity: sha512-MC6TRQH5lgMXpcN+Vku2WMI2T8BsiUPzMQHGo81uqFZD3/9O79WWJAysEDGuzduP6R4tvtgwMLwmqIxynM10JQ==} - engines: {node: '>=22.19.0'} - - '@earendil-works/pi-tui@0.87.1': - resolution: {integrity: sha512-YEH2vRyOeiO7hhN6j6AE6YwKSq2Kz2f3XR8bj1TbR+aGE/JsnY1hLPMI2pvaZfRM1n9Y00tejxFQ4zbzvF7nkQ==} - engines: {node: '>=22.19.0'} - - '@esbuild/aix-ppc64@0.28.2': - resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.2': - resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.2': - resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.2': - resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.2': - resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.2': - resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.2': - resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.2': - resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.2': - resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.2': - resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.2': - resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.2': - resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.2': - resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.2': - resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.2': - resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.2': - resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.2': - resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.2': - resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.2': - resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.2': - resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.2': - resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.2': - resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.2': - resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.2': - resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.2': - resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.2': - resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@google/genai@2.21.0': - resolution: {integrity: sha512-+PDtco2/Z0ONdzCGekCoCT+O1VJS9xJQNN4XzQpXG/t3El/SWWMkCWlFRO1KmivOHPa4Q0VjUYu1HBKCZ/v33Q==} - engines: {node: '>=20.0.0'} - peerDependencies: - '@modelcontextprotocol/sdk': ^1.25.2 - peerDependenciesMeta: - '@modelcontextprotocol/sdk': - optional: true - - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.5': - resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} - - '@protobufjs/eventemitter@1.1.1': - resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} - - '@protobufjs/fetch@1.1.1': - resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.2': - resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} - - '@silvia-odwyer/photon-node@0.3.4': - resolution: {integrity: sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==} - - '@smithy/core@3.35.0': - resolution: {integrity: sha512-zRMhfkByhT2snNdr1si24vJitU6Cr9ix2MikUfWmkAgp4jrNP0GcKSP5YvwQ+TlI8AZXER5QOGJn3JsVtSD9/A==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.5.2': - resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} - engines: {node: '>=18.0.0'} - - '@smithy/fetch-http-handler@5.8.0': - resolution: {integrity: sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.12.1': - resolution: {integrity: sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==} - engines: {node: '>=18.0.0'} - - '@smithy/signature-v4@5.7.4': - resolution: {integrity: sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==} - engines: {node: '>=18.0.0'} - - '@smithy/types@4.19.0': - resolution: {integrity: sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==} - engines: {node: '>=18.0.0'} - - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} - - '@types/node@26.6.3': - resolution: {integrity: sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==} - - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} - engines: {node: '>= 14'} - - agent-base@9.0.0: - resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} - engines: {node: '>= 20'} - - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - - bowser@2.14.1: - resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - - brace-expansion@5.0.12: - resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} - engines: {node: 20 || >=22} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - chalk@6.0.0: - resolution: {integrity: sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==} - engines: {node: '>=22'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - diff@8.0.4: - resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} - engines: {node: '>=0.3.1'} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - esbuild@0.28.2: - resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} - engines: {node: '>=18'} - hasBin: true - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - fast-sha256@1.3.0: - resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} - - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - - gaxios@7.3.1: - resolution: {integrity: sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==} - engines: {node: '>=18'} - - gcp-metadata@8.1.2: - resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} - engines: {node: '>=18'} - - get-east-asian-width@1.6.0: - resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} - engines: {node: '>=18'} - - google-auth-library@10.9.1: - resolution: {integrity: sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw==} - engines: {node: '>=18'} - - google-logging-utils@1.1.3: - resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} - engines: {node: '>=14'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - grok-mermaid@0.2.3: - resolution: {integrity: sha512-/4KopAbsjvuRP9MdPtlDjOHUmUVEohOX73JNcsWpzAtFxh+bq5+Dhb6gzvRieLDwIPQIR3/vy8V1NNTuz4Zsmg==} - engines: {node: '>=18'} - - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - - hosted-git-info@9.0.3: - resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} - engines: {node: ^20.17.0 || >=22.9.0} - - http-proxy-agent@9.1.0: - resolution: {integrity: sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==} - engines: {node: '>= 20'} - - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - - https-proxy-agent@9.1.0: - resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} - engines: {node: '>= 20'} - - ignore@7.0.8: - resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==} - engines: {node: '>= 4'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jiti@2.7.0: - resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} - hasBin: true - - json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - - json-schema-to-ts@3.1.1: - resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} - engines: {node: '>=16'} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - - lru-cache@11.5.3: - resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} - engines: {node: 20 || >=22} - - marked@18.0.11: - resolution: {integrity: sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==} - engines: {node: '>= 20'} - hasBin: true - - minimatch@10.2.6: - resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} - engines: {node: 18 || 20 || >=22} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - openai@6.40.0: - resolution: {integrity: sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==} - peerDependencies: - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - - partial-json@0.1.7: - resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - proper-lockfile@4.1.2: - resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - - protobufjs@7.6.6: - resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} - engines: {node: '>=12.0.0'} - - proxy-agent-negotiate@1.1.0: - resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==} - engines: {node: '>= 20'} - peerDependencies: - kerberos: ^2.0.0 - peerDependenciesMeta: - kerberos: - optional: true - - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} - engines: {node: '>=10'} - hasBin: true - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - standardwebhooks@1.1.1: - resolution: {integrity: sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==} - - ts-algebra@2.0.0: - resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - typebox@1.3.27: - resolution: {integrity: sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==} - - undici-types@8.9.0: - resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - - undici@8.10.2: - resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} - engines: {node: '>=22.19.0'} - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - ws@8.22.0: - resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - yaml@2.9.0: - resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} - engines: {node: '>= 14.6'} - hasBin: true - -snapshots: - - '@anthropic-ai/sdk@0.124.0': - dependencies: - json-schema-to-ts: 3.1.1 - standardwebhooks: 1.1.1 - - '@aws-sdk/client-bedrock-runtime@3.1127.0': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/credential-provider-node': 3.972.84 - '@aws-sdk/eventstream-handler-node': 3.972.35 - '@aws-sdk/middleware-eventstream': 3.972.30 - '@aws-sdk/middleware-websocket': 3.972.54 - '@aws-sdk/token-providers': 3.1127.0 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/fetch-http-handler': 5.8.0 - '@smithy/node-http-handler': 4.12.1 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/core@3.978.1': - dependencies: - '@aws-sdk/types': 3.974.6 - '@aws-sdk/xml-builder': 3.972.41 - '@aws/lambda-invoke-store': 0.3.0 - '@smithy/core': 3.35.0 - '@smithy/signature-v4': 5.7.4 - '@smithy/types': 4.19.0 - bowser: 2.14.1 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-env@3.972.72': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-http@3.972.74': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/fetch-http-handler': 5.8.0 - '@smithy/node-http-handler': 4.12.1 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-ini@3.973.17': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/credential-provider-env': 3.972.72 - '@aws-sdk/credential-provider-http': 3.972.74 - '@aws-sdk/credential-provider-login': 3.972.79 - '@aws-sdk/credential-provider-process': 3.972.72 - '@aws-sdk/credential-provider-sso': 3.973.16 - '@aws-sdk/credential-provider-web-identity': 3.972.78 - '@aws-sdk/nested-clients': 3.997.46 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/credential-provider-imds': 4.5.2 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-login@3.972.79': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/nested-clients': 3.997.46 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-node@3.972.84': - dependencies: - '@aws-sdk/credential-provider-env': 3.972.72 - '@aws-sdk/credential-provider-http': 3.972.74 - '@aws-sdk/credential-provider-ini': 3.973.17 - '@aws-sdk/credential-provider-process': 3.972.72 - '@aws-sdk/credential-provider-sso': 3.973.16 - '@aws-sdk/credential-provider-web-identity': 3.972.78 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/credential-provider-imds': 4.5.2 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-process@3.972.72': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-sso@3.973.16': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/nested-clients': 3.997.46 - '@aws-sdk/token-providers': 3.1138.0 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-web-identity@3.972.78': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/nested-clients': 3.997.46 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/eventstream-handler-node@3.972.35': - dependencies: - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-eventstream@3.972.30': - dependencies: - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-websocket@3.972.54': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/fetch-http-handler': 5.8.0 - '@smithy/signature-v4': 5.7.4 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/nested-clients@3.997.46': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/signature-v4-multi-region': 3.996.47 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/fetch-http-handler': 5.8.0 - '@smithy/node-http-handler': 4.12.1 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/signature-v4-multi-region@3.996.47': - dependencies: - '@aws-sdk/types': 3.974.6 - '@smithy/signature-v4': 5.7.4 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/token-providers@3.1127.0': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/nested-clients': 3.997.46 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/token-providers@3.1138.0': - dependencies: - '@aws-sdk/core': 3.978.1 - '@aws-sdk/nested-clients': 3.997.46 - '@aws-sdk/types': 3.974.6 - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/types@3.974.6': - dependencies: - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws-sdk/xml-builder@3.972.41': - dependencies: - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@aws/lambda-invoke-store@0.3.0': {} - - '@babel/runtime@7.29.7': {} - - '@earendil-works/chord@0.87.1': - dependencies: - esbuild: 0.28.2 - - '@earendil-works/pi-agent-core@0.87.1(ws@8.22.0)': - dependencies: - '@earendil-works/chord': 0.87.1 - '@earendil-works/pi-ai': 0.87.1(ws@8.22.0) - '@earendil-works/pi-telemetry': 0.87.1 - diff: 8.0.4 - ignore: 7.0.8 - typebox: 1.3.27 - yaml: 2.9.0 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - kerberos - - supports-color - - utf-8-validate - - ws - - zod - - '@earendil-works/pi-ai@0.87.1(ws@8.22.0)': - dependencies: - '@anthropic-ai/sdk': 0.124.0 - '@aws-sdk/client-bedrock-runtime': 3.1127.0 - '@earendil-works/pi-telemetry': 0.87.1 - '@google/genai': 2.21.0 - '@smithy/node-http-handler': 4.12.1 - http-proxy-agent: 9.1.0 - https-proxy-agent: 9.1.0 - openai: 6.40.0(ws@8.22.0) - partial-json: 0.1.7 - typebox: 1.3.27 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - kerberos - - supports-color - - utf-8-validate - - ws - - zod - - '@earendil-works/pi-coding-agent@0.87.1(ws@8.22.0)': - dependencies: - '@earendil-works/chord': 0.87.1 - '@earendil-works/pi-agent-core': 0.87.1(ws@8.22.0) - '@earendil-works/pi-ai': 0.87.1(ws@8.22.0) - '@earendil-works/pi-tui': 0.87.1 - '@silvia-odwyer/photon-node': 0.3.4 - chalk: 6.0.0 - cross-spawn: 7.0.6 - diff: 8.0.4 - grok-mermaid: 0.2.3 - highlight.js: 10.7.3 - hosted-git-info: 9.0.3 - ignore: 7.0.8 - jiti: 2.7.0 - minimatch: 10.2.6 - proper-lockfile: 4.1.2 - semver: 7.8.5 - typebox: 1.3.27 - undici: 8.10.2 - yaml: 2.9.0 - transitivePeerDependencies: - - '@modelcontextprotocol/sdk' - - bufferutil - - kerberos - - supports-color - - utf-8-validate - - ws - - zod - - '@earendil-works/pi-telemetry@0.87.1': {} - - '@earendil-works/pi-tui@0.87.1': - dependencies: - get-east-asian-width: 1.6.0 - marked: 18.0.11 - - '@esbuild/aix-ppc64@0.28.2': - optional: true - - '@esbuild/android-arm64@0.28.2': - optional: true - - '@esbuild/android-arm@0.28.2': - optional: true - - '@esbuild/android-x64@0.28.2': - optional: true - - '@esbuild/darwin-arm64@0.28.2': - optional: true - - '@esbuild/darwin-x64@0.28.2': - optional: true - - '@esbuild/freebsd-arm64@0.28.2': - optional: true - - '@esbuild/freebsd-x64@0.28.2': - optional: true - - '@esbuild/linux-arm64@0.28.2': - optional: true - - '@esbuild/linux-arm@0.28.2': - optional: true - - '@esbuild/linux-ia32@0.28.2': - optional: true - - '@esbuild/linux-loong64@0.28.2': - optional: true - - '@esbuild/linux-mips64el@0.28.2': - optional: true - - '@esbuild/linux-ppc64@0.28.2': - optional: true - - '@esbuild/linux-riscv64@0.28.2': - optional: true - - '@esbuild/linux-s390x@0.28.2': - optional: true - - '@esbuild/linux-x64@0.28.2': - optional: true - - '@esbuild/netbsd-arm64@0.28.2': - optional: true - - '@esbuild/netbsd-x64@0.28.2': - optional: true - - '@esbuild/openbsd-arm64@0.28.2': - optional: true - - '@esbuild/openbsd-x64@0.28.2': - optional: true - - '@esbuild/openharmony-arm64@0.28.2': - optional: true - - '@esbuild/sunos-x64@0.28.2': - optional: true - - '@esbuild/win32-arm64@0.28.2': - optional: true - - '@esbuild/win32-ia32@0.28.2': - optional: true - - '@esbuild/win32-x64@0.28.2': - optional: true - - '@google/genai@2.21.0': - dependencies: - google-auth-library: 10.9.1 - p-retry: 4.6.2 - protobufjs: 7.6.6 - ws: 8.22.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.5': {} - - '@protobufjs/eventemitter@1.1.1': {} - - '@protobufjs/fetch@1.1.1': - dependencies: - '@protobufjs/aspromise': 1.1.2 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.2': {} - - '@silvia-odwyer/photon-node@0.3.4': {} - - '@smithy/core@3.35.0': - dependencies: - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.5.2': - dependencies: - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.8.0': - dependencies: - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@smithy/node-http-handler@4.12.1': - dependencies: - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@smithy/signature-v4@5.7.4': - dependencies: - '@smithy/core': 3.35.0 - '@smithy/types': 4.19.0 - tslib: 2.8.1 - - '@smithy/types@4.19.0': - dependencies: - tslib: 2.8.1 - - '@stablelib/base64@1.0.1': {} - - '@types/node@26.6.3': - dependencies: - undici-types: 8.9.0 - - '@types/retry@0.12.0': {} - - agent-base@7.1.4: {} - - agent-base@9.0.0: {} - - balanced-match@4.0.4: {} - - base64-js@1.5.1: {} - - bignumber.js@9.3.1: {} - - bowser@2.14.1: {} - - brace-expansion@5.0.12: - dependencies: - balanced-match: 4.0.4 - - buffer-equal-constant-time@1.0.1: {} - - chalk@6.0.0: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - data-uri-to-buffer@4.0.1: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - diff@8.0.4: {} - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - esbuild@0.28.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.2 - '@esbuild/android-arm': 0.28.2 - '@esbuild/android-arm64': 0.28.2 - '@esbuild/android-x64': 0.28.2 - '@esbuild/darwin-arm64': 0.28.2 - '@esbuild/darwin-x64': 0.28.2 - '@esbuild/freebsd-arm64': 0.28.2 - '@esbuild/freebsd-x64': 0.28.2 - '@esbuild/linux-arm': 0.28.2 - '@esbuild/linux-arm64': 0.28.2 - '@esbuild/linux-ia32': 0.28.2 - '@esbuild/linux-loong64': 0.28.2 - '@esbuild/linux-mips64el': 0.28.2 - '@esbuild/linux-ppc64': 0.28.2 - '@esbuild/linux-riscv64': 0.28.2 - '@esbuild/linux-s390x': 0.28.2 - '@esbuild/linux-x64': 0.28.2 - '@esbuild/netbsd-arm64': 0.28.2 - '@esbuild/netbsd-x64': 0.28.2 - '@esbuild/openbsd-arm64': 0.28.2 - '@esbuild/openbsd-x64': 0.28.2 - '@esbuild/openharmony-arm64': 0.28.2 - '@esbuild/sunos-x64': 0.28.2 - '@esbuild/win32-arm64': 0.28.2 - '@esbuild/win32-ia32': 0.28.2 - '@esbuild/win32-x64': 0.28.2 - - extend@3.0.2: {} - - fast-sha256@1.3.0: {} - - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - - gaxios@7.3.1: - dependencies: - extend: 3.0.2 - https-proxy-agent: 7.0.6 - node-fetch: 3.3.2 - transitivePeerDependencies: - - supports-color - - gcp-metadata@8.1.2: - dependencies: - gaxios: 7.3.1 - google-logging-utils: 1.1.3 - json-bigint: 1.0.0 - transitivePeerDependencies: - - supports-color - - get-east-asian-width@1.6.0: {} - - google-auth-library@10.9.1: - dependencies: - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - gaxios: 7.3.1 - gcp-metadata: 8.1.2 - google-logging-utils: 1.1.3 - jws: 4.0.1 - transitivePeerDependencies: - - supports-color - - google-logging-utils@1.1.3: {} - - graceful-fs@4.2.11: {} - - grok-mermaid@0.2.3: {} - - highlight.js@10.7.3: {} - - hosted-git-info@9.0.3: - dependencies: - lru-cache: 11.5.3 - - http-proxy-agent@9.1.0: - dependencies: - agent-base: 9.0.0 - debug: 4.4.3 - proxy-agent-negotiate: 1.1.0 - transitivePeerDependencies: - - kerberos - - supports-color - - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - https-proxy-agent@9.1.0: - dependencies: - agent-base: 9.0.0 - debug: 4.4.3 - proxy-agent-negotiate: 1.1.0 - transitivePeerDependencies: - - kerberos - - supports-color - - ignore@7.0.8: {} - - isexe@2.0.0: {} - - jiti@2.7.0: {} - - json-bigint@1.0.0: - dependencies: - bignumber.js: 9.3.1 - - json-schema-to-ts@3.1.1: - dependencies: - '@babel/runtime': 7.29.7 - ts-algebra: 2.0.0 - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - long@5.3.2: {} - - lru-cache@11.5.3: {} - - marked@18.0.11: {} - - minimatch@10.2.6: - dependencies: - brace-expansion: 5.0.12 - - ms@2.1.3: {} - - node-domexception@1.0.0: {} - - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - - openai@6.40.0(ws@8.22.0): - optionalDependencies: - ws: 8.22.0 - - p-retry@4.6.2: - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - - partial-json@0.1.7: {} - - path-key@3.1.1: {} - - proper-lockfile@4.1.2: - dependencies: - graceful-fs: 4.2.11 - retry: 0.12.0 - signal-exit: 3.0.7 - - protobufjs@7.6.6: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.5 - '@protobufjs/eventemitter': 1.1.1 - '@protobufjs/fetch': 1.1.1 - '@protobufjs/float': 1.0.2 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.2 - '@types/node': 26.6.3 - long: 5.3.2 - - proxy-agent-negotiate@1.1.0: {} - - retry@0.12.0: {} - - retry@0.13.1: {} - - safe-buffer@5.2.1: {} - - semver@7.8.5: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - signal-exit@3.0.7: {} - - standardwebhooks@1.1.1: - dependencies: - '@stablelib/base64': 1.0.1 - fast-sha256: 1.3.0 - - ts-algebra@2.0.0: {} - - tslib@2.8.1: {} - - typebox@1.3.27: {} - - undici-types@8.9.0: {} - - undici@8.10.2: {} - - web-streams-polyfill@3.3.3: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - ws@8.22.0: {} - - yaml@2.9.0: {} diff --git a/web/src/components/canvas/art-critique/ai-art-critique-report.tsx b/web/src/components/canvas/art-critique/ai-art-critique-report.tsx index c333f7880..8dbf7203f 100644 --- a/web/src/components/canvas/art-critique/ai-art-critique-report.tsx +++ b/web/src/components/canvas/art-critique/ai-art-critique-report.tsx @@ -19,7 +19,6 @@ export const ART_CRITIQUE_STAGE_PROGRESS: Record> = { - Brush, Camera, Clapperboard, Clock, Contrast, FastForward, Globe2, Grid2x2, Grid3x3, Package, Palette, PersonStanding, Rewind, ScanFace, SlidersHorizontal, Sparkles, Sun, + Brush, + Camera, + Clapperboard, + Clock, + Contrast, + FastForward, + Globe2, + Grid2x2, + Grid3x3, + Package, + Palette, + PersonStanding, + Rewind, + ScanFace, + SlidersHorizontal, + Sparkles, + Sun, }; export function toolIconSvg(iconName: string): string { @@ -71,7 +87,9 @@ export function InlineReferencePreview({ reference, onClose }: { reference: Canv
event.stopPropagation()}> {reference.label} - +
, document.body, diff --git a/web/src/components/canvas/canvas-mention-menu.tsx b/web/src/components/canvas/canvas-mention-menu.tsx index a02979f0f..0c9a946c8 100644 --- a/web/src/components/canvas/canvas-mention-menu.tsx +++ b/web/src/components/canvas/canvas-mention-menu.tsx @@ -10,7 +10,19 @@ import { CanvasNodeType } from "@/types/canvas"; import { primeVideoPreviewFrame } from "./canvas-mention-chips"; import { pointForOffset } from "./canvas-mention-editable"; -export function MentionMenu({ anchor, connectedReferences, assetReferences, filteredReferences, query, cursorOffset, activeReferenceId, preferredWidth, onQueryChange, onClose, onSelect }: { +export function MentionMenu({ + anchor, + connectedReferences, + assetReferences, + filteredReferences, + query, + cursorOffset, + activeReferenceId, + preferredWidth, + onQueryChange, + onClose, + onSelect, +}: { anchor: HTMLElement; connectedReferences: CanvasResourceReference[]; assetReferences: CanvasResourceReference[]; @@ -60,11 +72,7 @@ export function MentionMenu({ anchor, connectedReferences, assetReferences, filt .filter((item) => item.count > 0); const connectedNodes = connectedReferences.filter((item) => item.kind !== "skill"); const skillReferences = connectedReferences.filter((item) => item.kind === "skill"); - const visibleReferences = query - ? filteredReferences - : category - ? assetReferences.filter((item) => item.category === category) - : []; + const visibleReferences = query ? filteredReferences : category ? assetReferences.filter((item) => item.category === category) : []; useLayoutEffect(() => { const closeOnOutsidePointer = (event: globalThis.PointerEvent) => { @@ -125,19 +133,28 @@ export function MentionMenu({ anchor, connectedReferences, assetReferences, filt <> {connectedNodes.length ? (
-

画布节点{connectedNodes.length}

+

+ 画布节点 + {connectedNodes.length} +

) : null} {skillReferences.length ? (
-

技能库{skillReferences.length}

+

+ 技能库 + {skillReferences.length} +

) : null} {categoryItems.length ? (
-

素材库{assetReferences.length}

+

+ 素材库 + {assetReferences.length} +

{categoryItems.map((item) => ( )); diff --git a/web/src/components/canvas/canvas-node-media-content.tsx b/web/src/components/canvas/canvas-node-media-content.tsx index b325a2050..c326bfc94 100644 --- a/web/src/components/canvas/canvas-node-media-content.tsx +++ b/web/src/components/canvas/canvas-node-media-content.tsx @@ -450,7 +450,18 @@ export function ImageContent({ } /** A stable visible img is updated only after a queued candidate has loaded and decoded. */ -export function RetainedCanvasImage({ identity, src, storageKey, fallbackSrc, originalSize, alt, fitToImage, className, loading, theme }: { +export function RetainedCanvasImage({ + identity, + src, + storageKey, + fallbackSrc, + originalSize, + alt, + fitToImage, + className, + loading, + theme, +}: { identity: string; src: string; storageKey?: string; diff --git a/web/src/components/canvas/director/canvas-director-workbench.tsx b/web/src/components/canvas/director/canvas-director-workbench.tsx index 5573be121..5f5767afb 100644 --- a/web/src/components/canvas/director/canvas-director-workbench.tsx +++ b/web/src/components/canvas/director/canvas-director-workbench.tsx @@ -12,7 +12,7 @@ import { type AnimationClip } from "three"; import { compileDirectorPrompt } from "@/lib/canvas/director/director-prompt-compiler"; import { Select } from "@/components/ui/base/select"; import { DirectorViewport } from "./director-viewport"; -import { CanvasDirectorOnboarding } from "./canvas-director-onboarding"; +import { CanvasDirectorOnboarding } from "@/components/canvas/director/canvas-director-onboarding"; import { DirectorViewportDock } from "./director-viewport-dock"; import { DirectorSequencer } from "./director-sequencer"; import { App, type MenuProps, Input, Button } from "antd"; diff --git a/web/src/components/canvas/director/director-inspectors.tsx b/web/src/components/canvas/director/director-inspectors.tsx index 2627c0262..b2311767c 100644 --- a/web/src/components/canvas/director/director-inspectors.tsx +++ b/web/src/components/canvas/director/director-inspectors.tsx @@ -25,25 +25,83 @@ import { Euler, Quaternion } from "three"; import { releaseDirectorFocusAfterPointer } from "@/lib/canvas/director/director-shortcuts"; export const poseOptions: Array<{ label: string; value: DirectorPose }> = [ - { label: "站立", value: "stand" }, { label: "T 型", value: "t_pose" }, { label: "行走", value: "walk" }, { label: "跑步", value: "run" }, - { label: "坐姿", value: "sit" }, { label: "蹲下", value: "squat" }, { label: "单膝跪", value: "kneel_single" }, { label: "双膝跪", value: "kneel_double" }, - { label: "叉腰", value: "hands_hips" }, { label: "倚靠", value: "lean" }, { label: "鞠躬", value: "bow" }, { label: "思考", value: "think" }, - { label: "格斗", value: "fight" }, { label: "踢球", value: "kick" }, { label: "投掷", value: "throw" }, { label: "推进", value: "push" }, - { label: "招手", value: "wave" }, { label: "伸手", value: "reach" }, { label: "抱臂", value: "arms_crossed" }, { label: "看手机", value: "phone" }, + { label: "站立", value: "stand" }, + { label: "T 型", value: "t_pose" }, + { label: "行走", value: "walk" }, + { label: "跑步", value: "run" }, + { label: "坐姿", value: "sit" }, + { label: "蹲下", value: "squat" }, + { label: "单膝跪", value: "kneel_single" }, + { label: "双膝跪", value: "kneel_double" }, + { label: "叉腰", value: "hands_hips" }, + { label: "倚靠", value: "lean" }, + { label: "鞠躬", value: "bow" }, + { label: "思考", value: "think" }, + { label: "格斗", value: "fight" }, + { label: "踢球", value: "kick" }, + { label: "投掷", value: "throw" }, + { label: "推进", value: "push" }, + { label: "招手", value: "wave" }, + { label: "伸手", value: "reach" }, + { label: "抱臂", value: "arms_crossed" }, + { label: "看手机", value: "phone" }, ]; -export const shotSizeOptions = [{ label: "大远景", value: "extreme_wide" }, { label: "远景", value: "wide" }, { label: "全身景", value: "full" }, { label: "中景", value: "medium" }, { label: "近景", value: "close_up" }, { label: "大特写", value: "extreme_close_up" }]; +export const shotSizeOptions = [ + { label: "大远景", value: "extreme_wide" }, + { label: "远景", value: "wide" }, + { label: "全身景", value: "full" }, + { label: "中景", value: "medium" }, + { label: "近景", value: "close_up" }, + { label: "大特写", value: "extreme_close_up" }, +]; -export const cameraMoveOptions = [{ label: "固定", value: "static" }, { label: "推进", value: "push_in" }, { label: "拉远", value: "pull_out" }, { label: "左摇", value: "pan_left" }, { label: "右摇", value: "pan_right" }, { label: "上摇", value: "tilt_up" }, { label: "下摇", value: "tilt_down" }, { label: "左环绕", value: "orbit_left" }, { label: "右环绕", value: "orbit_right" }, { label: "手持", value: "handheld" }]; +export const cameraMoveOptions = [ + { label: "固定", value: "static" }, + { label: "推进", value: "push_in" }, + { label: "拉远", value: "pull_out" }, + { label: "左摇", value: "pan_left" }, + { label: "右摇", value: "pan_right" }, + { label: "上摇", value: "tilt_up" }, + { label: "下摇", value: "tilt_down" }, + { label: "左环绕", value: "orbit_left" }, + { label: "右环绕", value: "orbit_right" }, + { label: "手持", value: "handheld" }, +]; - -export function ObjectInspector({ object, rendered, playhead, selectedBone, capabilities, onSelectBone, onUpdate, onTransformEdit, onBoneRotationStage, onBoneRotationCommit, onAddKeyframe, onDelete }: { object: DirectorObject; rendered: DirectorTransform; playhead: number; selectedBone: string | null; capabilities: DirectorModeCapabilities; onSelectBone: (bone: string | null) => void; onUpdate: (patch: Partial) => void; onTransformEdit: (transform: DirectorTransform) => void; onBoneRotationStage: (rotation: DirectorQuat) => void; onBoneRotationCommit: () => void; onAddKeyframe: () => void; onDelete: () => void }) { +export function ObjectInspector({ + object, + rendered, + playhead, + selectedBone, + capabilities, + onSelectBone, + onUpdate, + onTransformEdit, + onBoneRotationStage, + onBoneRotationCommit, + onAddKeyframe, + onDelete, +}: { + object: DirectorObject; + rendered: DirectorTransform; + playhead: number; + selectedBone: string | null; + capabilities: DirectorModeCapabilities; + onSelectBone: (bone: string | null) => void; + onUpdate: (patch: Partial) => void; + onTransformEdit: (transform: DirectorTransform) => void; + onBoneRotationStage: (rotation: DirectorQuat) => void; + onBoneRotationCommit: () => void; + onAddKeyframe: () => void; + onDelete: () => void; +}) { const motionClips = object.motionClips || []; const activeMotionClip = motionClips.find((clip) => clip.id === object.activeMotionClipId); const mappedBones = Object.keys(object.rig?.boneMap || {}) as DirectorHumanoidBone[]; const selectedBoneId = selectedBone as DirectorHumanoidBone | null; - const selectedBoneRotation = selectedBoneId ? object.boneOverrides?.[selectedBoneId] || [0, 0, 0, 1] as DirectorQuat : null; - const updateActiveMotion = (patch: Partial[number]>) => activeMotionClip && onUpdate({ motionClips: motionClips.map((clip) => clip.id === activeMotionClip.id ? { ...clip, ...patch } : clip) }); + const selectedBoneRotation = selectedBoneId ? object.boneOverrides?.[selectedBoneId] || ([0, 0, 0, 1] as DirectorQuat) : null; + const updateActiveMotion = (patch: Partial[number]>) => activeMotionClip && onUpdate({ motionClips: motionClips.map((clip) => (clip.id === activeMotionClip.id ? { ...clip, ...patch } : clip)) }); const applyPose = (pose: DirectorPose) => onUpdate({ pose, activeMotionClipId: undefined, boneOverrides: {} }); const resetSelectedBone = () => { if (!selectedBoneId) return; @@ -51,61 +109,254 @@ export function ObjectInspector({ object, rendered, playhead, selectedBone, capa delete boneOverrides[selectedBoneId]; onUpdate({ boneOverrides }); }; - return onUpdate({ name })} onDelete={onDelete}> - - {object.kind === "actor" || object.primitive === "character" - ?
{DIRECTOR_ACTOR_COLORS.map((color) =>
- : onUpdate({ color })} />} - {/* + return ( + onUpdate({ name })} onDelete={onDelete}> + + {object.kind === "actor" || object.primitive === "character" ? ( + +
+ {DIRECTOR_ACTOR_COLORS.map((color) => ( +
+
+ ) : ( + + onUpdate({ color })} /> + + )} + {/* 骨骼与姿势入口:只在姿态/动画模式出现,且只对演员出现。 规格要求「仅在演员选择时展示现有骨骼/姿势入口」—— 带动画的普通模型不是演员,不应拿到姿势预设与骨骼控制。 */} - {capabilities.bones && (object.kind === "actor" || object.primitive === "character") ? <> -
-
姿势预设{directorPoseLabel(object.pose || "stand")}
-
{poseOptions.map((option) => )}
- -
-
角色绑定{object.rig?.status === "ready" ? `${mappedBones.length} 根骨骼` : "等待模型"}
- {mappedBones.length ? ({ label: clip.name, value: clip.id }))]} onChange={(activeMotionClipId) => onUpdate({ activeMotionClipId: activeMotionClipId || undefined })} />{activeMotionClip ?
updateActiveMotion({ playbackRate: playbackRate || 1 })} /> updateActiveMotion({ loop })} />
: null} : null} - onUpdate({ visible })} /> - onUpdate({ castShadow })} /> - {/* 记录关键帧属于动画模式;摆场与姿态模式不默认制造关键帧。 */} - {capabilities.keyframes ? <> - -
Transform {object.keyframes.length} 个 · 骨骼 {object.boneTracks?.reduce((sum, track) => sum + track.keyframes.length, 0) || 0} 个
- : null} -
; + {capabilities.bones && (object.kind === "actor" || object.primitive === "character") ? ( + <> +
+
+ 姿势预设 + {directorPoseLabel(object.pose || "stand")} +
+
+ {poseOptions.map((option) => ( + + ))} +
+ +
+
+ 角色绑定 + {object.rig?.status === "ready" ? `${mappedBones.length} 根骨骼` : "等待模型"} +
+ {mappedBones.length ? ( + + ({ label: clip.name, value: clip.id }))]} + onChange={(activeMotionClipId) => onUpdate({ activeMotionClipId: activeMotionClipId || undefined })} + /> + + {activeMotionClip ? ( +
+ + updateActiveMotion({ playbackRate: playbackRate || 1 })} /> + + + updateActiveMotion({ loop })} /> + +
+ ) : null} + + ) : null} + + onUpdate({ visible })} /> + + + onUpdate({ castShadow })} /> + + {/* 记录关键帧属于动画模式;摆场与姿态模式不默认制造关键帧。 */} + {capabilities.keyframes ? ( + <> + +
+ Transform {object.keyframes.length} 个 · 骨骼 {object.boneTracks?.reduce((sum, track) => sum + track.keyframes.length, 0) || 0} 个 +
+ + ) : null} +
+ ); } export function LightInspector({ light, onUpdate, onDelete }: { light: DirectorLight; onUpdate: (patch: Partial) => void; onDelete: () => void }) { - return onUpdate({ name })} onDelete={onDelete}> onUpdate({ type })} + /> + + onUpdate({ transform: { ...light.transform, position } })} /> + + onUpdate({ color })} /> + + + onUpdate({ intensity: value || 0 })} /> + + + onUpdate({ castShadow })} /> + + + ); } -export function ShotInspector({ shot, camera, cameras, capabilities, onUpdateShot, onUpdateCamera, onAddCameraKeyframe, onApplyCameraMove, onAlignCameraToView, onExportClay, recording }: { shot: DirectorShot; camera: DirectorCamera | null; cameras: DirectorScene["cameras"]; capabilities: DirectorModeCapabilities; onUpdateShot: (patch: Partial) => void; onUpdateCamera: (patch: Partial) => void; onAddCameraKeyframe: () => void; onApplyCameraMove: () => void; onAlignCameraToView: () => void; onExportClay: () => void; recording: boolean }) { - return onUpdateShot({ name })}> - onUpdateShot({ shotSize })} /> onUpdateShot({ cameraMove })} /> - onUpdateShot({ duration: value || 5 })} /> - onUpdateShot({ prompt: event.target.value })} /> - {camera ? <> onUpdateCamera({ transform: { ...camera.transform, position } })} /> onUpdateCamera({ target })} /> onUpdateCamera({ focalLength: focalLength || 35, fov: directorFocalLengthToFov(focalLength || 35) })} />
onUpdateCamera({ aperture: aperture || 2.8 })} /> onUpdateCamera({ focusDistance: focusDistance || 5 })} />
{capabilities.keyframes ? : null} : null} -
; +export function ShotInspector({ + shot, + camera, + cameras, + capabilities, + onUpdateShot, + onUpdateCamera, + onAddCameraKeyframe, + onApplyCameraMove, + onAlignCameraToView, + onExportClay, + recording, +}: { + shot: DirectorShot; + camera: DirectorCamera | null; + cameras: DirectorScene["cameras"]; + capabilities: DirectorModeCapabilities; + onUpdateShot: (patch: Partial) => void; + onUpdateCamera: (patch: Partial) => void; + onAddCameraKeyframe: () => void; + onApplyCameraMove: () => void; + onAlignCameraToView: () => void; + onExportClay: () => void; + recording: boolean; +}) { + return ( + onUpdateShot({ name })}> + + onUpdateShot({ shotSize })} /> + + + onUpdateShot({ cameraMove })} /> + + + onUpdateShot({ duration: value || 5 })} /> + + + onUpdateShot({ prompt: event.target.value })} /> + + {camera ? ( + <> + onUpdateCamera({ transform: { ...camera.transform, position } })} /> + onUpdateCamera({ target })} /> + + onUpdateCamera({ focalLength: focalLength || 35, fov: directorFocalLengthToFov(focalLength || 35) })} /> + +
+ + onUpdateCamera({ aperture: aperture || 2.8 })} /> + + + onUpdateCamera({ focusDistance: focusDistance || 5 })} /> + +
+ + + {capabilities.keyframes ? ( + + ) : null} + + + ) : null} +
+ ); } export function Inspector({ title, children, onTitleChange, onDelete }: { title: string; children: ReactNode; onTitleChange: (value: string) => void; onDelete?: () => void }) { - return
onTitleChange(event.target.value)} />{onDelete ? : null}
{children}
; + return ( +
+
+ onTitleChange(event.target.value)} /> + {onDelete ? ( + + + + ) : null} +
+ {children} +
+ ); } export function TransformFields({ transform, onChange }: { transform: DirectorTransform; onChange: (transform: DirectorTransform) => void }) { - return <> onChange({ ...transform, position })} /> onChange({ ...transform, rotation })} /> onChange({ ...transform, scale })} />; + return ( + <> + onChange({ ...transform, position })} /> + onChange({ ...transform, rotation })} /> + onChange({ ...transform, scale })} /> + + ); } export function BoneRotationFields({ rotation, onChange, onChangeComplete }: { rotation: DirectorQuat; onChange: (rotation: DirectorQuat) => void; onChangeComplete: () => void }) { @@ -123,20 +374,26 @@ export function BoneRotationFields({ rotation, onChange, onChangeComplete }: { r setDegrees(initialDegrees); }, [initialDegrees, rotation]); const updateAxis = (index: number, value: number) => { - const next = degrees.map((entry, entryIndex) => entryIndex === index ? value : entry) as DirectorVec3; + const next = degrees.map((entry, entryIndex) => (entryIndex === index ? value : entry)) as DirectorVec3; const radians = next.map((entry) => (entry * Math.PI) / 180) as DirectorVec3; const nextRotation = new Quaternion().setFromEuler(new Euler(radians[0], radians[1], radians[2], "XYZ")).toArray() as DirectorQuat; setDegrees(next); lastEmittedRotation.current = nextRotation; onChange(nextRotation); }; - return
- {degrees.map((value, index) =>
- {["X", "Y", "Z"][index]} - updateAxis(index, Array.isArray(next) ? next[0] ?? 0 : next)} onChangeComplete={onChangeComplete} /> - {value.toFixed(1)}° -
)} -
; + return ( + +
+ {degrees.map((value, index) => ( +
+ {["X", "Y", "Z"][index]} + updateAxis(index, Array.isArray(next) ? (next[0] ?? 0) : next)} onChangeComplete={onChangeComplete} /> + {value.toFixed(1)}° +
+ ))} +
+
+ ); } export function sameDirectorQuaternion(left: DirectorQuat, right: DirectorQuat) { @@ -146,12 +403,34 @@ export function sameDirectorQuaternion(left: DirectorQuat, right: DirectorQuat) } export function Vec3Field({ label, value, step = 0.1, onChange }: { label: string; value: DirectorVec3; step?: number; onChange: (value: DirectorVec3) => void }) { - return
{value.map((item, index) => onChange(value.map((entry, itemIndex) => itemIndex === index ? next || 0 : entry) as DirectorVec3)} />)}
; + return ( + +
+ {value.map((item, index) => ( + onChange(value.map((entry, itemIndex) => (itemIndex === index ? next || 0 : entry)) as DirectorVec3)} /> + ))} +
+
+ ); } -export function Field({ label, children }: { label: string; children: ReactNode }) { return ; } +export function Field({ label, children }: { label: string; children: ReactNode }) { + return ( + + ); +} -export function PanelTitle({ title, action }: { title: string; action?: ReactNode }) { return
{title}{action}
; } +export function PanelTitle({ title, action }: { title: string; action?: ReactNode }) { + return ( +
+ {title} + {action} +
+ ); +} /** * 场景列表行。选择按钮点完必须释放焦点: @@ -159,19 +438,78 @@ export function PanelTitle({ title, action }: { title: string; action?: ReactNod * 焦点留在按钮上会让守卫把 Delete 吃掉。 */ export function SceneRow({ active, icon, label, onClick, onDelete }: { active?: boolean; icon: ReactElement; label: string; onClick: () => void; onDelete?: () => void }) { - return
- - {onDelete ? : null} -
; + return ( +
+ + {onDelete ? ( + + ) : null} +
+ ); } export function AddMenuButton({ label, items }: { label: string; items: MenuProps["items"] }) { - return ; + return ( + + + + ); } -export function QuickAdd({ label, icon, onClick }: { label: string; icon: ReactElement; onClick: () => void }) { return ; } +export function QuickAdd({ label, icon, onClick }: { label: string; icon: ReactElement; onClick: () => void }) { + return ( + + ); +} -export function IconButton({ label, disabled, children, onClick }: { label: string; disabled?: boolean; children: ReactNode; onClick: () => void }) { return ; } +export function IconButton({ label, disabled, children, onClick }: { label: string; disabled?: boolean; children: ReactNode; onClick: () => void }) { + return ( + + ); +} diff --git a/web/src/components/canvas/director/director-viewport-capture.ts b/web/src/components/canvas/director/director-viewport-capture.ts index fa10115f8..85b60aeca 100644 --- a/web/src/components/canvas/director/director-viewport-capture.ts +++ b/web/src/components/canvas/director/director-viewport-capture.ts @@ -46,12 +46,19 @@ export async function recordCanvas(context: CaptureContext | null, duration: num }; window.addEventListener("error", onRenderError); const result = new Promise((resolve, reject) => { - recorder.ondataavailable = (event) => { if (event.data.size) chunks.push(event.data); }; + recorder.ondataavailable = (event) => { + if (event.data.size) chunks.push(event.data); + }; recorder.onerror = () => reject(new Error("白膜视频录制失败")); recorder.onstop = () => resolve(new Blob(chunks, { type: recorder.mimeType || "video/webm" })); }); recorder.start(250); - const stopTimer = window.setTimeout(() => { if (recorder.state !== "inactive") recorder.stop(); }, Math.max(250, duration * 1000 + 120)); + const stopTimer = window.setTimeout( + () => { + if (recorder.state !== "inactive") recorder.stop(); + }, + Math.max(250, duration * 1000 + 120), + ); try { const blob = await result; if (renderError) throw renderError; @@ -84,7 +91,10 @@ export async function probeRecordedDuration(blob: Blob) { }); if (video.duration !== Infinity) return video.duration; await new Promise((resolve) => { - const finish = () => { video.removeEventListener("seeked", finish); resolve(); }; + const finish = () => { + video.removeEventListener("seeked", finish); + resolve(); + }; video.addEventListener("seeked", finish); video.currentTime = 1e6; window.setTimeout(finish, 1000); diff --git a/web/src/components/canvas/director/director-viewport-rig.ts b/web/src/components/canvas/director/director-viewport-rig.ts index 7b548be79..26a0a1415 100644 --- a/web/src/components/canvas/director/director-viewport-rig.ts +++ b/web/src/components/canvas/director/director-viewport-rig.ts @@ -64,15 +64,19 @@ export function updateActorReferenceColor(root: Object3D, color: string) { } export function readRigRestRotations(root: Object3D, rig: DirectorRig) { - return Object.fromEntries(Object.entries(rig.boneMap).flatMap(([bone, name]) => { - const target = name ? root.getObjectByName(name) : null; - return target ? [[bone, target.quaternion.toArray() as DirectorQuat]] : []; - })) as Partial>; + return Object.fromEntries( + Object.entries(rig.boneMap).flatMap(([bone, name]) => { + const target = name ? root.getObjectByName(name) : null; + return target ? [[bone, target.quaternion.toArray() as DirectorQuat]] : []; + }), + ) as Partial>; } export function inferDirectorRig(root: Object3D, animationNames: string[]): DirectorRig { const names = new Map(); - root.traverse((child) => { if (child instanceof Bone) names.set(normalizeBoneName(child.name), child.name); }); + root.traverse((child) => { + if (child instanceof Bone) names.set(normalizeBoneName(child.name), child.name); + }); const fingerPatterns = (side: "left" | "right", finger: "thumb" | "index" | "middle" | "ring" | "pinky", segment: 1 | 2 | 3) => [ new RegExp(`^mixamorig${side}hand${finger}${segment}$`), new RegExp(`^${side}hand${finger}${segment}$`), @@ -80,22 +84,62 @@ export function inferDirectorRig(root: Object3D, animationNames: string[]): Dire new RegExp(`^${finger}0?${segment}${side === "left" ? "l" : "r"}$`), ]; const patterns: Record = { - root: [/^root$/, /armature/], hips: [/hips|pelvis/, /mixamorig.*hip/], spine: [/spine1?$|lowerback/], chest: [/spine2|chest|upperback/], neck: [/neck/], head: [/head/], - leftShoulder: [/leftshoulder|shoulder_l|mixamorigleftshoulder/], leftUpperArm: [/leftupperarm|leftarm|upperarm_l|mixamorigleftarm/], leftLowerArm: [/leftforearm|leftlowerarm|forearm_l|mixamorigleftforearm/], leftHand: [/^lefthand$/, /^handl$/, /^mixamoriglefthand$/], - leftThumb1: fingerPatterns("left", "thumb", 1), leftThumb2: fingerPatterns("left", "thumb", 2), leftThumb3: fingerPatterns("left", "thumb", 3), - leftIndex1: fingerPatterns("left", "index", 1), leftIndex2: fingerPatterns("left", "index", 2), leftIndex3: fingerPatterns("left", "index", 3), - leftMiddle1: fingerPatterns("left", "middle", 1), leftMiddle2: fingerPatterns("left", "middle", 2), leftMiddle3: fingerPatterns("left", "middle", 3), - leftRing1: fingerPatterns("left", "ring", 1), leftRing2: fingerPatterns("left", "ring", 2), leftRing3: fingerPatterns("left", "ring", 3), - leftPinky1: fingerPatterns("left", "pinky", 1), leftPinky2: fingerPatterns("left", "pinky", 2), leftPinky3: fingerPatterns("left", "pinky", 3), - rightShoulder: [/rightshoulder|shoulder_r|mixamorigrightshoulder/], rightUpperArm: [/rightupperarm|rightarm|upperarm_r|mixamorigrightarm/], rightLowerArm: [/rightforearm|rightlowerarm|forearm_r|mixamorigrightforearm/], rightHand: [/^righthand$/, /^handr$/, /^mixamorigrighthand$/], - rightThumb1: fingerPatterns("right", "thumb", 1), rightThumb2: fingerPatterns("right", "thumb", 2), rightThumb3: fingerPatterns("right", "thumb", 3), - rightIndex1: fingerPatterns("right", "index", 1), rightIndex2: fingerPatterns("right", "index", 2), rightIndex3: fingerPatterns("right", "index", 3), - rightMiddle1: fingerPatterns("right", "middle", 1), rightMiddle2: fingerPatterns("right", "middle", 2), rightMiddle3: fingerPatterns("right", "middle", 3), - rightRing1: fingerPatterns("right", "ring", 1), rightRing2: fingerPatterns("right", "ring", 2), rightRing3: fingerPatterns("right", "ring", 3), - rightPinky1: fingerPatterns("right", "pinky", 1), rightPinky2: fingerPatterns("right", "pinky", 2), rightPinky3: fingerPatterns("right", "pinky", 3), - leftUpperLeg: [/leftupleg|leftthigh|thigh_l|mixamorigleftupleg/], leftLowerLeg: [/leftleg|leftcalf|calf_l|mixamorigleftleg/], leftFoot: [/leftfoot|foot_l|mixamorigleftfoot/], rightUpperLeg: [/rightupleg|rightthigh|thigh_r|mixamorigrightupleg/], rightLowerLeg: [/rightleg|rightcalf|calf_r|mixamorigrightleg/], rightFoot: [/rightfoot|foot_r|mixamorigrightfoot/], + root: [/^root$/, /armature/], + hips: [/hips|pelvis/, /mixamorig.*hip/], + spine: [/spine1?$|lowerback/], + chest: [/spine2|chest|upperback/], + neck: [/neck/], + head: [/head/], + leftShoulder: [/leftshoulder|shoulder_l|mixamorigleftshoulder/], + leftUpperArm: [/leftupperarm|leftarm|upperarm_l|mixamorigleftarm/], + leftLowerArm: [/leftforearm|leftlowerarm|forearm_l|mixamorigleftforearm/], + leftHand: [/^lefthand$/, /^handl$/, /^mixamoriglefthand$/], + leftThumb1: fingerPatterns("left", "thumb", 1), + leftThumb2: fingerPatterns("left", "thumb", 2), + leftThumb3: fingerPatterns("left", "thumb", 3), + leftIndex1: fingerPatterns("left", "index", 1), + leftIndex2: fingerPatterns("left", "index", 2), + leftIndex3: fingerPatterns("left", "index", 3), + leftMiddle1: fingerPatterns("left", "middle", 1), + leftMiddle2: fingerPatterns("left", "middle", 2), + leftMiddle3: fingerPatterns("left", "middle", 3), + leftRing1: fingerPatterns("left", "ring", 1), + leftRing2: fingerPatterns("left", "ring", 2), + leftRing3: fingerPatterns("left", "ring", 3), + leftPinky1: fingerPatterns("left", "pinky", 1), + leftPinky2: fingerPatterns("left", "pinky", 2), + leftPinky3: fingerPatterns("left", "pinky", 3), + rightShoulder: [/rightshoulder|shoulder_r|mixamorigrightshoulder/], + rightUpperArm: [/rightupperarm|rightarm|upperarm_r|mixamorigrightarm/], + rightLowerArm: [/rightforearm|rightlowerarm|forearm_r|mixamorigrightforearm/], + rightHand: [/^righthand$/, /^handr$/, /^mixamorigrighthand$/], + rightThumb1: fingerPatterns("right", "thumb", 1), + rightThumb2: fingerPatterns("right", "thumb", 2), + rightThumb3: fingerPatterns("right", "thumb", 3), + rightIndex1: fingerPatterns("right", "index", 1), + rightIndex2: fingerPatterns("right", "index", 2), + rightIndex3: fingerPatterns("right", "index", 3), + rightMiddle1: fingerPatterns("right", "middle", 1), + rightMiddle2: fingerPatterns("right", "middle", 2), + rightMiddle3: fingerPatterns("right", "middle", 3), + rightRing1: fingerPatterns("right", "ring", 1), + rightRing2: fingerPatterns("right", "ring", 2), + rightRing3: fingerPatterns("right", "ring", 3), + rightPinky1: fingerPatterns("right", "pinky", 1), + rightPinky2: fingerPatterns("right", "pinky", 2), + rightPinky3: fingerPatterns("right", "pinky", 3), + leftUpperLeg: [/leftupleg|leftthigh|thigh_l|mixamorigleftupleg/], + leftLowerLeg: [/leftleg|leftcalf|calf_l|mixamorigleftleg/], + leftFoot: [/leftfoot|foot_l|mixamorigleftfoot/], + rightUpperLeg: [/rightupleg|rightthigh|thigh_r|mixamorigrightupleg/], + rightLowerLeg: [/rightleg|rightcalf|calf_r|mixamorigrightleg/], + rightFoot: [/rightfoot|foot_r|mixamorigrightfoot/], }; - const boneMap = Object.fromEntries(Object.entries(patterns).map(([bone, candidates]) => [bone, candidates.map((pattern) => [...names.entries()].find(([normalized]) => pattern.test(normalized))?.[1]).find(Boolean)]).filter(([, name]) => Boolean(name))) as DirectorRig["boneMap"]; + const boneMap = Object.fromEntries( + Object.entries(patterns) + .map(([bone, candidates]) => [bone, candidates.map((pattern) => [...names.entries()].find(([normalized]) => pattern.test(normalized))?.[1]).find(Boolean)]) + .filter(([, name]) => Boolean(name)), + ) as DirectorRig["boneMap"]; return { status: Object.keys(boneMap).length >= 8 ? "ready" : "unmapped", boneMap, animationNames }; } @@ -112,7 +156,7 @@ export function applyDirectorBoneTracks(model: Object3D, object: DirectorObject, const humanoidBone = bone as DirectorHumanoidBone; // hasActiveMotion 时 mixer 已写入 target.quaternion,直接作为动作层输入。 const rotation = resolveDirectorBoneRotation({ - motion: hasActiveMotion ? target.quaternion.toArray() as DirectorQuat : null, + motion: hasActiveMotion ? (target.quaternion.toArray() as DirectorQuat) : null, rest: hasActiveMotion ? null : restRotations[humanoidBone] || null, poseDelta: hasActiveMotion ? null : poseDeltas[humanoidBone] || null, override: object.boneOverrides?.[humanoidBone] || null, diff --git a/web/src/pages/admin/components/admin-announcement-results.ts b/web/src/pages/admin/components/admin-announcement-results.ts index 70415b7f2..c634d428f 100644 --- a/web/src/pages/admin/components/admin-announcement-results.ts +++ b/web/src/pages/admin/components/admin-announcement-results.ts @@ -47,7 +47,6 @@ export function formatDateTime(value?: string | null) { return new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false }).format(date).replaceAll("/", "-"); } - export function isMutationResultUncertain(error: unknown) { if (!(error instanceof ApiError)) return true; return error.status === undefined || error.retryable || error.status >= 500; diff --git a/web/src/pages/admin/settings/appearance-settings-parts.tsx b/web/src/pages/admin/settings/appearance-settings-parts.tsx index 516eb3dd9..8c0d15fdd 100644 --- a/web/src/pages/admin/settings/appearance-settings-parts.tsx +++ b/web/src/pages/admin/settings/appearance-settings-parts.tsx @@ -19,7 +19,6 @@ export const FILE_RULES: Record; - export function formValues(setting: AdminOSSSetting): OSSFormValues { return { mode: setting.enabled ? setting.provider : "local", diff --git a/web/src/pages/assets/asset-library-format.ts b/web/src/pages/assets/asset-library-format.ts index a86e1cfa6..993a8a9db 100644 --- a/web/src/pages/assets/asset-library-format.ts +++ b/web/src/pages/assets/asset-library-format.ts @@ -19,7 +19,6 @@ export const assetKindIcons: Record = { model: Box, }; - export function assetSummary(asset: LibraryAsset) { if (asset.kind === "text") return asset.data.content; if (asset.kind === "audio") return `${formatAssetDuration(asset.data.durationMs)} · ${formatBytes(asset.data.bytes)} · ${asset.data.mimeType}`; diff --git a/web/src/pages/create/creation-workspace-empty.tsx b/web/src/pages/create/creation-workspace-empty.tsx index f6364860e..72953d9e9 100644 --- a/web/src/pages/create/creation-workspace-empty.tsx +++ b/web/src/pages/create/creation-workspace-empty.tsx @@ -18,13 +18,19 @@ export const creationEmptyBannerFrames = [ export function CreationEmptyBanner() { const brandName = useAppearanceStore((state) => state.appearance.brandName); - return ; + return ( + + ); } export const creationEmptySuggestions: Array<{ mode: CreationMode; icon: typeof Clapperboard; title: string; hint: string; prompt: string; openLibrary?: boolean }> = [ @@ -37,61 +43,108 @@ export const creationEmptySuggestions: Array<{ mode: CreationMode; icon: typeof export function CreationEmptySuggest({ onStartPrompt, onOpenLibrary }: { onStartPrompt: (mode: CreationMode, prompt: string) => void; onOpenLibrary: () => void }) { const reducedMotion = useReducedMotion(); const [hovered, setHovered] = useState(null); - return -
- {creationEmptySuggestions.map((item) => { - const Icon = item.icon; - const start = () => { - if (item.openLibrary) onOpenLibrary(); - else onStartPrompt(item.mode, item.prompt); - }; - return setHovered(item.title)} - onHoverEnd={() => setHovered(null)} - whileHover={reducedMotion ? undefined : { y: -2 }} - transition={aceternityMotion.spring.surface} - > - {hovered === item.title ? ( - - ) : null} - - - {item.title} - {item.hint} - - ; - })} -
-
; + return ( + +
+ {creationEmptySuggestions.map((item) => { + const Icon = item.icon; + const start = () => { + if (item.openLibrary) onOpenLibrary(); + else onStartPrompt(item.mode, item.prompt); + }; + return ( + setHovered(item.title)} + onHoverEnd={() => setHovered(null)} + whileHover={reducedMotion ? undefined : { y: -2 }} + transition={aceternityMotion.spring.surface} + > + {hovered === item.title ? : null} + + + + + {item.title} + {item.hint} + + + ); + })} +
+
+ ); } export function CreationFeaturedWorks({ onStartPrompt }: { onStartPrompt: (mode: CreationMode, prompt: string) => void }) { const [filter, setFilter] = useState<"all" | CreationMode>("all"); const [limit, setLimit] = useState(12); const filtered = creationFeaturedWorks.filter((item) => filter === "all" || item.mode === filter); - return
-
-
-

{creationFeaturedWorks.length} 个创意起点 · 点击填入提示词,不自动生成

-
-
- {(["all", "video", "image", "text"] as const).map((value) => )} -
-
- {filtered.slice(0, limit).map((item, index) => )} -
-
{limit < filtered.length ? : 已展示全部 {filtered.length} 个创意}
模板与封面来源

{inspirationSource.notice}

awesome-chatgpt-prompts · CC0
-
; + return ( +
+
+
+ +
+

{creationFeaturedWorks.length} 个创意起点 · 点击填入提示词,不自动生成

+
+
+ {(["all", "video", "image", "text"] as const).map((value) => ( + + ))} +
+
+ {filtered.slice(0, limit).map((item, index) => ( + + ))} +
+
+ {limit < filtered.length ? ( + + ) : ( + 已展示全部 {filtered.length} 个创意 + )} +
+ 模板与封面来源 +

{inspirationSource.notice}

+ + awesome-chatgpt-prompts · CC0 + +
+
+
+ ); } diff --git a/web/src/pages/create/creation-workspace-history.tsx b/web/src/pages/create/creation-workspace-history.tsx index 5ddf4bf06..f5f3748ac 100644 --- a/web/src/pages/create/creation-workspace-history.tsx +++ b/web/src/pages/create/creation-workspace-history.tsx @@ -27,7 +27,25 @@ export function creationConversationBucket(updatedAt: string): "today" | "yester export const creationBucketLabels: Record<"today" | "yesterday" | "week" | "earlier", string> = { today: "今天", yesterday: "昨天", week: "近 7 天", earlier: "更早" }; -export function CreationHistoryDrawer({ open, conversations, activeId, onNew, onClose, onSelect, onDelete, onRename }: { open: boolean; conversations: CreationConversation[]; activeId: string; onNew: () => void; onClose: () => void; onSelect: (conversation: CreationConversation) => void; onDelete: (conversation: CreationConversation) => void; onRename: (conversation: CreationConversation, title: string) => void }) { +export function CreationHistoryDrawer({ + open, + conversations, + activeId, + onNew, + onClose, + onSelect, + onDelete, + onRename, +}: { + open: boolean; + conversations: CreationConversation[]; + activeId: string; + onNew: () => void; + onClose: () => void; + onSelect: (conversation: CreationConversation) => void; + onDelete: (conversation: CreationConversation) => void; + onRename: (conversation: CreationConversation, title: string) => void; +}) { const [keyword, setKeyword] = useState(""); const [renamingId, setRenamingId] = useState(null); const [menuOpenId, setMenuOpenId] = useState(null); @@ -78,54 +96,160 @@ export function CreationHistoryDrawer({ open, conversations, activeId, onNew, on ...conversation.messages.flatMap((message) => [message.content, displayCreationPrompt(message.content, message.references || [])]), latest?.mode ? modeLabels[latest.mode] : "创作", formatConversationTime(conversation.updatedAt), - ].filter(Boolean).join(" ").toLowerCase(); + ] + .filter(Boolean) + .join(" ") + .toLowerCase(); return searchable.includes(query); }); }, [conversations, keyword]); + return ( + } + className="creation-history-drawer" + rootClassName="creation-history-drawer-root" + title={ +
+ 历史对话 + {conversations.length} 个对话 +
+ } + > +
+ - return } className="creation-history-drawer" rootClassName="creation-history-drawer-root" title={
历史对话{conversations.length} 个对话
}> -
- - - - {visibleConversations.length ?
    - {visibleConversations.flatMap((conversation, index) => { - const showGroupHead = !keyword.trim() && (index === 0 || creationConversationBucket(conversation.updatedAt) !== creationConversationBucket(visibleConversations[index - 1].updatedAt)); - const latest = conversationPreviewMessage(conversation); - const active = conversation.id === activeId; - const HistoryTypeIcon = latest?.mode === "video" ? Clapperboard : latest?.mode === "image" ? ImageIcon : latest?.mode === "text" ? MessageSquareText : Sparkles; - return [ - showGroupHead ?
  • {creationBucketLabels[creationConversationBucket(conversation.updatedAt)]}

  • : null, -
  • - {renamingId === conversation.id ? ( -
    - event.currentTarget.select()} onKeyDown={(event) => { if (event.key === "Enter") { event.preventDefault(); event.currentTarget.blur(); } else if (event.key === "Escape") { cancelRename(); } }} onBlur={() => commitRename(conversation)} /> -
    - ) : ( -
    - - - setMenuOpenId(open ? conversation.id : null)} overlayClassName="creation-history-menu-overlay" menu={{ items: [{ key: "rename", label: "重命名", icon: }, { key: "export", label: "导出对话", icon: }, { key: "delete", label: "删除对话", danger: true, icon: }], onClick: ({ key }) => { setMenuOpenId(null); if (key === "rename") { beginRename(conversation); } else if (key === "export") { downloadCreationConversation(conversation, assistantName, exportUser?.displayName || "你"); drawerToast.success("对话已导出为 Markdown"); } else { onDelete(conversation); } } }}> - - -
    - )} -
  • , - ]; - })} -
:
{keyword.trim() ? "没有找到匹配的对话" : "暂无历史对话"}
} -
-
; + + {visibleConversations.length ? ( +
    + {visibleConversations.flatMap((conversation, index) => { + const showGroupHead = !keyword.trim() && (index === 0 || creationConversationBucket(conversation.updatedAt) !== creationConversationBucket(visibleConversations[index - 1].updatedAt)); + const latest = conversationPreviewMessage(conversation); + const active = conversation.id === activeId; + const HistoryTypeIcon = latest?.mode === "video" ? Clapperboard : latest?.mode === "image" ? ImageIcon : latest?.mode === "text" ? MessageSquareText : Sparkles; + return [ + showGroupHead ? ( +
  • +

    {creationBucketLabels[creationConversationBucket(conversation.updatedAt)]}

    +
  • + ) : null, +
  • + {renamingId === conversation.id ? ( +
    + event.currentTarget.select()} + onKeyDown={(event) => { + if (event.key === "Enter") { + event.preventDefault(); + event.currentTarget.blur(); + } else if (event.key === "Escape") { + cancelRename(); + } + }} + onBlur={() => commitRename(conversation)} + /> +
    + ) : ( +
    + + + + + setMenuOpenId(open ? conversation.id : null)} + overlayClassName="creation-history-menu-overlay" + menu={{ + items: [ + { key: "rename", label: "重命名", icon: }, + { key: "export", label: "导出对话", icon: }, + { key: "delete", label: "删除对话", danger: true, icon: }, + ], + onClick: ({ key }) => { + setMenuOpenId(null); + if (key === "rename") { + beginRename(conversation); + } else if (key === "export") { + downloadCreationConversation(conversation, assistantName, exportUser?.displayName || "你"); + drawerToast.success("对话已导出为 Markdown"); + } else { + onDelete(conversation); + } + }, + }} + > + + +
    + )} +
  • , + ]; + })} +
+ ) : ( +
{keyword.trim() ? "没有找到匹配的对话" : "暂无历史对话"}
+ )} +
+
+ ); } export function buildConversationExportMarkdown(conversation: CreationConversation, assistantName: string, userName: string) { @@ -133,7 +257,7 @@ export function buildConversationExportMarkdown(conversation: CreationConversati for (const message of conversation.messages) { const stamp = formatMessageTime(message.createdAt); const modeTag = message.mode && message.mode !== "text" ? (message.mode === "image" ? "[图像生成] " : "[视频生成] ") : ""; - const speaker = message.role === "user" ? (userName || "我") : assistantName; + const speaker = message.role === "user" ? userName || "我" : assistantName; if (message.role === "user") { const prompt = displayCreationPrompt(message.content, message.references || []).trim(); if (!prompt) continue; diff --git a/web/src/pages/create/creation-workspace-messages.tsx b/web/src/pages/create/creation-workspace-messages.tsx index b9287ce44..e314ee6d4 100644 --- a/web/src/pages/create/creation-workspace-messages.tsx +++ b/web/src/pages/create/creation-workspace-messages.tsx @@ -24,22 +24,100 @@ import { Button } from "antd"; import { CanvasImagePreview } from "@/components/canvas/canvas-image-preview"; import { AppModal } from "@/components/ui/product/app-modal"; -export function CreationMessageView({ item, shotNumber, onRetryFailure, onCreateVariant, onEditUserMessage, onContinueCanvas, openingCanvas }: { item: CreationMessage; shotNumber: number; onRetryFailure: () => void; onCreateVariant: () => void; onEditUserMessage: (text: string) => void; onContinueCanvas: (ids?: string[]) => void; openingCanvas: boolean }) { +export function CreationMessageView({ + item, + shotNumber, + onRetryFailure, + onCreateVariant, + onEditUserMessage, + onContinueCanvas, + openingCanvas, +}: { + item: CreationMessage; + shotNumber: number; + onRetryFailure: () => void; + onCreateVariant: () => void; + onEditUserMessage: (text: string) => void; + onContinueCanvas: (ids?: string[]) => void; + openingCanvas: boolean; +}) { const brandName = useAppearanceStore((state) => state.appearance.brandName); if (item.role === "user") return ; const mode = item.mode || "text"; const stateLabel = item.status === "pending" ? "生成中" : item.status === "cancelled" ? "已停止" : item.status === "error" ? "生成失败" : ""; const heading = mode !== "text" ? ( - <>{shotNumber > 0 ? 镜 {shotNumber} : null}{mode === "image" ? "图像生成" : "视频生成"}{item.status === "pending" ? {brandName}正在生成{mode === "video" ? "视频" : "图像"}…… : item.status === "done" ? 你的{mode === "video" ? "视频" : "图像"}已创建 : null}{item.status === "done" ? : null}{item.createdAt ? : null}{stateLabel ? {stateLabel} : null} + <> + {shotNumber > 0 ? 镜 {shotNumber} : null} + + + + {mode === "image" ? "图像生成" : "视频生成"} + {item.status === "pending" ? ( + + {brandName}正在生成{mode === "video" ? "视频" : "图像"}…… + + ) : item.status === "done" ? ( + 你的{mode === "video" ? "视频" : "图像"}已创建 + ) : null} + {item.status === "done" ? ( + + ) : null} + {item.createdAt ? : null} + {stateLabel ? {stateLabel} : null} + ) : ( - <>{shotNumber > 0 ? 镜 {shotNumber} : null}{brandName}{item.createdAt ? : null}{stateLabel ? {stateLabel} : null} + <> + {shotNumber > 0 ? 镜 {shotNumber} : null} + + + + {brandName} + {item.createdAt ? : null} + {stateLabel ? {stateLabel} : null} + ); const toolStatus: GenerationToolStatus = item.status === "pending" ? "running" : item.status === "error" ? "error" : item.status === "cancelled" ? "cancelled" : "completed"; - return
- {mode === "text" ? <>
{heading}
{item.reasoning ?
: null}
{item.content ? {item.content} : 正在生成…}
: } - {item.error && mode === "text" ?
{generationErrorMessage(item.error)}
: null} -
; + return ( +
+ {mode === "text" ? ( + <> +
{heading}
+ {item.reasoning ? ( +
+ +
+ ) : null} +
+ {item.content ? ( + {item.content} + ) : ( + + + 正在生成… + + )} +
+ + ) : ( + + + + )} + {item.error && mode === "text" ? ( +
+ {generationErrorMessage(item.error)} + +
+ ) : null} +
+ ); } export function CreationUserMessage({ item, shotNumber, onEditUserMessage }: { item: CreationMessage; shotNumber: number; onEditUserMessage: (text: string) => void }) { @@ -49,31 +127,93 @@ export function CreationUserMessage({ item, shotNumber, onEditUserMessage }: { i const visiblePrompt = displayCreationPrompt(item.content, item.references || []); const user = useUserStore((state) => state.user); const userAvatarUrl = user?.avatarUrl?.trim(); - return
-
{shotNumber > 0 ? 镜 {shotNumber} : null}{item.createdAt ? : null}{user?.displayName || "你"}{userAvatarUrl ? : }
-

{visiblePrompt}

- {item.references?.length ? : null} - {item.attachments?.length ?
{item.attachments.map((attachment) => { - const kind = creationAttachmentKind(attachment); - const previewable = kind === "image" || kind === "video"; - const url = attachment.previewUrl || ("dataUrl" in attachment ? attachment.dataUrl : attachment.url) || ""; - const imageUrl = kind === "image" ? resolveResourceUrl(attachment.storageKey, url) : ""; - const previewUrl = kind === "image" ? imageUrl : url; - return ; - })}
: null} -
- setPreviewUrl("")} /> -
; + return ( +
+
+ {shotNumber > 0 ? 镜 {shotNumber} : null} + {item.createdAt ? : null} + {user?.displayName || "你"} + {userAvatarUrl ? : } +
+
+

{visiblePrompt}

+
+ {item.references?.length ? : null} + {item.attachments?.length ? ( +
+ {item.attachments.map((attachment) => { + const kind = creationAttachmentKind(attachment); + const previewable = kind === "image" || kind === "video"; + const url = attachment.previewUrl || ("dataUrl" in attachment ? attachment.dataUrl : attachment.url) || ""; + const imageUrl = kind === "image" ? resolveResourceUrl(attachment.storageKey, url) : ""; + const previewUrl = kind === "image" ? imageUrl : url; + return ( + + ); + })} +
+ ) : null} +
+ + + + + + +
+ setPreviewUrl("")} /> +
+ ); } -export function MediaResult({ item, onRetryFailure, onCreateVariant, onContinueCanvas, openingCanvas }: { item: CreationMessage; onRetryFailure: () => void; onCreateVariant: () => void; onContinueCanvas: (ids?: string[]) => void; openingCanvas: boolean }) { +export function MediaResult({ + item, + onRetryFailure, + onCreateVariant, + onContinueCanvas, + openingCanvas, +}: { + item: CreationMessage; + onRetryFailure: () => void; + onCreateVariant: () => void; + onContinueCanvas: (ids?: string[]) => void; + openingCanvas: boolean; +}) { const [previewUrl, setPreviewUrl] = useState(""); const [previewType, setPreviewType] = useState<"image" | "video">("image"); const assets = useAssetStore((state) => state.assets); const storedResultUrls = item.resultUrls || []; - const resultStorageKeys = item.resultStorageKeys?.length - ? item.resultStorageKeys - : creationResultStorageKeys(assets, { messageId: item.id, taskIds: item.taskIds || [], resultUrls: storedResultUrls }); + const resultStorageKeys = item.resultStorageKeys?.length ? item.resultStorageKeys : creationResultStorageKeys(assets, { messageId: item.id, taskIds: item.taskIds || [], resultUrls: storedResultUrls }); const [resolvedResultUrls, setResolvedResultUrls] = useState(storedResultUrls); useEffect(() => { @@ -134,52 +274,192 @@ export function MediaResult({ item, onRetryFailure, onCreateVariant, onContinueC const expectedResultCount = resultStorageKeys.length || alignedResultUrls.length; const canContinueWithResults = expectedResultCount > 0 && resultAssetIds.length === expectedResultCount; if (item.status === "pending") return ; - if ((item.status === "error" || item.status === "cancelled") && !expectedResultCount) return
{item.status === "cancelled" ? item.content || "已停止" : generationErrorMessage(item.error || "生成失败")}
; - if (!expectedResultCount || !displayResultUrls.length) return
没有返回可预览结果
; + if ((item.status === "error" || item.status === "cancelled") && !expectedResultCount) + return ( +
+ {item.status === "cancelled" ? item.content || "已停止" : generationErrorMessage(item.error || "生成失败")} + +
+ ); + if (!expectedResultCount || !displayResultUrls.length) + return ( +
+ 没有返回可预览结果{" "} + +
+ ); const isVideo = item.mode === "video"; - return
- {isVideo ? :
{displayResultUrls.map((url) => )}
} -
{isVideo ? "视频结果" : `${displayResultUrls.length} 张图片`}{displayResultUrls.map((url, index) => {displayResultUrls.length > 1 ? `下载 ${index + 1}` : <>下载})}
- setPreviewUrl("")} /> -
; + return ( +
+ {isVideo ? ( + + ) : ( +
+ {displayResultUrls.map((url) => ( + + ))} +
+ )} +
+ {isVideo ? "视频结果" : `${displayResultUrls.length} 张图片`} + + {displayResultUrls.map((url, index) => ( + + {displayResultUrls.length > 1 ? ( + `下载 ${index + 1}` + ) : ( + <> + + 下载 + + )} + + ))} +
+ setPreviewUrl("")} /> +
+ ); } export function CreationMediaPending({ mode, ratio }: { mode: CreationMode; ratio?: string }) { const brandName = useAppearanceStore((state) => state.appearance.brandName); - return
{brandName}正在生成{mode === "video" ? "视频" : "图像"}
; + return ( +
+ + + + + {brandName}正在生成{mode === "video" ? "视频" : "图像"} + +
+ ); } export function CreationMessageReferences({ references }: { references: CreationReference[] }) { - return
{references.map((reference) => { - const Icon = reference.kind === "skill" ? Sparkles : reference.kind === "image" ? ImageIcon : reference.kind === "video" ? Film : reference.kind === "audio" ? Music2 : FileText; - const imageUrl = reference.kind === "image" ? resolveResourceUrl(reference.storageKey, reference.previewUrl) : reference.previewUrl; - return {imageUrl && reference.kind === "video" ? ; - })}
; + return ( +
+ {references.map((reference) => { + const Icon = reference.kind === "skill" ? Sparkles : reference.kind === "image" ? ImageIcon : reference.kind === "video" ? Film : reference.kind === "audio" ? Music2 : FileText; + const imageUrl = reference.kind === "image" ? resolveResourceUrl(reference.storageKey, reference.previewUrl) : reference.previewUrl; + return ( + + {imageUrl && reference.kind === "video" ? ( + + ); + })} +
+ ); } export function CreationMediaPreviewModal({ url, type, onClose }: { url: string; type: "image" | "video"; onClose: () => void }) { if (type === "image") return ; - return - {url ? ; + return ( + + {url ? + ); } -export function CreationAttachmentThumbnail({ item, onPreview, onRemove }: { - item: CreationAttachment; - onPreview: (type: "image" | "video", url: string) => void; - onRemove: (id: string) => void; -}) { +export function CreationAttachmentThumbnail({ item, onPreview, onRemove }: { item: CreationAttachment; onPreview: (type: "image" | "video", url: string) => void; onRemove: (id: string) => void }) { const kind = creationAttachmentKind(item); const previewable = kind === "image" || kind === "video"; const url = (kind === "video" ? item.url : item.previewUrl) || ""; const imageUrl = kind === "image" ? resolveResourceUrl(item.storageKey, item.previewUrl) : ""; const previewUrl = kind === "image" ? imageUrl : url; - const content = kind === "video" ?