v1.0.10 两件大事:
1. 界面中英双语(英文界面无中文残留)
- 新增 web/i18n.js(约 4200 条词条),零依赖零构建
- 后端下发中文数据走 bt4() 查表翻译;独立模块(upstream/netdetail/
realtime/update)纳入翻译并注册切语言重绘钩子
- 后端「状态/聚合」接口只回稳定枚举+原始数值,不下发中文文案
- 顺带修一个真实 XSS:顶层 const 不挂 window,独立脚本 esc 静默退化成零转义
2. 修掉「文字竖着排/错位」(多分辨率反复暴露)
- 单元格 min-content 只有 1 字宽 → word-break:keep-all + 表头 nowrap
- 依赖卡片正文被标签/按钮挤成 27px → .dep-body{flex:1 1 200px} + flex-wrap
- 裸 flex:1(下限为0)给正文 → 一律改 flex:1 1 <px>
- .dep-item 横向容器直接塞块级子项 → 退回块级
- 关键陷阱:auto-fill 布局屏越宽列数越多单卡越窄,窄屏反而测不出来,
真机审计推进到「逐视图点按钮 + 1920/2560 宽视口 + 中英双语」
- 守卫 t-layout.py 扩到 35 条,12 处反向验证全红
3. 修真缺陷:_link_since() 重启检测被误改成 if False:(注释还写着「已修」),
uptime 归零后相减得负数 → 恢复比对 boot_id
附带:新增 25 个检查器(t-*.py)、排版审计工具链(shot-*/ocr-*/live-*)、
版本号兜底值同步到 1.0.10、.gitignore 收纳 i18n/排版排查的一次性脚本。
87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""从截图里**机器定位**「竖排文字」。
|
|
|
|
不需要 OCR:横排文字的每个字只占 ~10-16px 高,所以「某个窄列上出现
|
|
>= 45px 的连续墨迹」基本只有两种可能 —— 竖排文字,或垂直分隔线/图标。
|
|
按墨迹宽度把后者滤掉,剩下的就是竖排文字的位置。
|
|
|
|
用法:python _dev/shot-scan.py <图片> [图片2 ...]
|
|
"""
|
|
import sys
|
|
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
Image.MAX_IMAGE_PIXELS = None
|
|
|
|
RUN_MIN = 45 # 连续墨迹最短高度
|
|
BAND_W = 18 # 探测列宽
|
|
INK_MAX = 150 # 灰度阈值(小于它算墨迹)
|
|
|
|
|
|
def scan(path):
|
|
im = Image.open(path).convert('L')
|
|
a = np.asarray(im)
|
|
h, w = a.shape
|
|
print('=== %s %dx%d' % (path.split('\\')[-1], w, h))
|
|
ink = a < INK_MAX
|
|
half = BAND_W // 2
|
|
hits = []
|
|
xs = range(half, w - half, 3)
|
|
for x in xs:
|
|
band = ink[:, x - half:x + half]
|
|
rows = band.any(axis=1)
|
|
# 找连续 True 段
|
|
idx = np.flatnonzero(rows)
|
|
if idx.size == 0:
|
|
continue
|
|
splits = np.flatnonzero(np.diff(idx) > 1)
|
|
starts = np.r_[0, splits + 1]
|
|
ends = np.r_[splits, idx.size - 1]
|
|
for s, e in zip(starts, ends):
|
|
y0, y1 = int(idx[s]), int(idx[e])
|
|
if y1 - y0 + 1 < RUN_MIN:
|
|
continue
|
|
seg = ink[y0:y1 + 1, x - half:x + half]
|
|
colsum = seg.sum(axis=0)
|
|
inked_cols = np.flatnonzero(colsum > 0)
|
|
if inked_cols.size == 0:
|
|
continue
|
|
real_w = int(inked_cols[-1] - inked_cols[0] + 1)
|
|
density = seg.sum() / float(seg.size)
|
|
hits.append((x, y0, y1, real_w, density))
|
|
if not hits:
|
|
print(' 未发现竖排候选')
|
|
return
|
|
# 合并相邻 x 的同一段(同一块竖排会被多列重复命中)
|
|
hits.sort()
|
|
merged = []
|
|
for x, y0, y1, rw, d in hits:
|
|
for m in merged:
|
|
if abs(m['x1'] - x) <= BAND_W and not (y1 < m['y0'] - 20 or y0 > m['y1'] + 20):
|
|
m['x1'] = max(m['x1'], x)
|
|
m['y0'] = min(m['y0'], y0)
|
|
m['y1'] = max(m['y1'], y1)
|
|
m['rw'] = max(m['rw'], rw)
|
|
m['n'] += 1
|
|
break
|
|
else:
|
|
merged.append({'x0': x, 'x1': x, 'y0': y0, 'y1': y1, 'rw': rw,
|
|
'd': d, 'n': 1})
|
|
print(' 竖排候选 %d 处:' % len(merged))
|
|
for m in merged[:20]:
|
|
bw = m['x1'] - m['x0'] + BAND_W
|
|
bh = m['y1'] - m['y0'] + 1
|
|
ratio = bh / float(max(bw, 1))
|
|
print(' x=%d..%d y=%d..%d 墨迹宽≈%d 高=%d 高/宽=%.1f 列数=%d'
|
|
% (m['x0'], m['x1'], m['y0'], m['y1'], m['rw'], bh, ratio, m['n']))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
for p in sys.argv[1:]:
|
|
try:
|
|
scan(p)
|
|
except Exception as exc: # noqa: BLE001
|
|
print('=== %s 失败: %s' % (p, exc))
|