diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..02be578 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "chatgpt.openOnStartup": true +} \ No newline at end of file diff --git "a/D\357\200\272Devclaw-scope.claudestatemilestonesa099994a6cd1a2ba7.jsonl" "b/D\357\200\272Devclaw-scope.claudestatemilestonesa099994a6cd1a2ba7.jsonl" new file mode 100644 index 0000000..eef9977 --- /dev/null +++ "b/D\357\200\272Devclaw-scope.claudestatemilestonesa099994a6cd1a2ba7.jsonl" @@ -0,0 +1,6 @@ +{"ts":"2026-04-15T21:10:53+08:00","event":"phase_start","detail":"party-mode-discussion: Cursor subagent configuration"} +{"ts":"2026-04-15T21:11:32+08:00","event":"tdd_red","detail":"checkpoint-1: identified Cursor-Claude capability gap"} +{"ts":"2026-04-15T21:12:05+08:00","event":"tdd_green","detail":"checkpoint-2: converged on stage-1 static export approach"} +{"ts":"2026-04-15T21:12:31+08:00","event":"tdd_refactor","detail":"checkpoint-3: refined glob mapping and disclaimer mechanism"} +{"ts":"2026-04-15T21:13:01+08:00","event":"tdd_refactor","detail":"checkpoint-4: provided MVP implementation code"} +{"ts":"2026-04-15T21:13:22+08:00","event":"phase_complete","detail":"party-mode converged: Cursor subagent export solution"} diff --git "a/D\357\200\272Devclaw-scope.claudestatemilestonesa480dfee29406285f.jsonl" "b/D\357\200\272Devclaw-scope.claudestatemilestonesa480dfee29406285f.jsonl" new file mode 100644 index 0000000..fd4e112 --- /dev/null +++ "b/D\357\200\272Devclaw-scope.claudestatemilestonesa480dfee29406285f.jsonl" @@ -0,0 +1 @@ +{"ts":"2026-04-15T10:00:00Z","event":"phase_start","detail":"party-mode-initialization"} diff --git "a/D\357\200\272Devclaw-scope.claudestatemilestonesa559a9568a8a9f1e9.jsonl" "b/D\357\200\272Devclaw-scope.claudestatemilestonesa559a9568a8a9f1e9.jsonl" new file mode 100644 index 0000000..9871531 --- /dev/null +++ "b/D\357\200\272Devclaw-scope.claudestatemilestonesa559a9568a8a9f1e9.jsonl" @@ -0,0 +1 @@ +{"ts":"2026-04-15T00:00:00Z","event":"phase_start","detail":"party-mode: cursor-subagent-recognition"} diff --git "a/D\357\200\272Devclaw-scope.claudestatemilestonesa9884bf81a9a4d2fb.jsonl" "b/D\357\200\272Devclaw-scope.claudestatemilestonesa9884bf81a9a4d2fb.jsonl" new file mode 100644 index 0000000..77db129 --- /dev/null +++ "b/D\357\200\272Devclaw-scope.claudestatemilestonesa9884bf81a9a4d2fb.jsonl" @@ -0,0 +1 @@ +{"ts":"2026-04-16T06:10:00Z","event":"phase_start","detail":"party-mode: cursor subagent recognition deep dive"} diff --git "a/D\357\200\272Devclaw-scope.claudestatemilestonesacfa8498ecbbb9129.jsonl" "b/D\357\200\272Devclaw-scope.claudestatemilestonesacfa8498ecbbb9129.jsonl" new file mode 100644 index 0000000..37cfab9 --- /dev/null +++ "b/D\357\200\272Devclaw-scope.claudestatemilestonesacfa8498ecbbb9129.jsonl" @@ -0,0 +1,7 @@ +{"ts":"2026-04-15T10:00:00Z","event":"phase_start","detail":"Party Mode: Cursor custom subagents recognition"} +{"ts":"2026-04-15T10:05:00Z","event":"phase_complete","detail":"Checkpoint 1/5 - Problem analysis and solution exploration"} +{"ts":"2026-04-15T10:10:00Z","event":"phase_complete","detail":"Checkpoint 2/5 - Solution evaluation and risk analysis"} +{"ts":"2026-04-15T10:15:00Z","event":"phase_complete","detail":"Checkpoint 3/5 - Solution refinement and expectation adjustment"} +{"ts":"2026-04-15T10:20:00Z","event":"phase_complete","detail":"Checkpoint 4/5 - Implementation details and edge cases"} +{"ts":"2026-04-15T10:25:00Z","event":"phase_complete","detail":"Checkpoint 5/5 - Final convergence and conclusion"} +{"ts":"2026-04-15T10:26:00Z","event":"phase_complete","detail":"Party Mode completed - 100 rounds, consensus reached"} diff --git a/README.en.md b/README.en.md index d16e176..09cb11a 100644 --- a/README.en.md +++ b/README.en.md @@ -1,7 +1,7 @@ # ClawScope

- ClawScope cover + ClawScope cover

**Memory Made Visible, Evolution Enabled** · [中文](README.zh.md) @@ -28,7 +28,7 @@ npm run tauri dev ClawScope connects to OpenClaw through the OpenClaw Gateway. When troubleshooting, verify the Gateway bind mode first, then verify authentication. If TCP / WebSocket cannot connect, the token or password has not been checked yet. -![OpenClaw Gateway binding and authentication flow](public/images/diagrams/openclaw-gateway-auth-binding.svg) +![OpenClaw Gateway binding and authentication flow](public/images/diagrams/openclaw-gateway-auth-binding-en.svg) ### Config File And jq diff --git a/README.md b/README.md index d16e176..09cb11a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ClawScope

- ClawScope cover + ClawScope cover

**Memory Made Visible, Evolution Enabled** · [中文](README.zh.md) @@ -28,7 +28,7 @@ npm run tauri dev ClawScope connects to OpenClaw through the OpenClaw Gateway. When troubleshooting, verify the Gateway bind mode first, then verify authentication. If TCP / WebSocket cannot connect, the token or password has not been checked yet. -![OpenClaw Gateway binding and authentication flow](public/images/diagrams/openclaw-gateway-auth-binding.svg) +![OpenClaw Gateway binding and authentication flow](public/images/diagrams/openclaw-gateway-auth-binding-en.svg) ### Config File And jq diff --git a/README.zh.md b/README.zh.md index 28f6617..fe78410 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,7 +1,7 @@ # ClawScope

- ClawScope 封面 + ClawScope 封面

**记忆可见,进化可期** · [English](README.md) diff --git a/public/images/covers/clawscope-cover-bg-v2.png b/public/images/covers/clawscope-cover-bg-v2.png new file mode 100644 index 0000000..aa4c6b9 Binary files /dev/null and b/public/images/covers/clawscope-cover-bg-v2.png differ diff --git "a/public/images/covers/clawscope-cover-bg-v3 - \345\211\257\346\234\254.png" "b/public/images/covers/clawscope-cover-bg-v3 - \345\211\257\346\234\254.png" new file mode 100644 index 0000000..6778119 Binary files /dev/null and "b/public/images/covers/clawscope-cover-bg-v3 - \345\211\257\346\234\254.png" differ diff --git a/public/images/covers/clawscope-cover-bg-v3.png b/public/images/covers/clawscope-cover-bg-v3.png new file mode 100644 index 0000000..6778119 Binary files /dev/null and b/public/images/covers/clawscope-cover-bg-v3.png differ diff --git a/public/images/covers/clawscope-cover-bg.png b/public/images/covers/clawscope-cover-bg.png new file mode 100644 index 0000000..f1c8624 Binary files /dev/null and b/public/images/covers/clawscope-cover-bg.png differ diff --git a/public/images/covers/clawscope-cover-v2.meta.txt b/public/images/covers/clawscope-cover-v2.meta.txt new file mode 100644 index 0000000..5933de5 --- /dev/null +++ b/public/images/covers/clawscope-cover-v2.meta.txt @@ -0,0 +1,12 @@ +background=D:\Dev\claw-scope\public\images\covers\clawscope-cover-bg-v2.png +icon=D:\Dev\claw-scope\icon-source.png +output=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v2.png +canvas=3840x2160 +icon_compositing=original 512x512 pixels, no resize, no crop, no recolor, no model redraw +icon_size=(512, 512) +icon_center=(920, 1100) +icon_top_left=(664, 844) +title=ClawScope +tagline=记忆可见,进化可期 +title_font=C:\Windows\Fonts\segoeuib.ttf +tagline_font=C:\Windows\Fonts\NotoSansSC-VF.ttf diff --git a/public/images/covers/clawscope-cover-v2.png b/public/images/covers/clawscope-cover-v2.png new file mode 100644 index 0000000..2aa30f4 Binary files /dev/null and b/public/images/covers/clawscope-cover-v2.png differ diff --git a/public/images/covers/clawscope-cover-v3-animated-en.gif b/public/images/covers/clawscope-cover-v3-animated-en.gif new file mode 100644 index 0000000..4f33bcb Binary files /dev/null and b/public/images/covers/clawscope-cover-v3-animated-en.gif differ diff --git a/public/images/covers/clawscope-cover-v3-animated-en.webp b/public/images/covers/clawscope-cover-v3-animated-en.webp index 9cb2691..368d2e1 100644 Binary files a/public/images/covers/clawscope-cover-v3-animated-en.webp and b/public/images/covers/clawscope-cover-v3-animated-en.webp differ diff --git a/public/images/covers/clawscope-cover-v3-animated.gif b/public/images/covers/clawscope-cover-v3-animated.gif new file mode 100644 index 0000000..fe376ae Binary files /dev/null and b/public/images/covers/clawscope-cover-v3-animated.gif differ diff --git a/public/images/covers/clawscope-cover-v3-animated.webp b/public/images/covers/clawscope-cover-v3-animated.webp index cd11fb9..21b1930 100644 Binary files a/public/images/covers/clawscope-cover-v3-animated.webp and b/public/images/covers/clawscope-cover-v3-animated.webp differ diff --git a/public/images/covers/clawscope-cover-v3-final-en.meta.txt b/public/images/covers/clawscope-cover-v3-final-en.meta.txt new file mode 100644 index 0000000..9cf3bee --- /dev/null +++ b/public/images/covers/clawscope-cover-v3-final-en.meta.txt @@ -0,0 +1,12 @@ +ClawScope README cover post-processing +locale=en +background=D:\Dev\claw-scope\public\images\covers\clawscope-cover-bg-v3.png +icon=D:\Dev\claw-scope\icon-source.png +static=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v3-final-en.png +animated_webp=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v3-animated-en.webp +animated_gif=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v3-animated-en.gif +canvas=3840x2160 +icon_display=900x900 +icon_center=(860, 1080) +icon_policy=uniform upscale from icon-source.png, preserve silhouette/proportions/colors/transparency; no redraw or recolor +motion_language=flowing memory currents, pulsing knowledge nodes, semantic search ripples, OpenClaw ecosystem paths, evolution diff streams diff --git a/public/images/covers/clawscope-cover-v3-final-en.png b/public/images/covers/clawscope-cover-v3-final-en.png new file mode 100644 index 0000000..b7aafc4 Binary files /dev/null and b/public/images/covers/clawscope-cover-v3-final-en.png differ diff --git a/public/images/covers/clawscope-cover-v3-final.meta.txt b/public/images/covers/clawscope-cover-v3-final.meta.txt new file mode 100644 index 0000000..ee10c9b --- /dev/null +++ b/public/images/covers/clawscope-cover-v3-final.meta.txt @@ -0,0 +1,12 @@ +ClawScope README cover post-processing +locale=zh +background=D:\Dev\claw-scope\public\images\covers\clawscope-cover-bg-v3.png +icon=D:\Dev\claw-scope\icon-source.png +static=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v3-final.png +animated_webp=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v3-animated.webp +animated_gif=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v3-animated.gif +canvas=3840x2160 +icon_display=900x900 +icon_center=(860, 1080) +icon_policy=uniform upscale from icon-source.png, preserve silhouette/proportions/colors/transparency; no redraw or recolor +motion_language=flowing memory currents, pulsing knowledge nodes, semantic search ripples, OpenClaw ecosystem paths, evolution diff streams diff --git a/public/images/covers/clawscope-cover-v3-final.png b/public/images/covers/clawscope-cover-v3-final.png new file mode 100644 index 0000000..d2c8502 Binary files /dev/null and b/public/images/covers/clawscope-cover-v3-final.png differ diff --git a/public/images/covers/clawscope-cover-v4-animated-en.gif b/public/images/covers/clawscope-cover-v4-animated-en.gif new file mode 100644 index 0000000..2036db6 Binary files /dev/null and b/public/images/covers/clawscope-cover-v4-animated-en.gif differ diff --git a/public/images/covers/clawscope-cover-v4-animated-en.webp b/public/images/covers/clawscope-cover-v4-animated-en.webp new file mode 100644 index 0000000..be230a5 Binary files /dev/null and b/public/images/covers/clawscope-cover-v4-animated-en.webp differ diff --git a/public/images/covers/clawscope-cover-v4-animated.gif b/public/images/covers/clawscope-cover-v4-animated.gif new file mode 100644 index 0000000..0173398 Binary files /dev/null and b/public/images/covers/clawscope-cover-v4-animated.gif differ diff --git a/public/images/covers/clawscope-cover-v4-animated.webp b/public/images/covers/clawscope-cover-v4-animated.webp new file mode 100644 index 0000000..0786c64 Binary files /dev/null and b/public/images/covers/clawscope-cover-v4-animated.webp differ diff --git a/public/images/covers/clawscope-cover-v4-final-en.meta.txt b/public/images/covers/clawscope-cover-v4-final-en.meta.txt new file mode 100644 index 0000000..50ab99a --- /dev/null +++ b/public/images/covers/clawscope-cover-v4-final-en.meta.txt @@ -0,0 +1,12 @@ +ClawScope README cover post-processing +locale=en +background=D:\Dev\claw-scope\public\images\covers\clawscope-cover-bg-v3.png +icon=D:\Dev\claw-scope\icon-source.png +static=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v4-final-en.png +animated_webp=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v4-animated-en.webp +animated_gif=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v4-animated-en.gif +canvas=3840x2160 +icon_display=900x900 +icon_center=(860, 1080) +icon_policy=uniform upscale from icon-source.png, preserve silhouette/proportions/colors/transparency; no redraw or recolor +motion_language=flowing memory currents, pulsing knowledge nodes, semantic search ripples, OpenClaw ecosystem paths, evolution diff streams diff --git a/public/images/covers/clawscope-cover-v4-final-en.png b/public/images/covers/clawscope-cover-v4-final-en.png new file mode 100644 index 0000000..19885a0 Binary files /dev/null and b/public/images/covers/clawscope-cover-v4-final-en.png differ diff --git a/public/images/covers/clawscope-cover-v4-final.meta.txt b/public/images/covers/clawscope-cover-v4-final.meta.txt new file mode 100644 index 0000000..71da6c1 --- /dev/null +++ b/public/images/covers/clawscope-cover-v4-final.meta.txt @@ -0,0 +1,12 @@ +ClawScope README cover post-processing +locale=zh +background=D:\Dev\claw-scope\public\images\covers\clawscope-cover-bg-v3.png +icon=D:\Dev\claw-scope\icon-source.png +static=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v4-final.png +animated_webp=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v4-animated.webp +animated_gif=D:\Dev\claw-scope\public\images\covers\clawscope-cover-v4-animated.gif +canvas=3840x2160 +icon_display=900x900 +icon_center=(860, 1080) +icon_policy=uniform upscale from icon-source.png, preserve silhouette/proportions/colors/transparency; no redraw or recolor +motion_language=flowing memory currents, pulsing knowledge nodes, semantic search ripples, OpenClaw ecosystem paths, evolution diff streams diff --git a/public/images/covers/clawscope-cover-v4-final.png b/public/images/covers/clawscope-cover-v4-final.png new file mode 100644 index 0000000..ce9ea22 Binary files /dev/null and b/public/images/covers/clawscope-cover-v4-final.png differ diff --git a/public/images/covers/clawscope-cover.meta.txt b/public/images/covers/clawscope-cover.meta.txt new file mode 100644 index 0000000..e37fcb1 --- /dev/null +++ b/public/images/covers/clawscope-cover.meta.txt @@ -0,0 +1,12 @@ +background=D:\Dev\claw-scope\public\images\covers\clawscope-cover-bg.png +icon=D:\Dev\claw-scope\icon-source.png +output=D:\Dev\claw-scope\public\images\covers\clawscope-cover.png +canvas=3840x2160 +icon_compositing=original 512x512 pixels, no resize, no recolor, no model redraw +icon_size=(512, 512) +icon_center=(1178, 1170) +title=ClawScope +tagline=记忆可见,进化可期 +caption=OpenClaw Memory & Evolution Control Center +title_font=C:\Windows\Fonts\segoeuib.ttf +tagline_font=C:\Windows\Fonts\NotoSansSC-VF.ttf diff --git a/public/images/covers/clawscope-cover.png b/public/images/covers/clawscope-cover.png new file mode 100644 index 0000000..e1b2ced Binary files /dev/null and b/public/images/covers/clawscope-cover.png differ diff --git a/public/images/diagrams/clawscope-system-architecture.html b/public/images/diagrams/clawscope-system-architecture.html new file mode 100644 index 0000000..2d543aa --- /dev/null +++ b/public/images/diagrams/clawscope-system-architecture.html @@ -0,0 +1,384 @@ + + + + + + ClawScope System Architecture + + + + + + +
+
+
+
+

