diff --git a/CHANGELOG.md b/CHANGELOG.md index 436f1c5..5574892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ## [Unreleased] +- style(branding): 重设计 logo/favicon 为「通透青绿圆环 + Tabler prompt `>_` 笔划」(方案 d05):全透明底 + 青绿渐变(`#22d3ee` → `#2dd4bf`)细圆环(stroke 0.5,仅原 1/3、通透若隐)+ 饱和青绿渐变 `>_`(stroke 2.5),极细环与饱和笔划形成权重对比、`>_` 为视觉主角,青绿承载终端/代码语义;基础笔划改用 Tabler prompt(chevron `M5 7l5 5l-5 5` + 下划线 `M13 17l6 0`,下划线长 6 单位、不会小尺寸点化);渐变改 `gradientUnits="userSpaceOnUse"` 绝对坐标,修复纯水平下划线(包围盒高度 0)在默认 objectBoundingBox 渐变下不着色而「消失」的问题;`_LOGO_DEFS` / `_PROMPT_PATHS` 单一事实源统一供 SVG favicon、ICO 回退(省略 0.5px 细环、保留饱和笔划)与页面 logo 消费;`.logo` 容器改全透明、SVG 内嵌圆环 + 青绿 drop-shadow 辉光(实机验证); + ## [v0.5.2a5](https://github.com/ThreeFish-AI/coding-proxy/releases/tag/v0.5.2a5) - 2026-07-04 - fix(dashboard): 供应商队列拖拽由原生 HTML5 DnD 改为 Pointer Events 实现,修复 v0.5.2a4 引入的拖拽排序功能在浏览器中无法拖动的问题;引入 4px 拖拽阈值、乐观实时重排与指针捕获机制,同步支持触屏设备;拖拽完成后顺序立即影响 cc 运行时供应商优先级调度(实机验证);为 `/dashboard` 响应增加 `Cache-Control: no-cache`,防止浏览器缓存旧内联脚本;补齐前端守卫测试断言 (#271); diff --git a/src/coding/proxy/server/dashboard.py b/src/coding/proxy/server/dashboard.py index 3168492..b40515d 100644 --- a/src/coding/proxy/server/dashboard.py +++ b/src/coding/proxy/server/dashboard.py @@ -11,86 +11,73 @@ from ..logging.db import TimePeriod -# ── 品牌图标:Tabler terminal-2 核心笔划(> 与 _),不含外框 ────────────── -# 容器为「淡色磨砂卡片」(#f5f6fb 浅底 + 品牌紫描边环),> 与 _ 笔划改用品牌渐变 -# (#667eea -> #764ba2)——色彩反转:品牌紫由背景迁移至图标笔划,轮廓成为视觉主角, -# 更简约、小尺寸下更清晰(Linear/Vercel 风)。保留 Tabler 原始外框笔划会双重描边、 -# 边缘发糊,故仅取 > 与 _ 两条。 -# 单一事实源:_TERMINAL2_PATHS 与 _LOGO_DEFS 同时供 favicon SVG(f-string)与页面 -# logo({{TERMINAL2}} / {{LOGO_DEFS}} 模板替换)消费。 -_BRAND_FROM, _BRAND_TO = "#667eea", "#764ba2" +# ── 品牌图标:Tabler prompt 核心笔划(> 与 _)+ 通透青绿圆环 ────────────── +# 设计(方案 d05):全透明底 + 青绿渐变细圆环(stroke 0.5,仅原 1/3,通透若隐)+ +# 饱和青绿渐变 >_ 笔划(stroke 2.5)。极细环与饱和笔划形成权重对比,>_ 为绝对主角; +# 青绿(#22d3ee -> #2dd4bf)承载终端/代码语义,通透明快。基础笔划取自 Tabler prompt +# (chevron M5 7l5 5l-5 5 + 下划线 M13 17l6 0,下划线长 6 单位、不会小尺寸点化)。 +# 渐变用 gradientUnits="userSpaceOnUse" + 绝对坐标:纯水平下划线包围盒高度为 0,若用 +# 默认 objectBoundingBox 渐变会映射失效而不着色(下划线消失),userSpaceOnUse 规避。 +# 单一事实源:_PROMPT_PATHS 与 _LOGO_DEFS 同时供 favicon SVG(f-string)与页面 +# logo({{PROMPT}} / {{LOGO_DEFS}} 模板替换)消费。 +_BRAND_FROM, _BRAND_TO = "#22d3ee", "#2dd4bf" _LOGO_DEFS = ( - '' + '' f'' f'' "" ) -_TERMINAL2_PATHS = '' +_PROMPT_PATHS = '' # ── Favicon (SVG, 现代浏览器主选) ────────────────────────────────────────── def _build_favicon_svg() -> str: - """生成 24×24 SVG favicon:淡色磨砂卡片 + 品牌渐变 terminal-2 笔划. + """生成 24×24 SVG favicon:通透青绿圆环 + 品牌渐变 prompt 笔划(方案 d05). - 容器为 ``#f5f6fb`` 浅底圆角方块 + 品牌紫描边环;``>`` 与 ``_`` 笔划由白色改为品牌 - 渐变(``url(#cpBrand)``),使图标轮廓成为主角。独立 SVG 文档无 CSS 上下文, - ``currentColor`` 不可靠,故颜色硬编码;```` 复用 ``_LOGO_DEFS`` 单一事实源。 + 全透明底 + 青绿渐变细圆环(``r=11``、``stroke-width=0.5``、``stroke-opacity=.55``, + 通透若隐)+ 饱和青绿渐变 ``>`` 与 ``_`` 笔划(``stroke-width=2.5``)。极细环与饱和 + 笔划形成权重对比,``>_`` 为视觉主角。独立 SVG 文档无 CSS 上下文,``currentColor`` + 不可靠,故渐变复用 ``_LOGO_DEFS``(userSpaceOnUse,规避水平下划线不着色)单一事实源。 """ return ( '' f"{_LOGO_DEFS}" - '' - f'{_TERMINAL2_PATHS}' + '' + f'{_PROMPT_PATHS}' "" ) # ── Favicon (ICO, 32×32 光栅回退) ───────────────────────────────────────── def _build_favicon() -> bytes: - """程序化生成 32×32 ICO:淡色磨砂卡片 + 品牌渐变 terminal-2 笔划(旧浏览器/Safari 回退). + """程序化生成 32×32 ICO:全透明底 + 青绿渐变 prompt 笔划(旧浏览器/Safari 回退). - 项目无 Pillow/cairosvg 等图像库,故以纯 Python 按像素光栅化:``#f5f6fb`` 浅底圆角 - 方块 + 品牌渐变(#667eea -> #764ba2)``>`` 折线与 ``_`` 下划线。沿用原实现「全零 - AND-mask + per-pixel alpha」的圆角透明方案(现代渲染器按 alpha 通道处理透明)。 - 32px 下细描边环易锯齿,故 ICO 仅保留浅底 + 渐变笔划,精修外观由 SVG 承载。 + 项目无 Pillow/cairosvg 等图像库,故以纯 Python 按像素光栅化:全透明底 + 青绿渐变 + (#22d3ee -> #2dd4bf)``>`` 折线与 ``_`` 下划线。沿用「全零 AND-mask + per-pixel + alpha」透明方案(现代渲染器按 alpha 通道处理透明)。方案 d05 的细圆环(0.5px)在 + 32px 下不足 1 像素、无法渲染,故 ICO 省略圆环、仅保留饱和 ``>_`` 笔划(符合「图标 + 为主角、环极弱」的设计意图),完整外观由 SVG 主版承载。 """ import math import struct width = height = 32 - radius = 8 scale = width / 24.0 # SVG 24 单位 -> 像素 - # 图标笔划品牌渐变:#667eea -> #764ba2(与 _LOGO_DEFS 同源) - r0, g0, b0 = 0x66, 0x7E, 0xEA - r1, g1, b1 = 0x76, 0x4B, 0xA2 - # 容器底色:#f5f6fb(近白、极浅冷蓝紫) - tile_r, tile_g, tile_b = 0xF5, 0xF6, 0xFB - - def inside(x: int, y: int) -> bool: - """满铺圆角方块(四角半径 radius)包含判定.""" - cx = min(x, width - 1 - x) - cy = min(y, height - 1 - y) - if cx >= radius or cy >= radius: - return True - return (radius - cx) ** 2 + (radius - cy) ** 2 <= radius * radius - - # 像素缓冲:[B, G, R, A],默认全透明 + # 图标笔划青绿渐变:#22d3ee -> #2dd4bf(与 _LOGO_DEFS 同源) + r0, g0, b0 = 0x22, 0xD3, 0xEE + r1, g1, b1 = 0x2D, 0xD4, 0xBF + + # 像素缓冲:[B, G, R, A],默认全透明(全透底,无容器填充) buf: list[list[list[int]]] = [ [[0, 0, 0, 0] for _ in range(width)] for _ in range(height) ] - # 1) 浅底圆角背景(扁平 #f5f6fb) - for y in range(height): - for x in range(width): - if not inside(x, y): - continue - buf[y][x] = [tile_b, tile_g, tile_r, 255] - def brand_at(px: float, py: float) -> tuple[int, int, int]: - """按对角参数 t 返回 (px,py) 处的品牌渐变 RGB(与 SVG url(#cpBrand) 同向).""" + """按对角参数 t 返回 (px,py) 处的青绿渐变 RGB(与 SVG url(#cpBrand) 同向).""" t = (px + py) / (width + height - 2) return ( int(r0 + (r1 - r0) * t), @@ -99,7 +86,7 @@ def brand_at(px: float, py: float) -> tuple[int, int, int]: ) def stamp(px: float, py: float) -> None: - """以 (px, py) 为中心盖 ~3px 品牌渐变方块(笔划加粗,保证小尺寸可辨识).""" + """以 (px, py) 为中心盖 ~3px 青绿渐变方块(笔划加粗,保证小尺寸可辨识).""" ix, iy = int(round(px)), int(round(py)) cr, cg, cb = brand_at(px, py) for oy in (-1, 0, 1): @@ -109,18 +96,18 @@ def stamp(px: float, py: float) -> None: buf[ny][nx] = [cb, cg, cr, 255] def line(x0: float, y0: float, x1: float, y1: float) -> None: - """稠密采样 + stamp 形成品牌渐变粗线段.""" + """稠密采样 + stamp 形成青绿渐变粗线段.""" dist = math.hypot(x1 - x0, y1 - y0) steps = max(1, int(dist * 3)) for i in range(steps + 1): s = i / steps stamp(x0 + (x1 - x0) * s, y0 + (y1 - y0) * s) - # 2) terminal-2 笔划(SVG 24 单位 -> 像素) - # > 折线 (8,9)->(11,12)->(8,15);_ 下划线 (13,15)->(16,15) - line(8 * scale, 9 * scale, 11 * scale, 12 * scale) - line(11 * scale, 12 * scale, 8 * scale, 15 * scale) - line(13 * scale, 15 * scale, 16 * scale, 15 * scale) + # prompt 笔划(SVG 24 单位 -> 像素) + # > 折线 (5,7)->(10,12)->(5,17);_ 下划线 (13,17)->(19,17) + line(5 * scale, 7 * scale, 10 * scale, 12 * scale) + line(10 * scale, 12 * scale, 5 * scale, 17 * scale) + line(13 * scale, 17 * scale, 19 * scale, 17 * scale) # 3) 打包 ICO(BMP bottom-up;全零 AND-mask,信任 per-pixel alpha 实现圆角透明) pixel_rows: list[bytes] = [] @@ -222,13 +209,9 @@ def line(x0: float, y0: float, x1: float, y1: float) -> None: .header-left { display: flex; align-items: center; gap: 12px; } .logo { width: 32px; height: 32px; - background: #f5f6fb; - border: 1px solid rgba(102,126,234,.18); - border-radius: 10px; display: flex; align-items: center; justify-content: center; - box-shadow: 0 4px 12px rgba(102,126,234,.18); } - .logo svg { width: 20px; height: 20px; display: block; } + .logo svg { width: 28px; height: 28px; display: block; filter: drop-shadow(0 0 3px rgba(45,212,191,.3)); } h1 { font-size: 18px; font-weight: 600; color: var(--text-primary); letter-spacing: -.3px; } .header-right { display: flex; align-items: center; gap: 12px; } .badge { @@ -815,7 +798,7 @@ def line(x0: float, y0: float, x1: float, y1: float) -> None:
- +

Coding Proxy Dashboard

v-.-.-
@@ -2385,7 +2368,7 @@ def line(x0: float, y0: float, x1: float, y1: float) -> None: -""".replace("{{TERMINAL2}}", _TERMINAL2_PATHS).replace("{{LOGO_DEFS}}", _LOGO_DEFS) +""".replace("{{PROMPT}}", _PROMPT_PATHS).replace("{{LOGO_DEFS}}", _LOGO_DEFS) # ── 数据计算工具 ──────────────────────────────────────────────────────────