release(ios): 0.2.2 移除分组改用标签归类并完成石墨配色重构
- 删除分组实体:分组视图/编辑器/仓储与 groupId 全部移除,升级首启清理 instance-groups.json 与 fleet.groupMode - 新增批量标签表:三态覆盖度(未贴 / N/M / 全部),整批加减与新建标签 - 配色重构,全应用零蓝色:石墨墨色承担主操作与选中态,颜色只留给状态语义; 浅色改中性冷灰,深色改低明度石墨三层 - 选中态改实心主色 + onPrimary,替换不可读的低透明度色雾(主题块、图例胶囊) - 应用图标重做为扁平石墨底 + 黄铜 SIM 面板,参数化光栅化脚本与参考稿 - 运行概览:检测进度并入副标题,读数栅格固定八格两行并保留不可用占位 - 实例详情:仪表改两列发丝线栅格,缺数据留轨道,修掉头部高度回弹; 补齐设备名称/存储总量/可用存储/使用率四个字段名 - 版本号显示去掉括号,设置页只显示 0.2.2 - 文档:新增 docs/ios-native-0.2.2.md,同步 ios-native.md 与两处 README
This commit is contained in:
+17
-30
@@ -1,44 +1,31 @@
|
||||
<!-- 设计参考稿,构图与 Design/generate_app_icon.swift 一致;构建实际用 Swift 脚本光栅化 1024 PNG。 -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<linearGradient id="field" x1="0.10" y1="0" x2="0.92" y2="1">
|
||||
<stop offset="0" stop-color="#1C4280"/>
|
||||
<stop offset="0.46" stop-color="#0D2450"/>
|
||||
<stop offset="1" stop-color="#040B1B"/>
|
||||
<linearGradient id="field" x1="0.06" y1="0" x2="0.94" y2="1">
|
||||
<stop offset="0" stop-color="#1A1F1E"/>
|
||||
<stop offset="0.55" stop-color="#111514"/>
|
||||
<stop offset="1" stop-color="#0B0E0D"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="key" cx="0.26" cy="-0.04" r="0.92">
|
||||
<stop offset="0" stop-color="#96C4FF" stop-opacity="0.25"/>
|
||||
<stop offset="0.55" stop-color="#70A2E6" stop-opacity="0.08"/>
|
||||
<stop offset="1" stop-color="#70A2E6" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="vig" cx="0.5" cy="0.52" r="0.78">
|
||||
<stop offset="0.55" stop-color="#020712" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#020712" stop-opacity="0.59"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="gold" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#FFF4CD"/>
|
||||
<stop offset="0.30" stop-color="#FACE6A"/>
|
||||
<stop offset="0.62" stop-color="#D69628"/>
|
||||
<stop offset="1" stop-color="#925C10"/>
|
||||
<linearGradient id="brass" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#F8D57C"/>
|
||||
<stop offset="0.50" stop-color="#ECBD56"/>
|
||||
<stop offset="1" stop-color="#D49E36"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="slot" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#050D1E"/>
|
||||
<stop offset="0.58" stop-color="#091732"/>
|
||||
<stop offset="1" stop-color="#112649"/>
|
||||
<stop offset="0" stop-color="#0F1312"/>
|
||||
<stop offset="1" stop-color="#1D2321"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<rect width="1024" height="1024" fill="url(#field)"/>
|
||||
<rect width="1024" height="1024" fill="url(#key)"/>
|
||||
<rect width="1024" height="1024" fill="url(#vig)"/>
|
||||
|
||||
<!-- SIM 卡体:左上切角,其余三角大圆角 -->
|
||||
<path d="M494.5,218 L688,218 Q822,218 822,352 L822,704 Q822,838 688,838 L336,838
|
||||
Q202,838 202,704 L202,510.5 Q202,420 266,356 L340,282 Q404,218 494.5,218 Z"
|
||||
fill="url(#gold)"/>
|
||||
<path d="M216,403.7 Q216,372 238.4,349.6 L361.6,226.4 Q384,204 415.7,204 L686,204
|
||||
Q808,204 808,326 L808,674 Q808,796 686,796 L304,796 Q216,796 216,674 Z"
|
||||
fill="url(#brass)"/>
|
||||
|
||||
<!-- 三格信号:底边对齐,右高,读作多卡队列健康度 -->
|
||||
<rect x="318" y="546" width="88" height="142" rx="26" fill="url(#slot)"/>
|
||||
<rect x="468" y="446" width="88" height="242" rx="26" fill="url(#slot)"/>
|
||||
<rect x="618" y="346" width="88" height="342" rx="26" fill="url(#slot)"/>
|
||||
<!-- 三格插槽:底边对齐,右高,读作多卡队列健康度 -->
|
||||
<rect x="324" y="504" width="92" height="136" rx="28" fill="url(#slot)"/>
|
||||
<rect x="470" y="408" width="92" height="232" rx="28" fill="url(#slot)"/>
|
||||
<rect x="616" y="312" width="92" height="328" rx="28" fill="url(#slot)"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,11 +1,10 @@
|
||||
// Renders the 1024x1024 MultiSimAdmin app icon with CoreGraphics only.
|
||||
// Usage: swift Design/generate_app_icon.swift <output.png>
|
||||
//
|
||||
// The mark is the same geometry as the web favicon: a gold SIM plate carrying
|
||||
// three ascending signal bars. One flat silhouette, one light source, and no
|
||||
// photographic detail, so it still reads at the 29pt settings size. The plate
|
||||
// keeps the classic SIM chamfer, moved to the top-left so the tallest bar can
|
||||
// sit under the intact top-right corner.
|
||||
// The mark is the web favicon grown up: a flat brass SIM plate with three
|
||||
// ascending slots cut out of it, on a graphite field. Two colours, one light
|
||||
// source from the upper left, no metal texture, so it still reads at the 29pt
|
||||
// settings size. The plate keeps the classic single SIM cut, top-left.
|
||||
//
|
||||
// Every coordinate is in visual space (origin top-left, y grows down).
|
||||
import AppKit
|
||||
@@ -87,15 +86,15 @@ func roundedPath(_ rect: CGRect, _ radius: CGFloat) -> CGPath {
|
||||
CGPath(roundedRect: rect, cornerWidth: radius, cornerHeight: radius, transform: nil)
|
||||
}
|
||||
|
||||
/// SIM plate outline: rounded square with one cut top corner.
|
||||
func platePath(_ rect: CGRect, corner: CGFloat, chamfer: CGFloat) -> CGPath {
|
||||
let bevel = corner * 0.28
|
||||
/// SIM plate outline: rounded square with the top-left corner cut at 45 degrees.
|
||||
func simPath(_ rect: CGRect, corner: CGFloat, chamfer: CGFloat) -> CGPath {
|
||||
let bevel = corner * 0.26
|
||||
let vertices: [(point: CGPoint, radius: CGFloat)] = [
|
||||
(CGPoint(x: rect.minX, y: rect.minY + chamfer), bevel),
|
||||
(CGPoint(x: rect.minX + chamfer, y: rect.minY), bevel),
|
||||
(CGPoint(x: rect.maxX, y: rect.minY), corner),
|
||||
(CGPoint(x: rect.maxX, y: rect.maxY), corner),
|
||||
(CGPoint(x: rect.minX, y: rect.maxY), corner),
|
||||
(CGPoint(x: rect.minX, y: rect.minY + chamfer), bevel),
|
||||
]
|
||||
let path = CGMutablePath()
|
||||
path.move(to: vertices[0].point)
|
||||
@@ -110,7 +109,7 @@ func platePath(_ rect: CGRect, corner: CGFloat, chamfer: CGFloat) -> CGPath {
|
||||
return path
|
||||
}
|
||||
|
||||
/// Bright along the upper-left edges, dead by the lower-right: an even outline
|
||||
/// Bright along the upper-left edges, gone by the lower-right: an even outline
|
||||
/// reads like a sticker cut rather than a plate lit from above.
|
||||
func directionalRim(_ path: CGPath, _ rect: CGRect, _ width: CGFloat, _ stops: [Stop]) {
|
||||
ctx.saveGState()
|
||||
@@ -130,38 +129,34 @@ func directionalRim(_ path: CGPath, _ rect: CGRect, _ width: CGFloat, _ stops: [
|
||||
fillLinear(
|
||||
rectPath(CGRect(x: 0, y: 0, width: side, height: side)),
|
||||
[
|
||||
(0.00, (28, 66, 128, 255)),
|
||||
(0.46, (13, 36, 80, 255)),
|
||||
(1.00, (4, 11, 27, 255)),
|
||||
(0.00, (26, 31, 30, 255)),
|
||||
(0.55, (17, 21, 20, 255)),
|
||||
(1.00, (11, 14, 13, 255)),
|
||||
] as [Stop],
|
||||
CGPoint(x: side * 0.10, y: 0),
|
||||
CGPoint(x: side * 0.92, y: side)
|
||||
CGPoint(x: side * 0.06, y: 0),
|
||||
CGPoint(x: side * 0.94, y: side)
|
||||
)
|
||||
fillRadial(
|
||||
CGPoint(x: side * 0.26, y: -side * 0.04), CGFloat(side * 0.92),
|
||||
[(0.0, (150, 196, 255, 64)), (0.55, (112, 162, 230, 20)), (1.0, (112, 162, 230, 0))] as [Stop]
|
||||
)
|
||||
fillRadial(
|
||||
CGPoint(x: side * 0.5, y: side * 0.52), CGFloat(side * 0.78),
|
||||
[(0.55, (2, 7, 18, 0)), (1.0, (2, 7, 18, 150))] as [Stop]
|
||||
CGPoint(x: side * 0.22, y: -side * 0.10), CGFloat(side * 0.86),
|
||||
[(0.0, (214, 226, 218, 26)), (0.60, (214, 226, 218, 7)), (1.0, (214, 226, 218, 0))] as [Stop]
|
||||
)
|
||||
|
||||
// MARK: - Plate
|
||||
|
||||
let plateSide = envDouble("ICON_PLATE_SIDE", 620)
|
||||
let plateCenterY = envDouble("ICON_PLATE_Y", 528)
|
||||
let plateCorner = envDouble("ICON_PLATE_CORNER", 134)
|
||||
let plateChamfer = envDouble("ICON_PLATE_CHAMFER", 202)
|
||||
let plateSide = envDouble("ICON_PLATE_SIDE", 592)
|
||||
let plateCenterY = envDouble("ICON_PLATE_Y", 500)
|
||||
let plateCorner = envDouble("ICON_PLATE_CORNER", 122)
|
||||
let plateChamfer = envDouble("ICON_PLATE_CHAMFER", 168)
|
||||
let plateRect = CGRect(
|
||||
x: (side - plateSide) / 2,
|
||||
y: plateCenterY - plateSide / 2,
|
||||
width: plateSide,
|
||||
height: plateSide
|
||||
)
|
||||
let plate = platePath(plateRect, corner: plateCorner, chamfer: plateChamfer)
|
||||
let plate = simPath(plateRect, corner: plateCorner, chamfer: plateChamfer)
|
||||
|
||||
ctx.saveGState()
|
||||
ctx.setShadow(offset: CGSize(width: 0, height: -30), blur: 62, color: rgb(2, 8, 20, 168))
|
||||
ctx.setShadow(offset: CGSize(width: 0, height: -22), blur: 46, color: rgb(0, 3, 2, 150))
|
||||
ctx.addPath(plate)
|
||||
ctx.setFillColor(rgb(0, 0, 0))
|
||||
ctx.fillPath()
|
||||
@@ -170,98 +165,61 @@ ctx.restoreGState()
|
||||
fillLinear(
|
||||
plate,
|
||||
[
|
||||
(0.00, (255, 244, 205, 255)),
|
||||
(0.30, (250, 206, 106, 255)),
|
||||
(0.62, (214, 150, 40, 255)),
|
||||
(1.00, (146, 92, 16, 255)),
|
||||
(0.00, (248, 213, 124, 255)),
|
||||
(0.50, (236, 189, 86, 255)),
|
||||
(1.00, (212, 158, 54, 255)),
|
||||
] as [Stop],
|
||||
CGPoint(x: plateRect.minX, y: plateRect.minY),
|
||||
CGPoint(x: plateRect.maxX, y: plateRect.maxY)
|
||||
)
|
||||
|
||||
directionalRim(
|
||||
plate, plateRect, plateSide * 0.016,
|
||||
plate, plateRect, plateSide * 0.007,
|
||||
[
|
||||
(0.00, (255, 251, 232, 230)),
|
||||
(0.40, (255, 240, 190, 66)),
|
||||
(0.74, (255, 232, 170, 0)),
|
||||
(0.00, (255, 244, 214, 132)),
|
||||
(0.36, (255, 238, 198, 30)),
|
||||
(0.68, (255, 234, 186, 0)),
|
||||
] as [Stop]
|
||||
)
|
||||
|
||||
ctx.saveGState()
|
||||
ctx.addPath(plate)
|
||||
ctx.clip()
|
||||
ctx.move(to: CGPoint(x: plateRect.minX - 40, y: plateRect.minY + plateSide * 0.52))
|
||||
ctx.addLine(to: CGPoint(x: plateRect.maxX + 40, y: plateRect.minY + plateSide * 0.10))
|
||||
ctx.addLine(to: CGPoint(x: plateRect.maxX + 40, y: plateRect.minY + plateSide * 0.22))
|
||||
ctx.addLine(to: CGPoint(x: plateRect.minX - 40, y: plateRect.minY + plateSide * 0.64))
|
||||
ctx.closePath()
|
||||
ctx.setFillColor(rgb(255, 252, 236, 5))
|
||||
ctx.fillPath()
|
||||
ctx.restoreGState()
|
||||
// MARK: - Slots
|
||||
|
||||
// MARK: - Signal bars
|
||||
let slotWidth = envDouble("ICON_SLOT_W", 92)
|
||||
let slotGap = envDouble("ICON_SLOT_GAP", 54)
|
||||
let slotBottom = envDouble("ICON_SLOT_BOTTOM", 640)
|
||||
let slotHeights: [CGFloat] = [136, 232, 328]
|
||||
let slotsWidth = CGFloat(slotHeights.count) * slotWidth + CGFloat(slotHeights.count - 1) * slotGap
|
||||
let slotsStartX = (side - slotsWidth) / 2
|
||||
|
||||
let barWidth = envDouble("ICON_BAR_W", 88)
|
||||
let barGap = envDouble("ICON_BAR_GAP", 62)
|
||||
let barBottom = envDouble("ICON_BAR_BOTTOM", 688)
|
||||
let barHeights: [CGFloat] = [142, 242, 342]
|
||||
let barsWidth = CGFloat(barHeights.count) * barWidth + CGFloat(barHeights.count - 1) * barGap
|
||||
let barsStartX = (side - barsWidth) / 2
|
||||
|
||||
for (index, height) in barHeights.enumerated() {
|
||||
for (index, height) in slotHeights.enumerated() {
|
||||
let rect = CGRect(
|
||||
x: barsStartX + CGFloat(index) * (barWidth + barGap),
|
||||
y: barBottom - height,
|
||||
width: barWidth,
|
||||
x: slotsStartX + CGFloat(index) * (slotWidth + slotGap),
|
||||
y: slotBottom - height,
|
||||
width: slotWidth,
|
||||
height: height
|
||||
)
|
||||
let radius = barWidth * 0.30
|
||||
let radius = slotWidth * 0.30
|
||||
let path = roundedPath(rect, radius)
|
||||
|
||||
ctx.saveGState()
|
||||
ctx.setShadow(offset: CGSize(width: 0, height: 5), blur: 16, color: rgb(255, 236, 176, 104))
|
||||
ctx.addPath(path)
|
||||
ctx.setFillColor(rgb(0, 0, 0))
|
||||
ctx.fillPath()
|
||||
ctx.restoreGState()
|
||||
|
||||
fillLinear(
|
||||
path,
|
||||
[
|
||||
(0.00, (5, 13, 30, 255)),
|
||||
(0.58, (9, 23, 50, 255)),
|
||||
(1.00, (17, 38, 74, 255)),
|
||||
(0.00, (15, 19, 18, 255)),
|
||||
(1.00, (29, 35, 33, 255)),
|
||||
] as [Stop],
|
||||
CGPoint(x: rect.minX, y: rect.minY),
|
||||
CGPoint(x: rect.minX, y: rect.maxY)
|
||||
)
|
||||
|
||||
directionalRim(
|
||||
path, rect, radius * 0.34,
|
||||
path, rect, radius * 0.30,
|
||||
[
|
||||
(0.00, (0, 0, 0, 120)),
|
||||
(0.48, (0, 0, 0, 26)),
|
||||
(0.84, (255, 226, 152, 70)),
|
||||
(1.00, (255, 236, 176, 118)),
|
||||
(0.00, (120, 84, 18, 96)),
|
||||
(0.52, (120, 84, 18, 18)),
|
||||
(1.00, (255, 232, 176, 58)),
|
||||
] as [Stop]
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Surface sheen
|
||||
|
||||
ctx.saveGState()
|
||||
ctx.addPath(rectPath(CGRect(x: 0, y: 0, width: side, height: side)))
|
||||
ctx.clip()
|
||||
ctx.move(to: CGPoint(x: -120, y: 430))
|
||||
ctx.addLine(to: CGPoint(x: 1140, y: 40))
|
||||
ctx.addLine(to: CGPoint(x: 1140, y: 214))
|
||||
ctx.addLine(to: CGPoint(x: -120, y: 604))
|
||||
ctx.closePath()
|
||||
ctx.setFillColor(rgb(255, 255, 255, 4))
|
||||
ctx.fillPath()
|
||||
ctx.restoreGState()
|
||||
|
||||
guard let image = ctx.makeImage() else {
|
||||
FileHandle.standardError.write("cannot finalize image\n".data(using: .utf8)!)
|
||||
exit(1)
|
||||
|
||||
Reference in New Issue
Block a user