ClawScope System Architecture

+
+
+ + + +
+ +
+
+

OpenClaw 记忆可视化、配置管理与进化审计的本地桌面控制面

+
+ +
+ + ClawScope 系统架构图 + ClawScope 由 React 视图层、Tauri 命令桥、Rust 本地能力、OpenClaw Gateway 与本地 JSON 状态存储组成。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ClawScope System Architecture + OpenClaw 记忆可视化、配置管理与进化审计的本地桌面控制面 + + + + + + + + + + + + + + + + + + ClawScope Desktop App / Tauri 2 runtime + + React UI Layer + + Rust Core Layer + + External OpenClaw Runtime + + + Operator + 搜索 / 审计 / 配置 + 桌面工作台 + + + Shell + Router + Profile / Memory / Config + Evolution views + + OpenClawContext + 连接状态 / 心跳 / 会话列表 + LAN discovery / saved endpoints + 统一前端命令入口 + + Domain Panels + 记忆搜索 / 知识图谱 / 足迹 + Agent 设置 / 配置 schema + 进化预览与审计报告 + + Browser localStorage + URL / auth mode / setup flags + + + Tauri IPC + invoke(...) + gateway_* + evolution_* + + + Gateway Orchestrator + commands + connector + WebSocket RPC / methods + + Session State + active sessions / snapshots + pending request router + + Auth + Discovery + device identity / signer + challenge / token fallback + + Evolution Engine + preview / execute / rollback + + Gateway Store + identity / tokens / endpoints + + Evolution Store + history / audit / snapshots + + + OpenClaw Gateway + ws://host:18789 + protocol verified endpoint + + Agent Registry + agents / identity / workspace + + Memory Runtime + MEMORY.md / daily memory + semantic search / timeline + index + diagnostics + + Config Surface + agent settings / config schema + + + UI + views call context + commands + WebSocket RPC + challenge + signed connect + memory get/set/index + 本地 JSON 状态保障离线重连、审计与回滚 + + Legend + + Frontend + + Rust backend + + Local store + + Security/auth + + IPC/runtime boundary + + Auth flow + + Data/control flow + Source: README architecture overview, src/app/contexts/OpenClawContext.tsx, src-tauri/src/lib.rs, gateway/*, evolution/* + +
+ +
+
+

用户看见的是控制面

+
    +
  • • React 负责 Profile、Memory、Config、Evolution 四类工作视图。
  • +
  • • OpenClawContext 将连接、心跳、会话与发现逻辑收束到一个入口。
  • +
  • • Browser localStorage 只保存界面级配置,不保存 Rust 审计状态。
  • +
+
+
+

本机 Rust 负责可信执行

+
    +
  • • Tauri IPC 将前端动作映射到 gateway_* 与 evolution_* commands。
  • +
  • • Gateway connector 维护 WebSocket 会话、请求路由与协议能力。
  • +
  • • Auth/Discovery 处理设备身份、签名连接、token fallback 与局域网发现。
  • +
+
+
+

进化流程可审计、可回滚

+
    +
  • • Evolution 先读取 MEMORY 文档生成 preview,再进入 execute。
  • +
  • • 本地 history、audit-log 与 snapshots 记录关键决策和恢复点。
  • +
  • • 执行后回写 OpenClaw memory,并触发 index 刷新。
  • +
+
+
+ + +
+ + + + diff --git a/public/images/diagrams/openclaw-gateway-auth-binding-en.svg b/public/images/diagrams/openclaw-gateway-auth-binding-en.svg new file mode 100644 index 0000000..f0adeb0 --- /dev/null +++ b/public/images/diagrams/openclaw-gateway-auth-binding-en.svg @@ -0,0 +1,66 @@ + + OpenClaw Gateway binding and authentication flow + A client reaches OpenClaw Gateway through a LAN URL, the Gateway listens on a bind mode, then applies token, password, none, or trusted proxy authentication before exposing OpenClaw runtime access. + + + + + + + + + + + + + + + + OpenClaw Gateway Setup Path + Verify the listening scope first, then choose auth; ClawScope checks token / password only after the network and WebSocket handshake succeed. + + + + ClawScope Client + http://LAN-IP:18789 + Test TCP / WebSocket + + + + + Gateway Listener + + bind = lan + 0.0.0.0:18789 + loopback is local-only + + + + + Auth Layer + + token + + password + + none + + + + + OpenClaw Runtime + Memory, evolution, agent config, and file access open only after auth passes. + + + + + + + + + Minimal Switch Loop + 1. Edit openclaw.json via jq + 2. Restart Gateway + 3. Retest in ClawScope + + diff --git a/public/images/diagrams/openclaw-gateway-auth-binding.html b/public/images/diagrams/openclaw-gateway-auth-binding.html new file mode 100644 index 0000000..5febc20 --- /dev/null +++ b/public/images/diagrams/openclaw-gateway-auth-binding.html @@ -0,0 +1,50 @@ + + + + + + OpenClaw Gateway 绑定与认证配置图 + + + +
+ OpenClaw Gateway 绑定与认证配置路径图 +

+ 该 HTML 是 README 配图的源/预览文件。README 中发布的是同目录下的 + openclaw-gateway-auth-binding.svg,避免在 Markdown 中嵌入整页 HTML。 +

