mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-12 09:35:07 +00:00
App/ 根目录原有 73 个散落源文件,已无法从目录读出任何分组信息。 按「目录即分层」重组为 Composition / Application / Presentation / UI / PlatformEffects / Resources 六个目录,UI 层再按子视图分目录(Workspace、 Home、Servers(含 Plugin)、Subscriptions、Rules、Settings、Diagnostics、 StatusMenu)。纯移动:类型名、模块名与 import 均未改动,App/ 根层不再有 散落文件。 配套改动: - project.yml:Core 测试镜像的 excludes 由裸文件名改为 `**/` 递归形式。 XcodeGen 的 exclude 不跨 `/`,裸名只匹配 App 根层;MainApp.swift 下移后 旧写法会静默失效,把 @main 组合根编进测试镜像。info.path 与 entitlements.path 指向 App/Resources/;并排除 `**/*.md`——App/ 下任何 非源码文件都会被 XcodeGen 收进 Resources 阶段并封进签名后的 bundle。 - SkeletonTests 的架构守卫由「默认扫描全部 App/ + 7 个豁免文件 + 1 个豁免 前缀 + 4 个豁免目录」改为正面圈定 App/UI/,豁免登记全部删除;新增运行时 适配器放进 Application/ 即自动落在扫描范围外,无需再登记。 - README 工程结构说明同步;根 .gitignore 的生成物路径随 Resources/ 归位。 验证:clean build 0 error;task test 1022 用例全通过(含两个架构守卫); task format 无改动(证明纯移动);task lint 与移动前同为 21 条内容型 warning;packaging-gate.sh ALL CHECKS PASSED,确认 plist/entitlements 归位 后签名与 entitlements 语义不变。 注意:迁移时须先删旧的生成物再 xcodegen generate,否则遗留的 App/Info.plist 会被当作普通资源扫进 Resources 阶段。
65 lines
2.2 KiB
Swift
65 lines
2.2 KiB
Swift
import SwiftUI
|
|
|
|
struct RulesSourceSummaryView: View {
|
|
let source: RulesSourceSnapshot
|
|
let openReport: () -> Void
|
|
|
|
var body: some View {
|
|
if let metadata = source.metadata {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
Text(verbatim: String(metadata.source.upstreamVersion.prefix(12)))
|
|
.help(metadata.source.upstreamVersion)
|
|
Text(verbatim: metadata.license)
|
|
if source.conversionReport != nil {
|
|
Button(RulesCopy.text("快照制作时的转换报告"), action: openReport)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
}
|
|
.font(.caption)
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.textSelection(.enabled)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct RulesReportView: View {
|
|
static let sceneID = "rules-conversion-report"
|
|
@ObservedObject var workflow: RulesWorkflow
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
if let source = workflow.reportSource, let metadata = source.metadata {
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text(verbatim: "\(source.id.label) · \(metadata.source.upstreamVersion)")
|
|
Text(verbatim: metadata.upstreamReference)
|
|
Text(verbatim: "\(metadata.license) · \(metadata.attribution)")
|
|
if let report = source.conversionReport {
|
|
Divider()
|
|
Text(verbatim: "\(RulesCopy.text("已转换")): \(report.convertedCount)")
|
|
Text(verbatim: "\(RulesCopy.text("已吸收")): \(report.absorbedCount)")
|
|
counts(report.skipped, label: RulesCopy.text("已跳过"))
|
|
counts(report.rejected, label: RulesCopy.text("已拒绝"))
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding()
|
|
.textSelection(.enabled)
|
|
}
|
|
}
|
|
.navigationTitle(
|
|
(workflow.reportSource?.id.label ?? "") + " · " + RulesCopy.text("快照制作时的转换报告")
|
|
)
|
|
.frame(minWidth: 480, minHeight: 320)
|
|
}
|
|
|
|
private func counts(_ values: [String: Int], label: String) -> some View {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text(verbatim: label)
|
|
ForEach(values.keys.sorted(), id: \.self) { key in
|
|
Text(verbatim: "\(key): \(values[key, default: 0])")
|
|
}
|
|
if values.isEmpty { Text(verbatim: "0") }
|
|
}
|
|
}
|
|
}
|