@devfellowship/ux-paths-capture (英语)
这个 生产商 半个 UX 地图: 它用源文件标记一个建筑和
线条,该线条将每个元素都写出,并按已提供的页面行走,然后写出
regions.json 那个 @devfellowship/ux-paths-spec 描述.
他们让一个设计师点击一个截图区域 得到准确的 文件——没有模式调用,也没有复制应用程序的寄存器 读者的一面.
npm install --save-dev @devfellowship/ux-paths-capture@^0.4.0
为什么这是包裹
两个应用以这种方式捕捉自己, 他们生活在不同的 GitHub 组织,所以任何一种都无法读取对方的来源。 副本 本来是 第三届 执行一份合同——读者一半 已经将第二个记录为债务.
两个制片人像两个读者一样漂移,漂移是看不见的: 在单击后解析出错误的文件时,两者都保持绿色。 npm 跨越 无法设置的边界.
通用屏幕react-native ——0.4.0 (中文(简体) )
一个屏幕从 View 编号 : Text 编号 : Pressable 联 合 国 react-native由
反应-内生-web,没有小写标记,所以主机-元素规则没有邮票.
从0.4.0起,其标记受从 react-native (或
react-native-web获得 dataSet={{ source: "<file>:<line>" }},哪个
反应-内生-网络制成相同 data-source="<file>:<line>" 属性。 没有
读者变化. ux-paths-assert-no-stamp 探测到 dataSet 表格形式a
捆绑也一样, 因为那里属性名称是在运行时构建的 .
QQ 邮票是建立时间和仅抓取
它永远不能生产。 有两个独立的原因:
- 体重. 每个主机元素一个属性,在每个屏幕的每个元素上. 对于最终用户来说,这是致命的有效载荷.
- 公开. 印章发布应用程序的内部文件布局 进入DOM,任何打开dev工具的人读取. 用于封闭源代码 应用程序是一个披露控制,而不是大小问题.
大门是 默认缺失的环境变量。这不是 捆绑模式: 抓取构建 实值 一个生产模式的建筑, 因为文物 船上的文物,减去印花 —— 要印章时, 模式是真实的 财务报告和审定财务报表 真实的 释放.
证明对文物的默认,而不是配置,用这个 包装船:
# the release build — expect ZERO
npx ux-paths-assert-no-stamp dist
# the capture build — expect MORE THAN ZERO
npx ux-paths-assert-no-stamp dist --expect-present
运行 两者 在中央情报局。 第二,是什么保持了第一个诚实: 一个grep for a 通常不免费通行证的字符串,如果 探测器坏了 垃圾箱也会在每次运行时自行测试自己的探测器,以及 拒绝空目录或缺少目录.
⚠️ 漏出是文件和线的属性 BOUND,没有字
data-source 出现的地方。 解释自己抓取的应用程序
管道将这个名称放入用户界面副本,副本被捆绑起来——
此检测器的第一个版本只计算了名称, 并且失败了
干净的建筑。 一个为事情而开枪的警卫是个工程师
学会了推翻, 和过度的披露控制 比没有.
三个条目
| 导入 | 需求 | 持有 |
|---|---|---|
@devfellowship/ux-paths-capture | 无 | 邮票转换器、组件命名器、区域建设器、文物探测器 |
…/vite | vite (可选对等者,仅类型) | 作为 Vite 插件的邮票 |
…/playwright | @playwright/test (可选对等者,仅类型) | 确定性浏览器, DOM 行走, 画布- 链接读取, 双写器 |
拆分让根可以被没有捆绑器和浏览器的东西导入 ——一个CI后卫,一个MCP工具,一个平庸的节点脚本.
盖章一个建筑
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import { sourceStamp } from "@devfellowship/ux-paths-capture/vite";
export default defineConfig({
plugins: [
react(),
// Returns `undefined` when the switch is off, and `undefined` is a valid
// member of `plugins` — so there is one gate, in one place.
sourceStamp({ root: __dirname }),
],
});
默认开关是 UX_PATHS_SOURCE_STAMP。一个已经拥有的应用程序
它自己的拼写保持它:
sourceStamp({ root: __dirname, envVar: "MY_APP_SOURCE_STAMP" });
共有之事是出出门,不是出弦. 重命名一个能起作用的开关 抓取已设定的工作将静默地禁用该工作 .
抓取屏幕
软件包拥有每套应用程序都一样的东西:
定型浏览器,DOM行走和几何. 故意这样做的
没有 自己的导航或结算策略——一个应用程序等待 data-testid, (中文(简体) ),
另一个拍摄,直到连续两个相片相同地散开,第三个相片接一
认证门先。 这些是应用程序的属性,而不是地图的属性.
边界是"应用特定",而不是"与定居有关的一切":
readCanvases 财务报告和审定财务报表 settleCanvases 在软件包中是因为空
<canvas> 是一个关于平台的事实, 各地读取相同。 他们
他们不拒绝。 见下文.
import { chromium } from "@playwright/test";
import {
CAPTURE_CONTEXT_OPTIONS,
CAPTURE_LAUNCH_ARGS,
applyFreezeStylesheet,
installDeterminism,
writeCapturePair,
} from "@devfellowship/ux-paths-capture/playwright";
const browser = await chromium.launch({ args: [...CAPTURE_LAUNCH_ARGS] });
const context = await browser.newContext(CAPTURE_CONTEXT_OPTIONS);
const page = await context.newPage();
await installDeterminism(page); // BEFORE the first navigation
await page.goto("/login");
await applyFreezeStylesheet(page); // AFTER every navigation
// …your own settle policy here…
const canvas = await settleCanvases(page); // null when the screen has no canvas
const png = await page.screenshot({
fullPage: false,
animations: "disabled",
caret: "hide",
scale: "css",
});
await writeCapturePair(page, {
outDir: "capture/login",
screen: "login",
root: process.cwd(), // where a stamped path is resolved from
png,
});
写着 screenshot.png 财务报告和审定财务报表 regions.json 并肩而来.
在另一个取景口捕捉—— VIEWPORT_PRESETS (0.3.0) (中文(简体) )
捕捉是屏幕的照片 在一个视图端口,直到0.3.0为止 视点是一个常数。 它现在是一个参数.
import {
VIEWPORT_PRESETS,
captureContextOptions,
captureDirName,
resolveViewportSpec,
screenshotMetaFor,
} from "@devfellowship/ux-paths-capture/playwright";
const { name, preset, custom } = resolveViewportSpec(process.env.VIEWPORT ?? "desktop");
const context = await browser.newContext(captureContextOptions(preset));
// …capture as above…
await writeCapturePair(page, { outDir: captureDirName("login", preset), /* … */ });
| 预设 | 大小 | 旗帜 |
|---|---|---|
desktop (默认) | 1280x800 (英语) | —— 说 |
mobile | 390x844 (英语) | isMobile, (中文(简体) ), hasTouch |
两者都是 deviceScaleFactor: 1。 。 。 .
captureContextOptions("desktop") 与 CAPTURE_CONTEXT_OPTIONS, (中文(简体) ),
和单位测试 声称它. CAPTURE_VIEWPORT 财务报告和审定财务报表 CAPTURE_CONTEXT_OPTIONS
停止出口并停止1280x800——任何消费者已经进口的东西都没有移动.
🚨 isMobile 这是重要的领域, 它在图像中是看不见的. A 类
390x844 语境 无 它是一个狭窄的桌面,而不是一个电话:
@media (hover: hover) 财务报告和审定财务报表 pointer: fine 分支仍然采用桌面路径,
因此,捕捉照片的布局 从来没有显示任何电话用户 - 它这样做
从决定性的角度来说,这是造成错误持久的原因。 这就是为什么
resolveViewportSpec("390x844") 返回时 移动预设包括旗帜,
而不是光着身子 A 类 WxH 匹配没有预设返回
custom: true 所以打电话的人可以大声说出来.
🚨 deviceScaleFactor 保留1,而不是3个. 剧作家自己的 iPhone 13 预设为
减贫战略 3. 采用这一标准将是一项新的确定性主张(此处没有衡量标准为:
3) 和一项新的区域预测索赔(scale: 'css' 共享一个坐标空间
与 getBoundingClientRect() 仅限DPR 1。 价格是移动图像
为 390x844 CSS 像素而不是 1170x2532 设备像素: 真正的移动
版式 在 1x 时, 区域地图仍然正确 .
相对所起之处. captureDirName 将默认视图端口写入
<screen_id>/ 和以前一样, 和所有其他的景点 到平地
<screen_id>@<WxH>/。内部的文件名称不变,而且 regions.json
记录真实的取景口, 所以一对是自我描述, 即使从它的
目录。 嵌入 <screen_id>/<WxH>/ 被否决了,它打破了每一个读者
将目录名称作为屏幕 id .
screenshotMetaFor(preset) 返回时 { platform, orientation, viewport }
3个换1个 screens[].screenshots[] 输入,所以一个被观测到的流的生产者
文档不硬性类型 web编号 :landscape 再来一次.
这两个文件是PAIR
regions.json 描述一个图像的几何形状, 在图像本身
像素空间 — CSS 像素, deviceScaleFactor: 1风景点 直肠
放置在不同的图像旁边比完全没有直角更糟糕.
查看端口不剪接 fullPage 具体原因:
getBoundingClientRect() 说取景口坐标和铬 调整大小
视图端口 拍出一整页的镜头 fixed编号 :sticky 要素
并可以挂载读取矩形时未挂载的内容.
像素稳定是一站式的承诺
下游所有东西都比较两个截图 问diff是否到达 零,所以冻结是合同的一部分。 不过 格莱弗光栅化属于 给机器: 不同的 FreeType 或字体配置使文本与 不同的反变相。 一个主机上的两个捕捉器是同字节;两个 不同主机上的捕获不承诺。 不同东道类的比较 必须在一个容器里运行 任何意义.
performance.now() - 故意地留下真实的- 冻结它拖住反应的
调度器和每个 requestAnimationFrame 循环,交易化妆品摇摆
一个绞刑.
🕒 new Date() 被冻结. Date.now() 在虚拟时钟上运行
已更改 0.2.0 (中文(简体) ),它改变每个消费者的行为.
在0.2.0之前,两者都被冻结。 这让每一次的测量回报
0, 静态禁用 lodash.debounce, (中文(简体) ), lodash.throttle 和任何东西
以减掉两个时钟读数来决定 Date.now() - last >= wait
实值 0 >= 1虚假的,永远的. kapsule (单位:千美元) react-force-graph-2d) 附表
它的文摘是这样的, 所以一个力图从来没有列出 一个空白的画布是
作为屏幕发布,有稳定的文摘和0退出代码.
现在:
| 时钟 | 行为 |
|---|---|
new Date() | 冰冻在时代,这就是DOM |
Date.now() | epoch + virtual elapsed,而虚拟过期的垫款只有在 setTimeout 编号 : setInterval 回叫起火,到计时器的时间 |
装有 setTimeout(fn, 300) 因此,当
它运行,在一个闲散的宿主上,一个满载的宿主上, 交易内容是:
Date.now()财务报告和审定财务报表new Date().getTime()不再认同,通过虚拟 时间已过.- 决定主义变成有条件的. 冰冻常数是相同的 运行,无论页面如何操作;虚拟时钟仅在同时运行 中,页面以相同的顺序排列相同的计时器。 字节稳定现在是 测量,不是这个文件的属性——用你自己的抓取.
- 一个生的
Date.now()屏幕上打印的值可以移动. 格式化日期 无法:来自new Date()。 。 。 .
installDeterminism(page, { clock: "frozen" }) 恢复旧行为
确切地说,对于一个需要两个钟表同意的捕捉, 并且没有它的任何东西
使措施过时.
QQ 静态页面不是被画过的页面
settleCanvases(page) 等待到每个 <canvas> 携带的InK——不止一个
在它自己的后缓冲器中具有明显的像素值——直到这道墨水停止了变化.
两个事实都从布告中读出 getImageData,不等待出来.
它之所以存在,是因为两个相同的 BLANK 框架的散去相同,所以是一个空的图案
在第一次尝试中通过射击到稳定检查 它回来了 null 编号
没有画布的屏幕,以及 { painted: false, reason } 当预算用完时.
它报告;它不拒绝. writeCapturePair 从不咨询它。 一个
空画布是一个应用程序的缺陷和一个空白的正确第一帧
画出另一块地表,这个包不能将它们分开——所以
决定不留图像是消费者的,它应该让它:一个洞
在映射图中,一个自信的空白图片是不可见的.
何为不归咎,为何如此
- 第三方组成部分 从自己汇编的联署材料中释放自己的DOM。 这个 邦德从未见过他们的JSX.
- 预编译的工作空间包 已经把他们的JSX变成了
jsx()在捆绑者逃跑前打电话 首方代码,仍未分出. React.createElement财务报告和审定财务报表dangerouslySetInnerHTML- 没有JSX标签,没有 邮票.
在未分配DOM上着陆的区域通过最近的 已盖章 祖先,这是挂载第三方组件的文件。 这是 诚实的答案, 而这是一个设计师可以采取行动.
回读地图
resolveRegionAt 是再出口到这里,但它是 定义 内
@devfellowship/ux-paths-spec。一个查看器应该从那里导入
全部跳过此软件包
用于浏览器.
许可证
麻省理工学院