+
+ + diff --git a/src/app/components/views/MemoryFootprintsPanel.tsx b/src/app/components/views/MemoryFootprintsPanel.tsx index 440ff69..e8d6e07 100644 --- a/src/app/components/views/MemoryFootprintsPanel.tsx +++ b/src/app/components/views/MemoryFootprintsPanel.tsx @@ -95,6 +95,19 @@ export function MemoryFootprintsPanel({ chip: tonePalette.softBadge, outline: `${tonePalette.softBadge} bg-transparent`, }; + const formatProbeDateLabel = (date: string) => date.replace(/^(\d{4})-(\d{2})-(\d{2})$/, "$2/$3"); + + const formatProbeDateList = (dates: string[], limit = 3) => { + if (dates.length === 0) { + return []; + } + + return dates.slice(0, limit).map(formatProbeDateLabel); + }; + + const coveredPreview = formatProbeDateList(timelineProbeFeedback.coveredDates); + const missingPreviewDates = timelineProbeFeedback.missingDates.slice(0, 6); + const probingPreview = formatProbeDateList(timelineProbeFeedback.probingDates); const highlightSelection = useMemo(() => { if (!timelineEntryContent || !selectedHighlightTerm) { @@ -256,19 +269,25 @@ export function MemoryFootprintsPanel({ -
- onProbeRangeChange({ ...timelineProbeRange, startDate: event.target.value })} - className={`h-9 rounded-lg border border-slate-300 bg-white px-3 text-xs outline-none dark:border-slate-700 dark:bg-slate-950 ${toneClasses.focus}`} - placeholder={t("memory.footprints.probePlaceholder")} - /> - onProbeRangeChange({ ...timelineProbeRange, endDate: event.target.value })} - className={`h-9 rounded-lg border border-slate-300 bg-white px-3 text-xs outline-none dark:border-slate-700 dark:bg-slate-950 ${toneClasses.focus}`} - placeholder={t("memory.footprints.probePlaceholder")} - /> +
+ + - {timelineProbeFeedback.failureReasons[date] ? ( - - {timelineProbeFeedback.failureReasons[date]} - - ) : null} -
- ))} + ); + })} + {timelineProbeFeedback.missingDates.length > missingPreviewDates.length ? ( + + +{timelineProbeFeedback.missingDates.length - missingPreviewDates.length} + + ) : null}
) : ( -
{t("memory.footprints.probe.none")}
+
{t("memory.footprints.probe.none")}
)} -
- {t("memory.footprints.probe.probingDays")}: {timelineProbeFeedback.probingDates.length > 0 ? timelineProbeFeedback.probingDates.join(", ") : t("memory.footprints.probe.idleState")} +
+
+
{t("memory.footprints.probe.probingDays")}
+ + {timelineProbeFeedback.probingDates.length} + +
+
+ {probingPreview.length > 0 ? ( + probingPreview.map((date) => ( + + {date} + + )) + ) : ( + {t("memory.footprints.probe.idleState")} + )} + {timelineProbeFeedback.probingDates.length > probingPreview.length ? ( + + +{timelineProbeFeedback.probingDates.length - probingPreview.length} + + ) : null} +
) : ( -
{t("memory.footprints.probeHint")}
+
{t("memory.footprints.probeHint")}
)} {timelineError ?
{timelineError}
: null}
- - {filteredFootprintGroups.length === 0 ? ( -
-
- + + {filteredFootprintGroups.length === 0 ? ( +
+
+ +
+

{t("memory.footprints.emptyTitle")}

+

{t("memory.footprints.emptyDesc")}

-

{t("memory.footprints.emptyTitle")}

-

{t("memory.footprints.emptyDesc")}

-
- ) : ( -
- {filteredFootprintGroups.map((group) => ( -
-
- -
-
entry.name === selectedTimelineEntryName) ? `${toneClasses.selected} shadow-sm` : "border-slate-200 bg-slate-50/70 dark:border-slate-800 dark:bg-slate-900/70"}`}> -
-

{group.dateLabel}

- {t("memory.footprints.entries", group.entries.length)} - {group.probeDay ? {probeStatusLabel(group.probeDay.status, t)} : null} + ) : ( +
+ {filteredFootprintGroups.map((group) => ( +
+
+
-
-
{group.entries[0]?.content ? group.entries[0].content.slice(0, 180) : group.entries[0]?.path ?? t("memory.footprints.noDetail")}
-
entry.name === selectedTimelineEntryName) ? toneClasses.selectedBadge : "border-slate-200 bg-white text-slate-600 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300"}`}>{group.entries[0]?.updatedAtMs ? new Date(group.entries[0].updatedAtMs).toLocaleTimeString() : t("memory.footprints.noTime")}
+
entry.name === selectedTimelineEntryName) ? `${toneClasses.selected} shadow-sm` : "border-slate-200 bg-slate-50/70 dark:border-slate-800 dark:bg-slate-900/70"}`}> +
+

{group.dateLabel}

+ {t("memory.footprints.entries", group.entries.length)} + {group.probeDay ? {probeStatusLabel(group.probeDay.status, t)} : null} +
+
+
{group.entries[0]?.content ? group.entries[0].content.slice(0, 180) : group.entries[0]?.path ?? t("memory.footprints.noDetail")}
+
entry.name === selectedTimelineEntryName) ? toneClasses.selectedBadge : "border-slate-200 bg-white text-slate-600 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300"}`}>{group.entries[0]?.updatedAtMs ? new Date(group.entries[0].updatedAtMs).toLocaleTimeString() : t("memory.footprints.noTime")}
+
-
-
- {group.entries.map((item) => ( -
onSelectTimelineEntry(item.name)}> -
-
-
-
- {item.updatedAtMs ? new Date(item.updatedAtMs).toLocaleTimeString() : "-"} - {timelineAccess?.mode ?? timelineResult?.source ?? t("memory.footprints.timelineFallback")} - {getAgentBadge(selectedAgentId)} +
+ {group.entries.map((item) => ( +
onSelectTimelineEntry(item.name)}> +
+
+
+
+ {item.updatedAtMs ? new Date(item.updatedAtMs).toLocaleTimeString() : "-"} + {timelineAccess?.mode ?? timelineResult?.source ?? t("memory.footprints.timelineFallback")} + {getAgentBadge(selectedAgentId)} +
+ {item.name}
- {item.name} +

{item.path}

-

{item.path}

-
- ))} + ))} +
-
- ))} -
- )} - + ))} +
+ )} +
diff --git a/src/app/contexts/I18nContext.tsx b/src/app/contexts/I18nContext.tsx index b7d17ef..b6134e2 100644 --- a/src/app/contexts/I18nContext.tsx +++ b/src/app/contexts/I18nContext.tsx @@ -2134,6 +2134,8 @@ const BASE_DICT: Record = { "探测范围", "探測範圍", ], + "memory.footprints.probeStart": ["Start date", "开始日期", "開始日期"], + "memory.footprints.probeEnd": ["End date", "结束日期", "結束日期"], "memory.footprints.probePlaceholder": [ "YYYY-MM-DD", "YYYY-MM-DD", diff --git a/tmp/bmad-speckit-sdd-flow-0.1.0.tgz b/tmp/bmad-speckit-sdd-flow-0.1.0.tgz new file mode 100644 index 0000000..d43f153 Binary files /dev/null and b/tmp/bmad-speckit-sdd-flow-0.1.0.tgz differ diff --git a/tmp/check-icon-on-white.png b/tmp/check-icon-on-white.png new file mode 100644 index 0000000..57cc074 Binary files /dev/null and b/tmp/check-icon-on-white.png differ diff --git a/tmp/check-ios-on-white.png b/tmp/check-ios-on-white.png new file mode 100644 index 0000000..8976d1c Binary files /dev/null and b/tmp/check-ios-on-white.png differ diff --git a/tmp/check-ios20-after-threshold.png b/tmp/check-ios20-after-threshold.png new file mode 100644 index 0000000..dd88e2d Binary files /dev/null and b/tmp/check-ios20-after-threshold.png differ diff --git a/tmp/check-square150-after-threshold.png b/tmp/check-square150-after-threshold.png new file mode 100644 index 0000000..b00327a Binary files /dev/null and b/tmp/check-square150-after-threshold.png differ diff --git a/tmp/check-square150-on-white.png b/tmp/check-square150-on-white.png new file mode 100644 index 0000000..eeacfdc Binary files /dev/null and b/tmp/check-square150-on-white.png differ diff --git a/tmp/cover-restore/current-en-frame0.png b/tmp/cover-restore/current-en-frame0.png new file mode 100644 index 0000000..38db271 Binary files /dev/null and b/tmp/cover-restore/current-en-frame0.png differ diff --git a/tmp/cover-restore/current-zh-frame0.png b/tmp/cover-restore/current-zh-frame0.png new file mode 100644 index 0000000..fa28608 Binary files /dev/null and b/tmp/cover-restore/current-zh-frame0.png differ diff --git a/tmp/cover-restore/new-en-frame0.png b/tmp/cover-restore/new-en-frame0.png new file mode 100644 index 0000000..c6b46c5 Binary files /dev/null and b/tmp/cover-restore/new-en-frame0.png differ diff --git a/tmp/cover-restore/new-zh-frame0.png b/tmp/cover-restore/new-zh-frame0.png new file mode 100644 index 0000000..7785beb Binary files /dev/null and b/tmp/cover-restore/new-zh-frame0.png differ diff --git a/tmp/cover-restore/orig-en-frame0.png b/tmp/cover-restore/orig-en-frame0.png new file mode 100644 index 0000000..fc5b8c5 Binary files /dev/null and b/tmp/cover-restore/orig-en-frame0.png differ diff --git a/tmp/cover-restore/orig-en.webp b/tmp/cover-restore/orig-en.webp new file mode 100644 index 0000000..9cb2691 Binary files /dev/null and b/tmp/cover-restore/orig-en.webp differ diff --git a/tmp/cover-restore/orig-zh-frame0.png b/tmp/cover-restore/orig-zh-frame0.png new file mode 100644 index 0000000..668ccfd Binary files /dev/null and b/tmp/cover-restore/orig-zh-frame0.png differ diff --git a/tmp/cover-restore/orig-zh.webp b/tmp/cover-restore/orig-zh.webp new file mode 100644 index 0000000..cd11fb9 Binary files /dev/null and b/tmp/cover-restore/orig-zh.webp differ diff --git a/tmp/cover-restore/v4-en-frame0.png b/tmp/cover-restore/v4-en-frame0.png new file mode 100644 index 0000000..25518fb Binary files /dev/null and b/tmp/cover-restore/v4-en-frame0.png differ diff --git a/tmp/cover-restore/v4-zh-frame0.png b/tmp/cover-restore/v4-zh-frame0.png new file mode 100644 index 0000000..92e4c43 Binary files /dev/null and b/tmp/cover-restore/v4-zh-frame0.png differ diff --git a/tmp/current-icon-on-black.png b/tmp/current-icon-on-black.png new file mode 100644 index 0000000..ba4aa9a Binary files /dev/null and b/tmp/current-icon-on-black.png differ diff --git a/tmp/final-32-white.png b/tmp/final-32-white.png new file mode 100644 index 0000000..928256b Binary files /dev/null and b/tmp/final-32-white.png differ diff --git a/tmp/final-icon-preview-clean.png b/tmp/final-icon-preview-clean.png new file mode 100644 index 0000000..0a2d34f Binary files /dev/null and b/tmp/final-icon-preview-clean.png differ diff --git a/tmp/final-icon-white.png b/tmp/final-icon-white.png new file mode 100644 index 0000000..57cc074 Binary files /dev/null and b/tmp/final-icon-white.png differ diff --git a/tmp/git-head-icons/icon-source.png b/tmp/git-head-icons/icon-source.png new file mode 100644 index 0000000..ee42a9f Binary files /dev/null and b/tmp/git-head-icons/icon-source.png differ diff --git a/tmp/git-head-icons/icon.png b/tmp/git-head-icons/icon.png new file mode 100644 index 0000000..ce1771a Binary files /dev/null and b/tmp/git-head-icons/icon.png differ diff --git a/tmp/git-head-icons/src-tauri__icons__icon.icns b/tmp/git-head-icons/src-tauri__icons__icon.icns new file mode 100644 index 0000000..a4cd7e4 Binary files /dev/null and b/tmp/git-head-icons/src-tauri__icons__icon.icns differ diff --git a/tmp/git-head-icons/src-tauri__icons__icon.png b/tmp/git-head-icons/src-tauri__icons__icon.png new file mode 100644 index 0000000..b63d352 Binary files /dev/null and b/tmp/git-head-icons/src-tauri__icons__icon.png differ diff --git a/tmp/icon-source-masked-test-black.png b/tmp/icon-source-masked-test-black.png new file mode 100644 index 0000000..dd35090 Binary files /dev/null and b/tmp/icon-source-masked-test-black.png differ diff --git a/tmp/icon-source-masked-test.png b/tmp/icon-source-masked-test.png new file mode 100644 index 0000000..077398d Binary files /dev/null and b/tmp/icon-source-masked-test.png differ diff --git a/tmp/icon-transparent-preview.png b/tmp/icon-transparent-preview.png new file mode 100644 index 0000000..d7c5994 Binary files /dev/null and b/tmp/icon-transparent-preview.png differ diff --git a/tmp/imagegen/_test_anim.gif b/tmp/imagegen/_test_anim.gif new file mode 100644 index 0000000..5a33f72 Binary files /dev/null and b/tmp/imagegen/_test_anim.gif differ diff --git a/tmp/imagegen/_test_anim.webp b/tmp/imagegen/_test_anim.webp new file mode 100644 index 0000000..0e4ac33 Binary files /dev/null and b/tmp/imagegen/_test_anim.webp differ diff --git a/tmp/imagegen/clawscope-cover-api-run/output.json b/tmp/imagegen/clawscope-cover-api-run/output.json new file mode 100644 index 0000000..03a16ab --- /dev/null +++ b/tmp/imagegen/clawscope-cover-api-run/output.json @@ -0,0 +1,6 @@ +{ + "stage": "output_saved", + "output": "D:\\Dev\\claw-scope\\public\\images\\covers\\clawscope-cover-bg.png", + "output_bytes": 8329654, + "total_elapsed_ms": 89781 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-api-run/request-started.json b/tmp/imagegen/clawscope-cover-api-run/request-started.json new file mode 100644 index 0000000..1afebb7 --- /dev/null +++ b/tmp/imagegen/clawscope-cover-api-run/request-started.json @@ -0,0 +1,17 @@ +{ + "stage": "request_started", + "url": "https://ai-api-cn.db-kj.com/v1/images/generations", + "method": "POST", + "model": "gpt-image-2", + "size": "3840x2160", + "quality": "high", + "output_format": "png", + "n": 1, + "has_api_key": true, + "input_image": "D:\\Dev\\claw-scope\\icon-source.png", + "input_image_bytes": 329197, + "prompt_path": "D:\\Dev\\claw-scope\\tmp\\imagegen\\clawscope-cover-bg-prompt.txt", + "prompt_chars": 2629, + "body_bytes": 441766, + "started_ms": 1779106195743 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-api-run/response-status.json b/tmp/imagegen/clawscope-cover-api-run/response-status.json new file mode 100644 index 0000000..7e8f117 --- /dev/null +++ b/tmp/imagegen/clawscope-cover-api-run/response-status.json @@ -0,0 +1,26 @@ +{ + "stage": "response_received", + "http_status": 200, + "headers": { + "Access-Control-Allow-Headers": "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, X-API-Key, x-stainless-lang, x-stainless-package-version, x-stainless-os, x-stainless-arch, x-stainless-retry-count, x-stainless-runtime, x-stainless-runtime-version, x-stainless-async, x-stainless-helper-method, x-stainless-poll-helper, x-stainless-custom-poll-interval, x-stainless-timeout", + "Access-Control-Allow-Methods": "POST, OPTIONS, GET, PUT, DELETE, PATCH", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "ETag", + "Access-Control-Max-Age": "86400", + "Alt-Svc": "h3=\":443\"; ma=2592000", + "Content-Length": "11108880", + "Content-Type": "application/json; charset=utf-8", + "Date": "Mon, 18 May 2026 12:11:05 GMT", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Server": "nginx/1.30.1", + "X-Accel-Buffering": "no", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + "X-New-Api-Version": "v0.13.1-custom-1", + "X-Oneapi-Request-Id": "202605181209568985028398268d9d6iFkmd87P", + "X-Request-Id": "dc0c5a2c-9d01-49f4-af58-3b36ea371e65", + "Connection": "close" + }, + "response_bytes": 11108880, + "elapsed_ms": 89501 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-bg-prompt.txt b/tmp/imagegen/clawscope-cover-bg-prompt.txt new file mode 100644 index 0000000..53e1cb7 --- /dev/null +++ b/tmp/imagegen/clawscope-cover-bg-prompt.txt @@ -0,0 +1,18 @@ +Use case: ads-marketing +Asset type: GitHub README / project cover banner +Primary request: Create a premium hero cover background for a desktop developer tool named "ClawScope". The final project icon will be composited later as an exact original image, so do not redraw, imitate, distort, transform, or generate the logo/icon. Leave a clean central-left glass badge area reserved for the exact icon overlay. + +Input image: Image 1 is a brand reference only. Use it only to infer the project's visual language, geometry, color temperament, and technology mood. Do not reproduce, redraw, modify, or embed the icon from the input image. + +Scene/backdrop: A refined Apple-style technology cover with a dark polished spatial interface, subtle depth layers, translucent glass panels, soft metallic surfaces, and a high-end macOS product-launch feeling. +Subject: ClawScope as a memory visibility and agent evolution control center; abstract visual metaphors of memory nodes, knowledge graph arcs, audit trails, and a precise observation lens. +Style/medium: Premium 3D editorial product render, Apple keynote aesthetic, clean high-end technology visual, cinematic but restrained. +Composition/framing: 16:9 wide banner. Reserve a 360x360 px circular or rounded-square glass badge area on the left-center for the exact project icon to be composited later. Keep the reserved area empty, clean, uncluttered, and visually prominent. Put the main visual energy flowing from the icon area toward the right side with elegant node networks and luminous interface depth. +Lighting/mood: Soft studio lighting, dark graphite background, subtle cyan and blue highlights, controlled rim light, polished glass reflections, strong depth without clutter. +Color palette: Deep slate black, graphite, frosted glass, cyan-blue glow, small emerald accents, restrained silver highlights. Avoid purple-dominant styling. +Materials/textures: Frosted glass, anodized aluminum, subtle etched grid, luminous particles, thin vector-like data paths, premium macOS-style translucency. +Text: No rendered text inside the image. Leave clean negative space on the right for post-production typography: "ClawScope" and "记忆可见,进化可期". +Constraints: Do not include any logo, icon, app mark, fake text, watermark, UI screenshots, humans, animals, claws, cartoon mascots, or generic hacker imagery. The reserved icon badge must remain empty and clean for exact compositing. +Avoid: cheap sci-fi, cyberpunk clutter, neon overload, stock-photo look, noisy backgrounds, distorted symbols, illegible pseudo text, oversized lens flare, purple-on-black cliche. +Quality: high +Size: 3840x2160 diff --git a/tmp/imagegen/clawscope-cover-bg-v2-prompt.txt b/tmp/imagegen/clawscope-cover-bg-v2-prompt.txt new file mode 100644 index 0000000..a4634dd --- /dev/null +++ b/tmp/imagegen/clawscope-cover-bg-v2-prompt.txt @@ -0,0 +1,48 @@ +Use case: ads-marketing +Asset type: GitHub README / project cover banner + +Primary request: +Create a premium hero cover background for a desktop developer tool named "ClawScope". The final exact project icon will be composited later in post-production, so do not redraw, imitate, distort, reinterpret, or generate the icon/logo. The image should feel like a vibrant dark-mode macOS product poster: high-end, colorful, playful, technical, and memorable, but not like a rigid enterprise control room. + +Input image: +If Image 1 is provided, treat it only as a brand mood reference for color energy and personality. Do not reproduce or redraw the icon. + +Brand interpretation: +ClawScope is a memory visibility and agent evolution desktop app. It should feel like a polished macOS productivity tool for exploring memory, timelines, semantic search, knowledge graphs, and evolution diffs. The tone is smart, lively, precise, and approachable, not cold or militarized. + +Visual direction: +Use a rich dark-mode foundation, but avoid monochrome black. Build the scene with deep midnight blue, ink teal, soft slate, and subtle graphite gradients, energized by colorful semantic accents: cyan/sky blue, coral red, emerald green, violet, amber, and rose. The color system should feel diverse and alive, echoing a modern app UI with multiple feature modules. + +Icon landing zone: +Reserve a natural icon docking area on the left-center of the canvas for the exact original project icon to be composited later. The safe zone should be approximately 620x620 px on a 3840x2160 canvas, centered around x=900, y=1080, designed to receive a 512x512 rounded-square icon with 50-70 px breathing room. +The landing area should not look like an empty acrylic panel or placeholder box. Instead, create a subtle soft halo, gentle cyan/coral ambient glow, and a natural shadow-receiving surface so the real icon will feel integrated after compositing. Keep this zone clean, uncluttered, and free of lines crossing behind the icon. Do not draw any fake icon, logo frame, symbol, app mark, or placeholder graphic inside it. + +Scene/backdrop: +A refined dark-mode spatial desktop interface, with floating rounded app cards, soft layered surfaces, memory timeline strips, small semantic graph nodes, colorful knowledge arcs, diff-like green/red accent bands, and delicate data paths. The visual language should resemble a beautiful product launch image for a macOS developer tool, not a sci-fi command center. + +Composition/framing: +16:9 wide banner. Left-center is the reserved icon landing zone. From the icon zone, let the visual energy flow toward the right side through colorful memory nodes, timeline fragments, knowledge graph ribbons, and clean UI card layers. Reserve clean negative space on the right half for post-production typography: "ClawScope" and "记忆可见,进化可期". Do not render any text in the image. + +Lighting/mood: +Premium dark-mode lighting with depth, but lively and clear. Use soft studio glow, subtle rim light, colorful reflections, and gentle atmospheric gradients. The image should feel vivid, modern, and optimistic, not gloomy. + +Materials/textures: +Soft frosted UI surfaces, rounded cards, subtle grain, luminous vector-like paths, delicate grid hints, small colorful particles, polished but lightweight macOS-style translucency. Avoid heavy acrylic blocks. + +Color palette: +Deep midnight blue, ink teal, slate, soft graphite, bright cyan/sky blue, coral red from the icon mood, emerald green, violet, amber, rose, restrained silver highlights. No purple dominance. No flat black dominance. + +Text: +No rendered text. No fake UI labels. No pseudo text. Leave clean space for exact typography to be added later. + +Constraints: +Do not include any logo, icon, app mark, fake text, watermark, UI screenshot, humans, animals, claws, cartoon mascot, hacker imagery, terminal wall, surveillance dashboard, or sci-fi cockpit. Do not create a visible empty badge panel. The reserved icon area must remain clean and natural for exact compositing. + +Avoid: +monochrome dark control center, cyberpunk clutter, cheap sci-fi, blue-black corporate SOC dashboard, hard acrylic capsule, giant lens flare, fake glass pedestal, generic AI network background, noisy background, distorted symbols, illegible pseudo text, purple-on-black cliché, overly serious military/operations-room mood. + +Quality: +high + +Size: +3840x2160 diff --git a/tmp/imagegen/clawscope-cover-bg-v3-prompt.txt b/tmp/imagegen/clawscope-cover-bg-v3-prompt.txt new file mode 100644 index 0000000..f0f4eab --- /dev/null +++ b/tmp/imagegen/clawscope-cover-bg-v3-prompt.txt @@ -0,0 +1,57 @@ +Use case: ads-marketing +Asset type: GitHub README / project cover banner + +Primary request: +Create a bright, colorful, motion-ready hero cover background for a desktop developer tool named "ClawScope". The final exact project icon and all title typography will be composited later in post-production. Do not redraw, imitate, distort, reinterpret, or generate the logo/icon. Do not render any text. + +Input image: +If Image 1 is provided, treat it only as a brand mood reference for color personality: aqua/cyan app-icon background, coral-red character energy, and blue observation-lens feeling. Do not reproduce, redraw, or imitate the icon. The exact icon will be added later. + +Brand interpretation: +ClawScope is an OpenClaw ecosystem desktop tool. It makes agent memory visible and agent evolution controllable. It connects to OpenClaw Gateway, observes agents, inspects memory documents, maps timeline traces, performs semantic search, visualizes knowledge graphs, previews evolution diffs, tracks audit trails, and supports rollback paths. The image should immediately communicate: observe memory, search knowledge, understand evolution, and control change. + +Visual direction: +Use an ocean-inspired bright technology palette. Do not use a dark sci-fi control room style. Build the scene with luminous aqua, cyan, lagoon blue, seafoam, soft sky blue, pearl white, and clean slate, energized by coral red, emerald green, violet, amber, and rose accents. The mood should be vivid, optimistic, lively, premium, and product-like. It should feel like a polished macOS app launch artwork for an intelligent developer tool, not a generic AI network background. + +Scene/backdrop: +Create an abstract ocean-current product map rather than a literal ocean scene. Use flowing translucent currents, soft gradients, rounded UI cards, memory document tiles, timeline beads, semantic knowledge graph nodes, search ripple rings, and evolution diff streams. Show the OpenClaw ecosystem as a readable abstract flow: a central OpenClaw Gateway-like hub connects to multiple agent nodes, memory cards, timeline traces, knowledge clusters, audit trails, and green/red evolution diff paths. No text labels are needed. + +Icon integration area: +Leave a natural open visual focus area on the left-center for the exact project icon to be composited later as a large hero app icon, approximately 900px wide on a 3840x2160 canvas. The icon will be uniformly scaled from the original source in post-production, preserving its shape, colors, proportions, transparency, and rounded-square silhouette. + +The open visual focus area should be about 1000x1000px to 1080x1080px, centered around x=860, y=1080. It must feel naturally sized for a large hero app icon, not a small badge. This area must not look like a reserved empty slot. Do not draw any visible frame, panel, badge, pedestal, rounded rectangle, square, glass plate, docking card, border, shadow box, or placeholder container. + +The icon area should be open luminous ocean-gradient space only: soft aqua/cyan ambient glow, subtle coral warmth, and smooth background depth that will blend naturally with the real icon's cyan background and coral-red subject. All memory nodes, cards, particles, graph paths, ribbons, and motion trails must curve around this area and fade before entering it. Nothing should cross behind or touch the future icon area. + +Composition/framing: +16:9 wide banner. Left-center is the natural icon focus area. The main visual energy should flow from the future icon area toward the center and right through ocean-current paths, OpenClaw ecosystem flows, memory cards, timeline beads, semantic search ripples, knowledge graph clusters, audit trails, and green/red evolution diff streams. Reserve clean readable space on the right side for post-production typography: "ClawScope" and "记忆可见,进化可期". The right side should be rich, bright, and energetic, but not cluttered. + +Motion language: +Design the image as the first frame of a looping animated README banner. Include clear motion cues that can be animated later: flowing ocean-current paths, pulsing knowledge nodes, drifting rounded memory cards, expanding search ripple rings, moving timeline beads, and green/red diff streams traveling through the OpenClaw ecosystem. The still image should already feel alive and directional. + +Style/medium: +Premium editorial product illustration, clean vector-3D hybrid, macOS app launch aesthetic, bright oceanic technology artwork, crisp rounded shapes, soft depth, gentle translucency, playful but precise, polished but approachable. + +Lighting/mood: +Bright ocean glow, soft studio lighting, airy depth, gentle ambient bloom, optimistic and vivid. Avoid gloomy lighting, heavy black shadows, and cold enterprise dashboard mood. + +Color palette: +Aqua, cyan, lagoon blue, seafoam, sky blue, pearl white, soft slate, coral red, emerald green, violet, amber, and rose. Diverse and lively module accents. No monochrome blue. No dark graphite dominance. No purple dominance. + +Materials/textures: +Soft translucent UI surfaces, rounded cards, subtle glass-like highlights, clean vector paths, luminous nodes, fine grain, light ocean-current gradients, delicate grid hints, smooth depth layers. Keep the image breathable and fresh. + +Text: +No rendered text. No fake UI labels. No pseudo text. No watermark. Leave clean space for exact title typography to be added later. + +Constraints: +Do not include any logo, icon, app mark, fake text, watermark, humans, animals, claws, cartoon mascots, literal lobster, hacker imagery, terminal wall, surveillance dashboard, cockpit, data center, enterprise command center, or cyberpunk scene. Do not create a visible empty icon container. Do not create any large rounded-square placeholder behind the future icon area. + +Avoid: +dark sci-fi control room, black-blue corporate dashboard, cyberpunk clutter, generic AI neural network background, empty glass badge, acrylic panel, hard icon container, literal underwater scene, fish, waves, beach, boats, coral reef, noisy particles, illegible pseudo text, distorted symbols, military mood, operations-room mood, cold monochrome technology poster. + +Quality: +high + +Size: +3840x2160 diff --git a/tmp/imagegen/clawscope-cover-v2-api-run/output.json b/tmp/imagegen/clawscope-cover-v2-api-run/output.json new file mode 100644 index 0000000..ca4758f --- /dev/null +++ b/tmp/imagegen/clawscope-cover-v2-api-run/output.json @@ -0,0 +1,6 @@ +{ + "stage": "output_saved", + "output": "D:\\Dev\\claw-scope\\public\\images\\covers\\clawscope-cover-bg-v2.png", + "output_bytes": 7132247, + "total_elapsed_ms": 91416 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-v2-api-run/request-started.json b/tmp/imagegen/clawscope-cover-v2-api-run/request-started.json new file mode 100644 index 0000000..8e30fb9 --- /dev/null +++ b/tmp/imagegen/clawscope-cover-v2-api-run/request-started.json @@ -0,0 +1,17 @@ +{ + "stage": "request_started", + "url": "https://ai-api-cn.db-kj.com/v1/images/generations", + "method": "POST", + "model": "gpt-image-2", + "size": "3840x2160", + "quality": "high", + "output_format": "png", + "n": 1, + "has_api_key": true, + "input_image": "D:\\Dev\\claw-scope\\icon-source.png", + "input_image_bytes": 329197, + "prompt_path": "D:\\Dev\\claw-scope\\tmp\\imagegen\\clawscope-cover-bg-v2-prompt.txt", + "prompt_chars": 4380, + "body_bytes": 443548, + "started_ms": 1779108129298 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-v2-api-run/response-status.json b/tmp/imagegen/clawscope-cover-v2-api-run/response-status.json new file mode 100644 index 0000000..a58f334 --- /dev/null +++ b/tmp/imagegen/clawscope-cover-v2-api-run/response-status.json @@ -0,0 +1,26 @@ +{ + "stage": "response_received", + "http_status": 200, + "headers": { + "Access-Control-Allow-Headers": "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, X-API-Key, x-stainless-lang, x-stainless-package-version, x-stainless-os, x-stainless-arch, x-stainless-retry-count, x-stainless-runtime, x-stainless-runtime-version, x-stainless-async, x-stainless-helper-method, x-stainless-poll-helper, x-stainless-custom-poll-interval, x-stainless-timeout", + "Access-Control-Allow-Methods": "POST, OPTIONS, GET, PUT, DELETE, PATCH", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "ETag", + "Access-Control-Max-Age": "86400", + "Alt-Svc": "h3=\":443\"; ma=2592000", + "Content-Length": "9512392", + "Content-Type": "application/json; charset=utf-8", + "Date": "Mon, 18 May 2026 12:43:23 GMT", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Server": "nginx/1.30.1", + "X-Accel-Buffering": "no", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + "X-New-Api-Version": "v0.13.1-custom-1", + "X-Oneapi-Request-Id": "202605181242106228124988268d9d6v68saQFB", + "X-Request-Id": "df55c6ca-6637-4063-b83f-d24f5c17a9c2", + "Connection": "close" + }, + "response_bytes": 9512392, + "elapsed_ms": 91063 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-v3-api-run/output.json b/tmp/imagegen/clawscope-cover-v3-api-run/output.json new file mode 100644 index 0000000..a6ffbd2 --- /dev/null +++ b/tmp/imagegen/clawscope-cover-v3-api-run/output.json @@ -0,0 +1,6 @@ +{ + "stage": "output_saved", + "output": "D:\\Dev\\claw-scope\\public\\images\\covers\\clawscope-cover-bg-v3.png", + "output_bytes": 8188199, + "total_elapsed_ms": 120364 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-v3-api-run/request-started.json b/tmp/imagegen/clawscope-cover-v3-api-run/request-started.json new file mode 100644 index 0000000..524b75b --- /dev/null +++ b/tmp/imagegen/clawscope-cover-v3-api-run/request-started.json @@ -0,0 +1,17 @@ +{ + "stage": "request_started", + "url": "https://ai-api-cn.db-kj.com/v1/images/generations", + "method": "POST", + "model": "gpt-image-2", + "size": "3840x2160", + "quality": "high", + "output_format": "png", + "n": 1, + "has_api_key": true, + "input_image": "D:\\Dev\\claw-scope\\icon-source.png", + "input_image_bytes": 329197, + "prompt_path": "D:\\Dev\\claw-scope\\tmp\\imagegen\\clawscope-cover-bg-v3-prompt.txt", + "prompt_chars": 6189, + "body_bytes": 445365, + "started_ms": 1779114544774 +} \ No newline at end of file diff --git a/tmp/imagegen/clawscope-cover-v3-api-run/response-status.json b/tmp/imagegen/clawscope-cover-v3-api-run/response-status.json new file mode 100644 index 0000000..1479dbf --- /dev/null +++ b/tmp/imagegen/clawscope-cover-v3-api-run/response-status.json @@ -0,0 +1,26 @@ +{ + "stage": "response_received", + "http_status": 200, + "headers": { + "Access-Control-Allow-Headers": "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, X-API-Key, x-stainless-lang, x-stainless-package-version, x-stainless-os, x-stainless-arch, x-stainless-retry-count, x-stainless-runtime, x-stainless-runtime-version, x-stainless-async, x-stainless-helper-method, x-stainless-poll-helper, x-stainless-custom-poll-interval, x-stainless-timeout", + "Access-Control-Allow-Methods": "POST, OPTIONS, GET, PUT, DELETE, PATCH", + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "ETag", + "Access-Control-Max-Age": "86400", + "Alt-Svc": "h3=\":443\"; ma=2592000", + "Content-Length": "10920990", + "Content-Type": "application/json; charset=utf-8", + "Date": "Mon, 18 May 2026 14:30:33 GMT", + "Referrer-Policy": "strict-origin-when-cross-origin", + "Server": "nginx/1.30.1", + "X-Accel-Buffering": "no", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + "X-New-Api-Version": "v0.13.1-custom-1", + "X-Oneapi-Request-Id": "202605181429062849626868268d9d6sHGVUYEo", + "X-Request-Id": "02a451f3-f9dd-4a42-a84f-7a53d6b03ef1", + "Connection": "close" + }, + "response_bytes": 10920990, + "elapsed_ms": 119895 +} \ No newline at end of file diff --git a/tmp/imagegen/postprocess_clawscope_cover.py b/tmp/imagegen/postprocess_clawscope_cover.py new file mode 100644 index 0000000..990ca19 --- /dev/null +++ b/tmp/imagegen/postprocess_clawscope_cover.py @@ -0,0 +1,446 @@ +from __future__ import annotations + +import math +import os +from pathlib import Path + +import numpy as np +from PIL import Image, ImageChops, ImageDraw, ImageFilter, ImageFont + + +ROOT = Path(__file__).resolve().parents[2] +BACKGROUND = ROOT / "public/images/covers/clawscope-cover-bg-v3.png" +ICON = ROOT / "icon-source.png" +OUT_DIR = ROOT / "public/images/covers" +LOCALE = os.environ.get("COVER_LOCALE", "zh").strip().lower() +IS_EN = LOCALE in {"en", "eng", "english"} +OUTPUT_VERSION = "v4" +OUTPUT_SUFFIX = "-en" if IS_EN else "" +OUT_STATIC = OUT_DIR / f"clawscope-cover-{OUTPUT_VERSION}-final{OUTPUT_SUFFIX}.png" +OUT_WEBP = OUT_DIR / f"clawscope-cover-{OUTPUT_VERSION}-animated{OUTPUT_SUFFIX}.webp" +OUT_GIF = OUT_DIR / f"clawscope-cover-{OUTPUT_VERSION}-animated{OUTPUT_SUFFIX}.gif" +OUT_META = OUT_DIR / f"clawscope-cover-{OUTPUT_VERSION}-final{OUTPUT_SUFFIX}.meta.txt" + +LATIN_FONT_BOLD = Path(r"C:\Windows\Fonts\segoeuib.ttf") +LATIN_FONT = Path(r"C:\Windows\Fonts\segoeui.ttf") +SC_FONT = Path(r"C:\Windows\Fonts\NotoSansSC-VF.ttf") + +CANVAS = (3840, 2160) +ICON_CENTER = (860, 1080) +ICON_SIZE = 900 + + +def font(path: Path, size: int) -> ImageFont.FreeTypeFont: + return ImageFont.truetype(str(path), size=size) + + +def ease_in_out(value: float) -> float: + return 0.5 - 0.5 * math.cos(math.tau * value) + + +def rgba(color: tuple[int, int, int], alpha: int) -> tuple[int, int, int, int]: + return color[0], color[1], color[2], alpha + + +def radial_layer(size: tuple[int, int], inner: tuple[int, int, int, int], outer: tuple[int, int, int, int]) -> Image.Image: + width, height = size + y, x = np.ogrid[-1:1:height * 1j, -1:1:width * 1j] + distance = np.sqrt(x * x + y * y) + t = np.clip(distance, 0, 1) ** 1.35 + inner_arr = np.array(inner, dtype=np.float32) + outer_arr = np.array(outer, dtype=np.float32) + arr = inner_arr * (1 - t[..., None]) + outer_arr * t[..., None] + return Image.fromarray(np.clip(arr, 0, 255).astype(np.uint8), "RGBA") + + +def paste_center(base: Image.Image, layer: Image.Image, center: tuple[float, float]) -> None: + x = int(round(center[0] - layer.width / 2)) + y = int(round(center[1] - layer.height / 2)) + base.alpha_composite(layer, (x, y)) + + +def scaled_alpha(mask: Image.Image, factor: float) -> Image.Image: + return mask.point(lambda p: max(0, min(255, int(p * factor)))) + + +def layer_from_alpha(size: tuple[int, int], color: tuple[int, int, int], alpha: Image.Image) -> Image.Image: + layer = Image.new("RGBA", size, (*color, 0)) + layer.putalpha(alpha) + return layer + + +def bezier(points: tuple[tuple[float, float], tuple[float, float], tuple[float, float], tuple[float, float]], steps: int = 80) -> list[tuple[float, float]]: + p0, p1, p2, p3 = points + result: list[tuple[float, float]] = [] + for i in range(steps + 1): + t = i / steps + mt = 1 - t + x = mt**3 * p0[0] + 3 * mt**2 * t * p1[0] + 3 * mt * t**2 * p2[0] + t**3 * p3[0] + y = mt**3 * p0[1] + 3 * mt**2 * t * p1[1] + 3 * mt * t**2 * p2[1] + t**3 * p3[1] + result.append((x, y)) + return result + + +def scale_points(points: list[tuple[float, float]], scale: float) -> list[tuple[float, float]]: + return [(x * scale, y * scale) for x, y in points] + + +def draw_polyline(draw: ImageDraw.ImageDraw, points: list[tuple[float, float]], fill: tuple[int, int, int, int], width: int) -> None: + if len(points) > 1: + draw.line(points, fill=fill, width=max(1, width), joint="curve") + + +def draw_flow( + overlay: Image.Image, + points: list[tuple[float, float]], + color: tuple[int, int, int], + scale: float, + phase: float, + alpha: int = 92, + width: int = 10, +) -> None: + soft = Image.new("RGBA", overlay.size, (0, 0, 0, 0)) + soft_draw = ImageDraw.Draw(soft) + draw_polyline(soft_draw, points, rgba(color, alpha), int(width * scale)) + overlay.alpha_composite(soft.filter(ImageFilter.GaussianBlur(max(1, int(4 * scale))))) + + crisp = ImageDraw.Draw(overlay) + draw_polyline(crisp, points, rgba(color, min(210, alpha + 70)), max(1, int(3 * scale))) + + if len(points) < 2: + return + + for offset, radius, dot_alpha in ((0.0, 13, 220), (0.22, 8, 150), (0.45, 5, 110)): + idx = int(((phase + offset) % 1) * (len(points) - 1)) + x, y = points[idx] + r = max(2, int(radius * scale)) + crisp.ellipse((x - r, y - r, x + r, y + r), fill=rgba(color, dot_alpha)) + crisp.ellipse((x - r * 0.42, y - r * 0.42, x + r * 0.42, y + r * 0.42), fill=(255, 255, 255, min(220, dot_alpha))) + + +def draw_icon(base: Image.Image, scale: float, phase: float = 0.0, animated: bool = False) -> None: + icon_size = int(round(ICON_SIZE * scale)) + center = (ICON_CENTER[0] * scale, ICON_CENTER[1] * scale) + source = Image.open(ICON).convert("RGBA").resize((icon_size, icon_size), Image.Resampling.LANCZOS) + alpha = source.getchannel("A") + + glow_strength = 1.0 + (0.12 * math.sin(math.tau * phase) if animated else 0.0) + halo_size = int(round(1350 * scale)) + halo = radial_layer( + (halo_size, halo_size), + (190, 250, 255, int(160 * glow_strength)), + (190, 250, 255, 0), + ).filter(ImageFilter.GaussianBlur(max(1, int(18 * scale)))) + paste_center(base, halo, center) + + coral = radial_layer( + (int(1100 * scale), int(900 * scale)), + (255, 112, 94, int(62 * glow_strength)), + (255, 112, 94, 0), + ).filter(ImageFilter.GaussianBlur(max(1, int(28 * scale)))) + paste_center(base, coral, (center[0] - 190 * scale, center[1] - 70 * scale)) + + blue_alpha = scaled_alpha(alpha.filter(ImageFilter.GaussianBlur(max(1, int(18 * scale)))), 0.18) + blue_glow = layer_from_alpha((icon_size, icon_size), (72, 218, 255), blue_alpha) + base.alpha_composite( + blue_glow, + ( + int(round(center[0] - icon_size / 2)), + int(round(center[1] - icon_size / 2)), + ), + ) + + coral_alpha = scaled_alpha(alpha.filter(ImageFilter.GaussianBlur(max(1, int(24 * scale)))), 0.10) + coral_glow = layer_from_alpha((icon_size, icon_size), (255, 96, 78), coral_alpha) + base.alpha_composite( + coral_glow, + ( + int(round(center[0] - icon_size / 2 - 28 * scale)), + int(round(center[1] - icon_size / 2 - 12 * scale)), + ), + ) + + outer = alpha.filter(ImageFilter.GaussianBlur(max(1, int(10 * scale)))) + inner = alpha.filter(ImageFilter.GaussianBlur(max(1, int(2 * scale)))) + edge_alpha = scaled_alpha(ImageChops.subtract(outer, inner), 0.46) + edge_glow = layer_from_alpha((icon_size, icon_size), (235, 255, 255), edge_alpha) + base.alpha_composite( + edge_glow, + ( + int(round(center[0] - icon_size / 2 - 2 * scale)), + int(round(center[1] - icon_size / 2 - 4 * scale)), + ), + ) + + bevel = Image.new("RGBA", (icon_size, icon_size), (0, 0, 0, 0)) + bevel_draw = ImageDraw.Draw(bevel) + bevel_draw.rounded_rectangle( + ( + 8 * scale, + 8 * scale, + icon_size - 8 * scale, + icon_size - 8 * scale, + ), + radius=118 * scale, + outline=(255, 255, 255, 120), + width=max(1, int(5 * scale)), + ) + bevel_draw.arc( + ( + 18 * scale, + 18 * scale, + icon_size - 18 * scale, + icon_size - 18 * scale, + ), + start=198, + end=318, + fill=(128, 245, 255, 112), + width=max(1, int(8 * scale)), + ) + bevel_draw.arc( + ( + 20 * scale, + 20 * scale, + icon_size - 20 * scale, + icon_size - 20 * scale, + ), + start=36, + end=128, + fill=(255, 125, 98, 80), + width=max(1, int(7 * scale)), + ) + bevel.putalpha(ImageChops.multiply(bevel.getchannel("A"), alpha)) + base.alpha_composite( + bevel, + ( + int(round(center[0] - icon_size / 2)), + int(round(center[1] - icon_size / 2)), + ), + ) + + base.alpha_composite(source, (int(round(center[0] - icon_size / 2)), int(round(center[1] - icon_size / 2)))) + + +def draw_badge(draw: ImageDraw.ImageDraw, xy: tuple[float, float], text: str, color: tuple[int, int, int], scale: float) -> None: + label_font = font(LATIN_FONT_BOLD, max(10, int(39 * scale))) + pad_x = 30 * scale + pad_y = 18 * scale + dot = 17 * scale + box = draw.textbbox((0, 0), text, font=label_font) + width = (box[2] - box[0]) + pad_x * 2 + dot * 2 + height = (box[3] - box[1]) + pad_y * 2 + x, y = xy + radius = height / 2 + draw.rounded_rectangle((x, y, x + width, y + height), radius=radius, fill=(255, 255, 255, 128), outline=rgba(color, 115), width=max(1, int(2 * scale))) + draw.ellipse((x + pad_x, y + height / 2 - dot / 2, x + pad_x + dot, y + height / 2 + dot / 2), fill=rgba(color, 230)) + draw.text((x + pad_x + dot + 16 * scale, y + height / 2), text, font=label_font, fill=(22, 68, 92, 235), anchor="lm") + + +def draw_typography(base: Image.Image, scale: float) -> None: + veil = radial_layer( + (int(1780 * scale), int(1040 * scale)), + (255, 255, 255, 120), + (255, 255, 255, 0), + ).filter(ImageFilter.GaussianBlur(max(1, int(24 * scale)))) + paste_center(base, veil, (2220 * scale, 920 * scale)) + + draw = ImageDraw.Draw(base) + title_font = font(LATIN_FONT_BOLD, max(24, int(286 * scale))) + slogan_font = font(LATIN_FONT_BOLD if IS_EN else SC_FONT, max(20, int((98 if IS_EN else 116) * scale))) + sub_font = font(LATIN_FONT if IS_EN else SC_FONT, max(12, int((60 if IS_EN else 68) * scale))) + label_font = font(LATIN_FONT_BOLD, max(10, int(42 * scale))) + + x = 1420 * scale + y = 560 * scale + badge_text = "OPENCLAW ECOSYSTEM" + badge_w = draw.textlength(badge_text, font=label_font) + 148 * scale + badge_h = 78 * scale + draw.rounded_rectangle( + (x, y - 108 * scale, x + badge_w, y - 108 * scale + badge_h), + radius=badge_h / 2, + fill=(255, 255, 255, 132), + outline=(14, 165, 233, 128), + width=max(1, int(2 * scale)), + ) + draw.ellipse( + (x + 30 * scale, y - 84 * scale, x + 58 * scale, y - 56 * scale), + fill=(14, 165, 233, 235), + ) + draw.ellipse( + (x + 66 * scale, y - 84 * scale, x + 94 * scale, y - 56 * scale), + fill=(244, 91, 77, 225), + ) + draw.line((x + 58 * scale, y - 70 * scale, x + 66 * scale, y - 70 * scale), fill=(13, 148, 136, 200), width=max(1, int(4 * scale))) + draw.text((x + 116 * scale, y - 70 * scale), badge_text, font=label_font, fill=(13, 71, 95, 235), anchor="lm") + + title = "ClawScope" + draw.text((x + 10 * scale, y + 14 * scale), title, font=title_font, fill=(255, 255, 255, 135), stroke_width=max(1, int(3 * scale)), stroke_fill=(255, 255, 255, 120)) + draw.text((x, y), title, font=title_font, fill=(5, 37, 66, 255), stroke_width=max(1, int(3 * scale)), stroke_fill=(255, 255, 255, 145)) + + slogan = "Memory Made Visible, Evolution Enabled" if IS_EN else "记忆可见,进化可期" + sy = y + 370 * scale + slogan_color = (4, 50, 76, 255) + # Same-color micro-offsets create weight without a visible shadow or outline. + for dx, dy in ((0, 0), (1 * scale, 0), (0, 1 * scale), (1 * scale, 1 * scale)): + draw.text((x + dx, sy + dy), slogan, font=slogan_font, fill=slogan_color) + + sub = ( + "OpenClaw memory visibility, search, and evolution workspace" + if IS_EN + else "OpenClaw 生态里的记忆可视化、知识检索与 Agent 进化工作台" + ) + draw.text( + (x, sy + 154 * scale), + sub, + font=sub_font, + fill=(3, 45, 67, 255), + stroke_width=max(1, int(2 * scale)), + stroke_fill=(195, 249, 255, 190), + ) + + chips = [ + ("Gateway", (14, 165, 233)), + ("Agents", (139, 92, 246)), + ("Memory", (6, 182, 212)), + ("Knowledge Graph", (16, 185, 129)), + ("Evolution Diff", (244, 91, 77)), + ("Rollback", (245, 158, 11)), + ] + cx = x + cy = sy + 280 * scale + for text, color in chips: + draw_badge(draw, (cx, cy), text, color, scale) + text_width = draw.textlength(text, font=font(LATIN_FONT_BOLD, max(10, int(39 * scale)))) + cx += text_width + 150 * scale + if cx > 3600 * scale: + cx = x + cy += 92 * scale + + +def draw_ecosystem_overlay(base: Image.Image, scale: float, phase: float, animated: bool) -> None: + overlay = Image.new("RGBA", base.size, (0, 0, 0, 0)) + + raw_paths = [ + (((1260, 840), (1610, 640), (2040, 690), (2410, 860)), (14, 165, 233), 0.08), + (((1300, 1080), (1700, 970), (2050, 1120), (2480, 1170)), (16, 185, 129), 0.34), + (((1250, 1320), (1580, 1530), (2060, 1460), (2620, 1490)), (244, 91, 77), 0.58), + (((1420, 1250), (1880, 1300), (2220, 1040), (2950, 1190)), (139, 92, 246), 0.72), + ] + + for raw, color, offset in raw_paths: + points = scale_points(bezier(raw, 95), scale) + local_phase = (phase + offset) % 1 if animated else offset + draw_flow(overlay, points, color, scale, local_phase) + + draw = ImageDraw.Draw(overlay) + ripple_centers = [(3000 * scale, 1265 * scale), (2460 * scale, 895 * scale)] + for center_x, center_y in ripple_centers: + for i in range(3): + p = (phase + i / 3) % 1 if animated else i / 3 + radius = (70 + 210 * p) * scale + alpha = int(115 * (1 - p)) + draw.ellipse( + (center_x - radius, center_y - radius, center_x + radius, center_y + radius), + outline=(14, 165, 233, alpha), + width=max(1, int(5 * scale)), + ) + + hub = (2530 * scale, 1390 * scale) + pulse = 1.0 + (0.16 * math.sin(math.tau * phase) if animated else 0.0) + for radius, color, alpha in ((98, (14, 165, 233), 60), (64, (255, 255, 255), 150), (34, (14, 165, 233), 220)): + r = radius * scale * pulse + draw.ellipse((hub[0] - r, hub[1] - r, hub[0] + r, hub[1] + r), fill=rgba(color, alpha)) + draw.ellipse( + (hub[0] - 30 * scale, hub[1] - 30 * scale, hub[0] + 30 * scale, hub[1] + 30 * scale), + outline=(255, 255, 255, 220), + width=max(1, int(4 * scale)), + ) + + base.alpha_composite(overlay) + + +def render(scale: float = 1.0, phase: float = 0.0, animated: bool = False) -> Image.Image: + width = int(round(CANVAS[0] * scale)) + height = int(round(CANVAS[1] * scale)) + base = Image.open(BACKGROUND).convert("RGBA") + if scale != 1.0: + base = base.resize((width, height), Image.Resampling.LANCZOS) + + # This is a soft wash, not an icon container; it hides model noise under the future app icon. + focus = radial_layer( + (int(1320 * scale), int(1320 * scale)), + (210, 255, 255, 120), + (210, 255, 255, 0), + ).filter(ImageFilter.GaussianBlur(max(1, int(30 * scale)))) + paste_center(base, focus, (ICON_CENTER[0] * scale, ICON_CENTER[1] * scale)) + + draw_ecosystem_overlay(base, scale, phase, animated) + draw_icon(base, scale, phase, animated) + draw_typography(base, scale) + return base.convert("RGB") + + +def save_animated() -> None: + webp_scale = 0.5 + gif_scale = 1 / 3 + frame_count = 24 + duration = 70 + + webp_frames = [render(webp_scale, i / frame_count, True) for i in range(frame_count)] + webp_frames[0].save( + OUT_WEBP, + save_all=True, + append_images=webp_frames[1:], + duration=duration, + loop=0, + quality=82, + method=4, + ) + + gif_frames = [frame.resize((1280, 720), Image.Resampling.LANCZOS) for frame in webp_frames] + palette = gif_frames[0].convert("P", palette=Image.Palette.ADAPTIVE, colors=256) + paletted = [palette] + [frame.quantize(palette=palette) for frame in gif_frames[1:]] + paletted[0].save( + OUT_GIF, + save_all=True, + append_images=paletted[1:], + duration=duration, + loop=0, + optimize=True, + disposal=2, + ) + + +def main() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + static = render(1.0, 0.0, False) + static.save(OUT_STATIC, quality=96) + save_animated() + + OUT_META.write_text( + "\n".join( + [ + "ClawScope README cover post-processing", + f"locale={LOCALE}", + f"background={BACKGROUND}", + f"icon={ICON}", + f"static={OUT_STATIC}", + f"animated_webp={OUT_WEBP}", + f"animated_gif={OUT_GIF}", + f"canvas={CANVAS[0]}x{CANVAS[1]}", + f"icon_display={ICON_SIZE}x{ICON_SIZE}", + f"icon_center={ICON_CENTER}", + "icon_policy=uniform upscale from icon-source.png, preserve silhouette/proportions/colors/transparency; no redraw or recolor", + "motion_language=flowing memory currents, pulsing knowledge nodes, semantic search ripples, OpenClaw ecosystem paths, evolution diff streams", + ] + ) + + "\n", + encoding="utf-8", + ) + + for path in (OUT_STATIC, OUT_WEBP, OUT_GIF, OUT_META): + print(path.relative_to(ROOT), path.stat().st_size) + + +if __name__ == "__main__": + main() diff --git a/tmp/imagegen/run_clawscope_cover_api.py b/tmp/imagegen/run_clawscope_cover_api.py new file mode 100644 index 0000000..a50147e --- /dev/null +++ b/tmp/imagegen/run_clawscope_cover_api.py @@ -0,0 +1,222 @@ +import base64 +import json +import os +import socket +import ssl +import time +from pathlib import Path +from urllib import error, request + + +API_KEY_NAME = "OPENAI-GPT-IMAGE-2-API-KEY" +API_URL = "https://ai-api-cn.db-kj.com/v1/images/generations" +ROOT = Path(__file__).resolve().parents[2] +RUN_DIR = ROOT / "tmp" / "imagegen" / "clawscope-cover-api-run" +PROMPT_PATH = ROOT / "tmp" / "imagegen" / "clawscope-cover-bg-prompt.txt" +ICON_PATH = ROOT / "icon-source.png" +OUT_PATH = ROOT / "public" / "images" / "covers" / "clawscope-cover-bg.png" + + +def write_json(name, data): + RUN_DIR.mkdir(parents=True, exist_ok=True) + path = RUN_DIR / name + path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") + return path + + +def now_ms(): + return int(time.time() * 1000) + + +def read_api_key(): + value = os.environ.get(API_KEY_NAME) + if value: + return value + if os.name == "nt": + # Environment variables with hyphens are often configured at User scope + # but not inherited by already-running shells. + import subprocess + + cmd = [ + "powershell", + "-NoProfile", + "-Command", + f"[Environment]::GetEnvironmentVariable('{API_KEY_NAME}', 'User')", + ] + result = subprocess.run(cmd, check=False, capture_output=True, text=True) + value = result.stdout.strip() + if value: + return value + return None + + +def main(): + started = now_ms() + api_key = read_api_key() + if not api_key: + write_json( + "error.json", + { + "stage": "read_api_key", + "message": f"{API_KEY_NAME} is missing", + "elapsed_ms": now_ms() - started, + }, + ) + raise SystemExit(2) + + prompt = PROMPT_PATH.read_text(encoding="utf-8") + image_bytes = ICON_PATH.read_bytes() + image_data_url = "data:image/png;base64," + base64.b64encode(image_bytes).decode("ascii") + payload = { + "model": "gpt-image-2", + "prompt": prompt, + "size": "3840x2160", + "quality": "high", + "n": 1, + "output_format": "png", + "images": [{"image_url": image_data_url}], + } + body = json.dumps(payload, ensure_ascii=False).encode("utf-8") + + write_json( + "request-started.json", + { + "stage": "request_started", + "url": API_URL, + "method": "POST", + "model": payload["model"], + "size": payload["size"], + "quality": payload["quality"], + "output_format": payload["output_format"], + "n": payload["n"], + "has_api_key": True, + "input_image": str(ICON_PATH), + "input_image_bytes": len(image_bytes), + "prompt_path": str(PROMPT_PATH), + "prompt_chars": len(prompt), + "body_bytes": len(body), + "started_ms": started, + }, + ) + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + "Accept": "application/json", + } + req = request.Request(API_URL, data=body, headers=headers, method="POST") + request_started = time.time() + + try: + with request.urlopen(req, timeout=1200) as resp: + raw = resp.read() + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "response-status.json", + { + "stage": "response_received", + "http_status": resp.status, + "headers": dict(resp.headers.items()), + "response_bytes": len(raw), + "elapsed_ms": elapsed_ms, + }, + ) + except error.HTTPError as exc: + raw = exc.read() + decoded = raw.decode("utf-8", errors="replace") + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "response-status.json", + { + "stage": "http_error", + "http_status": exc.code, + "headers": dict(exc.headers.items()) if exc.headers else {}, + "response_bytes": len(raw), + "elapsed_ms": elapsed_ms, + }, + ) + write_json( + "error.json", + { + "stage": "http_error", + "http_status": exc.code, + "body": decoded, + "elapsed_ms": elapsed_ms, + }, + ) + raise SystemExit(3) + except (TimeoutError, socket.timeout, ssl.SSLError, OSError) as exc: + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "error.json", + { + "stage": "transport_error", + "error_type": type(exc).__name__, + "message": str(exc), + "elapsed_ms": elapsed_ms, + }, + ) + raise SystemExit(4) + + decoded = raw.decode("utf-8", errors="replace") + try: + result = json.loads(decoded) + except json.JSONDecodeError: + (RUN_DIR / "response.raw").write_bytes(raw) + write_json( + "error.json", + { + "stage": "parse_response_json", + "message": "Response is not valid JSON", + "response_preview": decoded[:2000], + }, + ) + raise SystemExit(5) + + image_b64 = None + image_url = None + data = result.get("data") if isinstance(result, dict) else None + if isinstance(data, list) and data: + first = data[0] + if isinstance(first, dict): + image_b64 = first.get("b64_json") or first.get("image") or first.get("base64") + image_url = first.get("url") + if isinstance(result, dict): + image_b64 = image_b64 or result.get("b64_json") or result.get("image") or result.get("base64") + image_url = image_url or result.get("url") + + OUT_PATH.parent.mkdir(parents=True, exist_ok=True) + if image_b64: + if image_b64.startswith("data:"): + image_b64 = image_b64.split(",", 1)[1] + OUT_PATH.write_bytes(base64.b64decode(image_b64)) + elif image_url: + img_req = request.Request(image_url, headers={"User-Agent": "clawscope-imagegen"}) + with request.urlopen(img_req, timeout=300) as img_resp: + OUT_PATH.write_bytes(img_resp.read()) + else: + write_json("response.json", result) + write_json( + "error.json", + { + "stage": "extract_image", + "message": "No image field found in successful response", + "response_path": str(RUN_DIR / "response.json"), + }, + ) + raise SystemExit(6) + + write_json( + "output.json", + { + "stage": "output_saved", + "output": str(OUT_PATH), + "output_bytes": OUT_PATH.stat().st_size, + "total_elapsed_ms": now_ms() - started, + }, + ) + print(str(OUT_PATH)) + + +if __name__ == "__main__": + main() diff --git a/tmp/imagegen/run_clawscope_cover_v2_api.py b/tmp/imagegen/run_clawscope_cover_v2_api.py new file mode 100644 index 0000000..879c8ea --- /dev/null +++ b/tmp/imagegen/run_clawscope_cover_v2_api.py @@ -0,0 +1,220 @@ +import base64 +import json +import os +import socket +import ssl +import time +from pathlib import Path +from urllib import error, request + + +API_KEY_NAME = "OPENAI-GPT-IMAGE-2-API-KEY" +API_URL = "https://ai-api-cn.db-kj.com/v1/images/generations" +ROOT = Path(__file__).resolve().parents[2] +RUN_DIR = ROOT / "tmp" / "imagegen" / "clawscope-cover-v2-api-run" +PROMPT_PATH = ROOT / "tmp" / "imagegen" / "clawscope-cover-bg-v2-prompt.txt" +ICON_PATH = ROOT / "icon-source.png" +OUT_PATH = ROOT / "public" / "images" / "covers" / "clawscope-cover-bg-v2.png" + + +def write_json(name, data): + RUN_DIR.mkdir(parents=True, exist_ok=True) + path = RUN_DIR / name + path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") + return path + + +def now_ms(): + return int(time.time() * 1000) + + +def read_api_key(): + value = os.environ.get(API_KEY_NAME) + if value: + return value + if os.name == "nt": + import subprocess + + cmd = [ + "powershell", + "-NoProfile", + "-Command", + f"[Environment]::GetEnvironmentVariable('{API_KEY_NAME}', 'User')", + ] + result = subprocess.run(cmd, check=False, capture_output=True, text=True) + value = result.stdout.strip() + if value: + return value + return None + + +def main(): + started = now_ms() + api_key = read_api_key() + if not api_key: + write_json( + "error.json", + { + "stage": "read_api_key", + "message": f"{API_KEY_NAME} is missing", + "elapsed_ms": now_ms() - started, + }, + ) + raise SystemExit(2) + + prompt = PROMPT_PATH.read_text(encoding="utf-8") + image_bytes = ICON_PATH.read_bytes() + image_data_url = "data:image/png;base64," + base64.b64encode(image_bytes).decode("ascii") + payload = { + "model": "gpt-image-2", + "prompt": prompt, + "size": "3840x2160", + "quality": "high", + "n": 1, + "output_format": "png", + "images": [{"image_url": image_data_url}], + } + body = json.dumps(payload, ensure_ascii=False).encode("utf-8") + + write_json( + "request-started.json", + { + "stage": "request_started", + "url": API_URL, + "method": "POST", + "model": payload["model"], + "size": payload["size"], + "quality": payload["quality"], + "output_format": payload["output_format"], + "n": payload["n"], + "has_api_key": True, + "input_image": str(ICON_PATH), + "input_image_bytes": len(image_bytes), + "prompt_path": str(PROMPT_PATH), + "prompt_chars": len(prompt), + "body_bytes": len(body), + "started_ms": started, + }, + ) + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + "Accept": "application/json", + } + req = request.Request(API_URL, data=body, headers=headers, method="POST") + request_started = time.time() + + try: + with request.urlopen(req, timeout=1200) as resp: + raw = resp.read() + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "response-status.json", + { + "stage": "response_received", + "http_status": resp.status, + "headers": dict(resp.headers.items()), + "response_bytes": len(raw), + "elapsed_ms": elapsed_ms, + }, + ) + except error.HTTPError as exc: + raw = exc.read() + decoded = raw.decode("utf-8", errors="replace") + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "response-status.json", + { + "stage": "http_error", + "http_status": exc.code, + "headers": dict(exc.headers.items()) if exc.headers else {}, + "response_bytes": len(raw), + "elapsed_ms": elapsed_ms, + }, + ) + write_json( + "error.json", + { + "stage": "http_error", + "http_status": exc.code, + "body": decoded, + "elapsed_ms": elapsed_ms, + }, + ) + raise SystemExit(3) + except (TimeoutError, socket.timeout, ssl.SSLError, OSError) as exc: + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "error.json", + { + "stage": "transport_error", + "error_type": type(exc).__name__, + "message": str(exc), + "elapsed_ms": elapsed_ms, + }, + ) + raise SystemExit(4) + + decoded = raw.decode("utf-8", errors="replace") + try: + result = json.loads(decoded) + except json.JSONDecodeError: + (RUN_DIR / "response.raw").write_bytes(raw) + write_json( + "error.json", + { + "stage": "parse_response_json", + "message": "Response is not valid JSON", + "response_preview": decoded[:2000], + }, + ) + raise SystemExit(5) + + image_b64 = None + image_url = None + data = result.get("data") if isinstance(result, dict) else None + if isinstance(data, list) and data: + first = data[0] + if isinstance(first, dict): + image_b64 = first.get("b64_json") or first.get("image") or first.get("base64") + image_url = first.get("url") + if isinstance(result, dict): + image_b64 = image_b64 or result.get("b64_json") or result.get("image") or result.get("base64") + image_url = image_url or result.get("url") + + OUT_PATH.parent.mkdir(parents=True, exist_ok=True) + if image_b64: + if image_b64.startswith("data:"): + image_b64 = image_b64.split(",", 1)[1] + OUT_PATH.write_bytes(base64.b64decode(image_b64)) + elif image_url: + img_req = request.Request(image_url, headers={"User-Agent": "clawscope-imagegen"}) + with request.urlopen(img_req, timeout=300) as img_resp: + OUT_PATH.write_bytes(img_resp.read()) + else: + write_json("response.json", result) + write_json( + "error.json", + { + "stage": "extract_image", + "message": "No image field found in successful response", + "response_path": str(RUN_DIR / "response.json"), + }, + ) + raise SystemExit(6) + + write_json( + "output.json", + { + "stage": "output_saved", + "output": str(OUT_PATH), + "output_bytes": OUT_PATH.stat().st_size, + "total_elapsed_ms": now_ms() - started, + }, + ) + print(str(OUT_PATH)) + + +if __name__ == "__main__": + main() diff --git a/tmp/imagegen/run_clawscope_cover_v3_api.py b/tmp/imagegen/run_clawscope_cover_v3_api.py new file mode 100644 index 0000000..6717cbe --- /dev/null +++ b/tmp/imagegen/run_clawscope_cover_v3_api.py @@ -0,0 +1,220 @@ +import base64 +import json +import os +import socket +import ssl +import time +from pathlib import Path +from urllib import error, request + + +API_KEY_NAME = "OPENAI-GPT-IMAGE-2-API-KEY" +API_URL = "https://ai-api-cn.db-kj.com/v1/images/generations" +ROOT = Path(__file__).resolve().parents[2] +RUN_DIR = ROOT / "tmp" / "imagegen" / "clawscope-cover-v3-api-run" +PROMPT_PATH = ROOT / "tmp" / "imagegen" / "clawscope-cover-bg-v3-prompt.txt" +ICON_PATH = ROOT / "icon-source.png" +OUT_PATH = ROOT / "public" / "images" / "covers" / "clawscope-cover-bg-v3.png" + + +def write_json(name, data): + RUN_DIR.mkdir(parents=True, exist_ok=True) + path = RUN_DIR / name + path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") + return path + + +def now_ms(): + return int(time.time() * 1000) + + +def read_api_key(): + value = os.environ.get(API_KEY_NAME) + if value: + return value + if os.name == "nt": + import subprocess + + cmd = [ + "powershell", + "-NoProfile", + "-Command", + f"[Environment]::GetEnvironmentVariable('{API_KEY_NAME}', 'User')", + ] + result = subprocess.run(cmd, check=False, capture_output=True, text=True) + value = result.stdout.strip() + if value: + return value + return None + + +def main(): + started = now_ms() + api_key = read_api_key() + if not api_key: + write_json( + "error.json", + { + "stage": "read_api_key", + "message": f"{API_KEY_NAME} is missing", + "elapsed_ms": now_ms() - started, + }, + ) + raise SystemExit(2) + + prompt = PROMPT_PATH.read_text(encoding="utf-8") + image_bytes = ICON_PATH.read_bytes() + image_data_url = "data:image/png;base64," + base64.b64encode(image_bytes).decode("ascii") + payload = { + "model": "gpt-image-2", + "prompt": prompt, + "size": "3840x2160", + "quality": "high", + "n": 1, + "output_format": "png", + "images": [{"image_url": image_data_url}], + } + body = json.dumps(payload, ensure_ascii=False).encode("utf-8") + + write_json( + "request-started.json", + { + "stage": "request_started", + "url": API_URL, + "method": "POST", + "model": payload["model"], + "size": payload["size"], + "quality": payload["quality"], + "output_format": payload["output_format"], + "n": payload["n"], + "has_api_key": True, + "input_image": str(ICON_PATH), + "input_image_bytes": len(image_bytes), + "prompt_path": str(PROMPT_PATH), + "prompt_chars": len(prompt), + "body_bytes": len(body), + "started_ms": started, + }, + ) + + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + "Accept": "application/json", + } + req = request.Request(API_URL, data=body, headers=headers, method="POST") + request_started = time.time() + + try: + with request.urlopen(req, timeout=1200) as resp: + raw = resp.read() + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "response-status.json", + { + "stage": "response_received", + "http_status": resp.status, + "headers": dict(resp.headers.items()), + "response_bytes": len(raw), + "elapsed_ms": elapsed_ms, + }, + ) + except error.HTTPError as exc: + raw = exc.read() + decoded = raw.decode("utf-8", errors="replace") + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "response-status.json", + { + "stage": "http_error", + "http_status": exc.code, + "headers": dict(exc.headers.items()) if exc.headers else {}, + "response_bytes": len(raw), + "elapsed_ms": elapsed_ms, + }, + ) + write_json( + "error.json", + { + "stage": "http_error", + "http_status": exc.code, + "body": decoded, + "elapsed_ms": elapsed_ms, + }, + ) + raise SystemExit(3) + except (TimeoutError, socket.timeout, ssl.SSLError, OSError) as exc: + elapsed_ms = int((time.time() - request_started) * 1000) + write_json( + "error.json", + { + "stage": "transport_error", + "error_type": type(exc).__name__, + "message": str(exc), + "elapsed_ms": elapsed_ms, + }, + ) + raise SystemExit(4) + + decoded = raw.decode("utf-8", errors="replace") + try: + result = json.loads(decoded) + except json.JSONDecodeError: + (RUN_DIR / "response.raw").write_bytes(raw) + write_json( + "error.json", + { + "stage": "parse_response_json", + "message": "Response is not valid JSON", + "response_preview": decoded[:2000], + }, + ) + raise SystemExit(5) + + image_b64 = None + image_url = None + data = result.get("data") if isinstance(result, dict) else None + if isinstance(data, list) and data: + first = data[0] + if isinstance(first, dict): + image_b64 = first.get("b64_json") or first.get("image") or first.get("base64") + image_url = first.get("url") + if isinstance(result, dict): + image_b64 = image_b64 or result.get("b64_json") or result.get("image") or result.get("base64") + image_url = image_url or result.get("url") + + OUT_PATH.parent.mkdir(parents=True, exist_ok=True) + if image_b64: + if image_b64.startswith("data:"): + image_b64 = image_b64.split(",", 1)[1] + OUT_PATH.write_bytes(base64.b64decode(image_b64)) + elif image_url: + img_req = request.Request(image_url, headers={"User-Agent": "clawscope-imagegen"}) + with request.urlopen(img_req, timeout=300) as img_resp: + OUT_PATH.write_bytes(img_resp.read()) + else: + write_json("response.json", result) + write_json( + "error.json", + { + "stage": "extract_image", + "message": "No image field found in successful response", + "response_path": str(RUN_DIR / "response.json"), + }, + ) + raise SystemExit(6) + + write_json( + "output.json", + { + "stage": "output_saved", + "output": str(OUT_PATH), + "output_bytes": OUT_PATH.stat().st_size, + "total_elapsed_ms": now_ms() - started, + }, + ) + print(str(OUT_PATH)) + + +if __name__ == "__main__": + main() diff --git a/tmp/installed-exe-icon-preview.png b/tmp/installed-exe-icon-preview.png new file mode 100644 index 0000000..096edce Binary files /dev/null and b/tmp/installed-exe-icon-preview.png differ diff --git a/tmp/masked-ios20-preview.png b/tmp/masked-ios20-preview.png new file mode 100644 index 0000000..dd88e2d Binary files /dev/null and b/tmp/masked-ios20-preview.png differ diff --git a/tmp/masked-square150-preview.png b/tmp/masked-square150-preview.png new file mode 100644 index 0000000..d0f502e Binary files /dev/null and b/tmp/masked-square150-preview.png differ diff --git a/tmp/ralph-cli-smoke/prd.tasks.json b/tmp/ralph-cli-smoke/prd.tasks.json new file mode 100644 index 0000000..0d43aaf --- /dev/null +++ b/tmp/ralph-cli-smoke/prd.tasks.json @@ -0,0 +1,60 @@ +{ + "schemaVersion": "ralph_prd_v2", + "branchName": "release/v0.1.3", + "taskDescription": "Execute speckit implement tasks from tasks", + "projectContext": {}, + "userStories": [ + { + "id": "US-001", + "title": "T001 Implement smoke runtime flow in src/app.ts", + "description": "T001 Implement smoke runtime flow in src/app.ts", + "acceptanceCriteria": [ + "T001 Implement smoke runtime flow in src/app.ts" + ], + "priority": 1, + "passes": true, + "notes": "Source task checkbox: [ ]", + "involvesProductionCode": true, + "tddSteps": [ + { + "phase": "TDD-RED", + "passes": true, + "note": "T001 vitest smoke/runtime.test.ts => 1 failed", + "timestamp": "2026-04-20T01:20:00+08:00" + }, + { + "phase": "TDD-GREEN", + "passes": true, + "note": "T001 vitest smoke/runtime.test.ts => 1 passed", + "timestamp": "2026-04-20T01:21:00+08:00" + }, + { + "phase": "TDD-REFACTOR", + "passes": true, + "note": "T001 no refactor needed", + "timestamp": "2026-04-20T01:22:00+08:00" + } + ] + }, + { + "id": "US-002", + "title": "T002 Update README copy", + "description": "T002 Update README copy", + "acceptanceCriteria": [ + "T002 Update README copy" + ], + "priority": 2, + "passes": true, + "notes": "Source task checkbox: [x]", + "involvesProductionCode": false, + "tddSteps": [ + { + "phase": "DONE", + "passes": true, + "note": "T002 docs updated", + "timestamp": "2026-04-20T01:23:00+08:00" + } + ] + } + ] +} diff --git a/tmp/ralph-cli-smoke/progress.tasks.txt b/tmp/ralph-cli-smoke/progress.tasks.txt new file mode 100644 index 0000000..b43df6f --- /dev/null +++ b/tmp/ralph-cli-smoke/progress.tasks.txt @@ -0,0 +1,48 @@ +# Progress: Execute speckit implement tasks from tasks +# Total stories: 2 + +Current story: 2 +Completed: 2 + +--- +# Story log + +[2026-04-20 01:20] US-001: T001 Implement smoke runtime flow in src/app.ts - PASSED + +[2026-04-20 01:23] US-002: T002 Update README copy - PASSED + +## US-001: T001 Implement smoke runtime flow in src/app.ts +Status: PASSED +[TDD-RED] T001 vitest smoke/runtime.test.ts => 1 failed +[TDD-GREEN] T001 vitest smoke/runtime.test.ts => 1 passed +[TDD-REFACTOR] T001 no refactor needed + +## US-002: T002 Update README copy +Status: PASSED +[DONE] T002 docs updated + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tmp/ralph-cli-smoke/tasks.md b/tmp/ralph-cli-smoke/tasks.md new file mode 100644 index 0000000..c614e1f --- /dev/null +++ b/tmp/ralph-cli-smoke/tasks.md @@ -0,0 +1,4 @@ +# Tasks + +- [ ] T001 Implement smoke runtime flow in src/app.ts +- [x] T002 Update README copy diff --git a/tmp/release-check/ClawScope_0.1.1_x64-setup.exe b/tmp/release-check/ClawScope_0.1.1_x64-setup.exe new file mode 100644 index 0000000..077a815 Binary files /dev/null and b/tmp/release-check/ClawScope_0.1.1_x64-setup.exe differ diff --git a/tmp/release-check/ClawScope_0.1.1_x64_en-US.msi b/tmp/release-check/ClawScope_0.1.1_x64_en-US.msi new file mode 100644 index 0000000..244fcd6 Binary files /dev/null and b/tmp/release-check/ClawScope_0.1.1_x64_en-US.msi differ