)
桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载本文围绕 MooTool 仓库next-compose目录下的 DIFF-662 切片文档展开讲清 F11 网络/IP 工具中 IPv4↔Long 两个转换按钮的可点击状态是如何被NetWiringPresentation的两个纯函数ipv4ToLongActionEnabled/longToIpv4ActionEnabled派生出来的以及它们如何委托给NetEngine做校验、在NetScreen中完成接线、并被 desktopTest 单测覆盖。读完你能掌握这套“展示层纯函数 引擎委托校验”的按钮状态控制写法并能复现该切片的验证命令。切片定位与验证基线DIFF 系列文档是next-compose/docs/diff/下逐片记录的增量开发日志每片只聚焦一个最小变更面。DIFF-662 文档 原文极简但信息完整基线DIFF-661工作区即本切片建立在 DIFF-661 之后的工作树上后续切片 DIFF-663 又以 DIFF-662 为基线形成链式演进。范围F11 的 IPv4↔Long 转换钮ipv4ToLongActionEnabled/longToIpv4ActionEnabled委托NetEngine校验NetScreen接线。验证JDK 21 环境./gradlew :composeApp:desktopTest --offline --no-daemon ./scripts/verify-product-evidence-prep.sh其中--offline表示不访问网络解析依赖--no-daemon避免 Gradle 守护进程残留证据准备脚本对应仓库中的 verify-product-evidence-prep.sh。验收台账 acceptance.md 的 F11 行也明确把「IPv4/Long 转换ipv4ToLongActionEnabled/longToIpv4ActionEnabled」标注为引用 DIFF-662 的已接线能力说明该切片在 F11 工具左侧命令输出区 右侧功能区的网络/IP 面板中属于 IPv4/Long 转换小节的核心状态控制。NetWiringPresentation把按钮 enabled 派生成可单测的纯函数本切片新增的两个函数位于 NetWiringPresentation.ktfun ipv4ToLongActionEnabled(ipv4: String): Boolean runCatching { NetEngine.ipv4ToLong(ipv4.trim()) true }.getOrDefault(false) fun longToIpv4ActionEnabled(longText: String): Boolean runCatching { NetEngine.longToIpv4(longText.trim()) true }.getOrDefault(false)写法上有三个值得注意的点校验逻辑零重复。NetWiringPresentation本身不重新实现“什么算合法 IPv4 / 合法 Long”的判断而是直接调用NetEngine.ipv4ToLong/NetEngine.longToIpv4并捕获其抛出的NetConvertException转换成功返回true按钮可点抛异常返回false按钮置灰。这样 UI 的可用态与引擎的真实转换规则永远同源不会出现“按钮可点但点下去报错”的分裂状态。trim()先行。两个函数都先去除首尾空白再校验与同文件中portScanStart等入口对输入统一 trim 的处理风格一致见 NetWiringPresentation.kt。纯函数、无 Compose 依赖。NetWiringPresentation是一个object内部只放fun不含Composable因此可以被 desktopTest 直接调用断言这正是该切片的可测试性来源。同文件还定义了PortScanStart/HostCommandStart这类sealed interfaceL5-L13把“可启动/被拦截 错误码”建模为数据供 UI 分支渲染转换钮场景更简单只需一个Boolean。NetEngine 侧的转换规则与边界两个按钮最终委托到的引擎实现位于 NetEngine.ktfun ipv4ToLong(value: String): Long { val parts value.trim().split(.) if (parts.size ! 4 || parts.any { !it.matches(Regex(^\d{1,3}$)) || it.toInt() 255 }) { throw NetConvertException(Invalid IPv4 address) } return parts.fold(0L) { acc, part - acc * 256 part.toInt() } } fun longToIpv4(value: String): String { val number value.trim().toDoubleOrNull() ?: throw NetConvertException(Invalid IPv4 number) if (number % 1.0 ! 0.0 || number 0 || number 0xffffffffL.toDouble()) { throw NetConvertException(Invalid IPv4 number) } return longToIpv4(number.toLong()) } fun longToIpv4(value: Long): String { if (value 0 || value 0xffffffffL) throw NetConvertException(Invalid IPv4 number) return listOf(24, 16, 8, 0).joinToString(.) { shift - ((value shr shift) and 0xffL).toString() } }规则可以从源码直接读出IPv4 → Long必须恰好 4 段、每段 1~3 位纯数字且不超过 255按acc * 256 段值折叠成无符号 32 位整数。注意前导零是被接受的——测试断言了ipv4ToLong(127.0.0.01) 2130706433LNetEngineTest.kt因为正则^\d{1,3}$不禁止01。Long → IPv4字符串入口先toDoubleOrNull()再用number % 1.0 ! 0.0排除小数如1.5并用0..0xffffffffL限制在 32 位无符号区间Long 重载则按 24/16/8/0 位移取低 8 位拼成点分十进制。失败语义非法输入统一抛NetConvertExceptionNetEngine.kt它区别于命令类错误的NetException后者携带NetworkErrorCode让 UI 能对“转换失败”与“命令失败”分别给出提示。对应的失败边界用例集中在 NetEngineTest.kt256.0.0.1段越界、1.2.3/1.2.3.4.5段数错误、::1IPv6 字面量、127.0.0.空段应全部抛出NetConvertException反向则-1、4294967296上界 1、1.5小数均被拒绝同时0.0.0.0 ↔ 0、255.255.255.255 ↔ 4294967295两个端点可往返。NetScreen 接线按钮 enabled 绑定与点击后的完整副作用UI 侧接线在 NetScreen.kt 的Section(container.t(net.ipv4Long))内布局为「IPv4 输入框 → 两个转换按钮 → Long 输入框」Section(container.t(net.ipv4Long)) { LabeledField(IPv4, session.ipv4) { session.ipv4 it; persist() } Row(horizontalArrangement Arrangement.spacedBy(8.dp)) { MooButton( ${container.t(common.convert)} ↓, p5Toolbar true, enabled NetWiringPresentation.ipv4ToLongActionEnabled(session.ipv4), onClick { runCatching { NetEngine.ipv4ToLong(session.ipv4).toString() } .onSuccess { value - session.longValue value session.error container.history.save( ToolId.Net.id, NetHistoryMetadata.OP_IPV4_TO_LONG, value, session.ipv4, value, ) persist() } .onFailure { notifyNetFailure(container, session, convertMessage(container, it)) { persist() } } }) MooButton( ↑ ${container.t(common.convert)}, p5Toolbar true, enabled NetWiringPresentation.longToIpv4ActionEnabled(session.longValue), onClick { /* 对称longToIpv4 → session.ipv4历史操作 OP_LONG_TO_IPV4 */ }) } LabeledField(Long, session.longValue) { session.longValue it; persist() } }这条链路的完整行为是enabled 实时派生MooButton的enabled直接调用本切片新增的NetWiringPresentation.*ActionEnabled。由于session.ipv4/session.longValue是 Compose 可观察状态用户每次改输入框onChange触发persist()都会引发重组按钮置灰/点亮随之自动更新无需手动刷新。点击后写回对侧字段转换成功时把结果写入另一端session.longValue或session.ipv4并清空session.error。写入通用历史通过container.history.save(ToolId.Net.id, ...)以NetHistoryMetadata.OP_IPV4_TO_LONG/OP_LONG_TO_IPV4操作类型记录本次转换的输入与输出纳入 F11 工具的历史恢复体系。失败兜底虽然按钮已按纯函数置灰但点击与点击前的状态仍可能存在竞态例如用户在 IME 合成过程中状态未更新因此onClick内部仍保留runCatching失败时经notifyNetFailureconvertMessage给出真实失败原因而不是静默或伪造结果——这与 acceptance.md 对 F11「失败显示真原因」的总体要求一致。单测覆盖与回归验证针对本切片NetWiringPresentationTest.kt 新增了ipv4LongConvertActionEnabled用例覆盖按钮态的四个关键输入assertTrue(NetWiringPresentation.ipv4ToLongActionEnabled(127.0.0.1)) assertFalse(NetWiringPresentation.ipv4ToLongActionEnabled()) assertFalse(NetWiringPresentation.ipv4ToLongActionEnabled(256.0.0.1)) assertTrue(NetWiringPresentation.longToIpv4ActionEnabled(2130706433)) assertFalse(NetWiringPresentation.longToIpv4ActionEnabled()) assertFalse(NetWiringPresentation.longToIpv4ActionEnabled(4294967296))即合法地址/合法数值 → 可点空串、段越界256.0.0.1、超上界4294967296比 32 位无符号最大值大 1→ 置灰。与 NetEngineTest 的引擎级用例配合形成「引擎转换规则 展示层派生规则」双层验证。回归方式沿用原切片文档给出的两条命令JDK 21./gradlew :composeApp:desktopTest --offline --no-daemon ./scripts/verify-product-evidence-prep.sh从源码结构看*ActionEnabled这类命名与纯函数化接线在同仓库 DIFF-654 至 DIFF-669 的多个切片中反复出现如时间、加密、Cron、计算器等工具各有对应的*ActionEnabled切片可以推断这是next-compose在逐工具对齐 Electron 行为时统一采用的展示层状态控制模式把“按钮何时可点”的判断从Composable体内剥离到可单测的纯函数中并以对应工具引擎作为唯一校验来源。本文所讲的 F11 IPv4/Long 转换正是该模式在网络工具上的具体落地。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐openclaw-cn 浏览器自动化 CLI 完全指南clawdbot browser 的配置、标签页管理、快照操作与远程控制openclaw cn 浏览器自动化 CLI 完全指南clawdbot browser 的配置、标签页管理、快照操作与远程控制 clawdbot browse桌面应用开发工具cppcheck incompatibleFileOpen 检查详解同一文件被读写双流并发打开cppcheck incompatibleFileOpen 检查详解同一文件被读写双流并发打开 导读 本文深入讲解 Cppcheck 静态分析工具中的 inc桌面应用开发工具Zabbix 8.0 Nextcloud by HTTP 模板完全指南基于 OCS API 的无脚本监控实战Zabbix 8.0 Nextcloud by HTTP 模板完全指南基于 OCS API 的无脚本监控实战 导读 本文围绕 Zabbix 官方模板仓库中桌面应用开发工具上一篇如何快速掌握threejsfundamentals核心概念场景、相机与渲染器解析下一篇Gleam分布式执行系统简介与使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考