| | |
| | | { |
| | | "schema_version": 1, |
| | | "commit": "1fea9bfc8a7639bb3573a7cf2a449ab55065ab9e", |
| | | "indexed_at": "2026-06-27T17:07:38Z", |
| | | "project": "Users-ar-Projects-Aligner2-03-O", |
| | | "nodes": 3988, |
| | | "edges": 12382, |
| | | "original_size": 7864320, |
| | | "compressed_size": 1453118, |
| | | "compression_level": 9 |
| | | } |
| | | "commit": "21e9dd7c95e3f58a19a7c7342ff3d6584eb9097e", |
| | | "indexed_at": "2026-08-14T12:21:29Z", |
| | | "project": "Users-ar-Projects-Aligner-03-O", |
| | | "nodes": 4041, |
| | | "edges": 12465, |
| | | "original_size": 12386304, |
| | | "compressed_size": 1518740, |
| | | "compression_level": 19 |
| | | } |
| | |
| | | # Aligner CODEGRAPH |
| | | |
| | | 最后更新:2026-06-28 |
| | | 最后更新:2026-08-14 |
| | | |
| | | ## 用途 |
| | | |
| | |
| | | 当前基线: |
| | | |
| | | - 功能系列:`0.1.x` |
| | | - 当前可核对 tag:`v0.1.12-build20260625.2320` |
| | | - 最近本地验收候选包:`0.1.12 / 20260625.2320` |
| | | - 最后冻结前验证:2026-06-28,`swift build`、`swift test`、`round015-entitlement-fixture-qa.sh`、`round1-main-ui-qa.sh` 通过;主 UI QA 日志目录 `/tmp/aligner-round01-main-ui-qa-20260628_210821`。 |
| | | - 发布事实:`0.1.12` 是本地 QA 候选代码冻结 / rollback baseline;不是正式官网 release,Developer ID、notarization、staple、Gatekeeper、macOS 14 Phase C、下载源和正式发布 manifest 仍未闭环。 |
| | | - 当前可核对 tag:`v0.1.13-build20260814.0445` |
| | | - 最近本地验收候选包:`0.1.13 / 20260814.0445` |
| | | - 最后冻结前验证:2026-08-14,`swift build`、`swift test`、`round1-app-shelf-fixture-qa.sh`、DMG 挂载元数据检查和 mounted App `codesign --verify --deep --strict` 通过;用户实测两行 App Shelf 遮挡问题已解决。 |
| | | - 发布事实:`0.1.13` 是本地验收通过的代码冻结 / rollback baseline;不是正式官网 release,Developer ID、notarization、staple、Gatekeeper、下载源和正式发布 manifest 仍未闭环。 |
| | | - Git 根目录:`/Users/ar/Projects/Aligner/03-O` |
| | | - SwiftPM 根目录:`/Users/ar/Projects/Aligner/03-O/C1.source` |
| | | - codebase-memory 共享产物:`/Users/ar/Projects/Aligner/03-O/.codebase-memory/graph.db.zst` |
| | | - codebase-memory 当前状态:stale;`03-O/.codebase-memory/artifact.json` 仍指向旧项目名 `Users-ar-Projects-Aligner2-03-O` 和 commit `1fea9bfc8a7639bb3573a7cf2a449ab55065ab9e`,不得作为当前代码事实源。 |
| | | - codebase-memory 当前状态:refreshed;`03-O/.codebase-memory/artifact.json` 指向项目 `Users-ar-Projects-Aligner-03-O` 和源码 commit `21e9dd7c95e3f58a19a7c7342ff3d6584eb9097e`。 |
| | | - codebase-memory 预期索引范围:`C1.source`、`C3.tools` |
| | | - codebase-memory 旧产物规模:`3988` nodes / `12382` edges |
| | | - codebase-memory 当前产物规模:`4041` nodes / `12465` edges |
| | | |
| | | ## 更新触发 |
| | | |
| | |
| | | - App Shelf 序号为 `1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ`,最多 36 个一键入口。 |
| | | - App 序号键在纵栏和横栏瀑布下一视同仁,等效于 hover 到对应 App 图标。 |
| | | - App Shelf 鼠标 hover 只作用于真实 hover App:图标本身放大并轻微上浮,但不显示蓝色矩形底或蓝色矩形边框;纵栏瀑布和横栏瀑布下都适用。 |
| | | - App Shelf 两行布局必须按真实行高预留完整 shelf 高度;首行不得被 Space Lane 裁挡,且 Waterfall 仍保留固定顶间距。 |
| | | - 鼠标停留在 App 图标上不动时,即使 view-model / 截图刷新触发布局重建,真实 hover App 必须保持放大、上浮和 Waterfall 联动;不能自行恢复普通尺寸。 |
| | | - App Shelf hover 不做邻近 App 视觉跟随;左右邻居不放大、不上浮、不暴露 `magneticNeighbor`,也不触发 hover 语义、不显示 close button、不联动 Waterfall。 |
| | | - 用户开启 Reduce Motion 时,App Shelf 不做磁性放大动效,只保留稳定状态切换。 |
| | |
| | | <key>CFBundlePackageType</key> |
| | | <string>APPL</string> |
| | | <key>CFBundleShortVersionString</key> |
| | | <string>0.1.12</string> |
| | | <string>0.1.13</string> |
| | | <key>CFBundleVersion</key> |
| | | <string>20260625.2320</string> |
| | | <string>20260814.0445</string> |
| | | <key>LSMinimumSystemVersion</key> |
| | | <string>14.0</string> |
| | | <key>NSHighResolutionCapable</key> |
| | |
| | | static let normalGap: CGFloat = 16 |
| | | static let minGap: CGFloat = 8 |
| | | static let maxRows = 2 |
| | | static let rowHeight: CGFloat = selectedIconSize + 18 |
| | | static let rowGap: CGFloat = 8 |
| | | static let verticalPadding: CGFloat = 6 |
| | | static let indexFontSize: CGFloat = 16 |
| | | static let compressedIndexFontSize: CGFloat = 14 |
| | | static let indexWidth: CGFloat = 18 |
| | |
| | | } |
| | | |
| | | private func appShelfHeight(forRows rows: Int) -> CGFloat { |
| | | switch rows { |
| | | case 0, 1: |
| | | return 92 |
| | | default: |
| | | return 148 |
| | | } |
| | | appShelfRowsHeight(forRows: max(1, rows)) + AppShelfMetrics.verticalPadding |
| | | } |
| | | |
| | | private func appShelfRowsHeight(forRows rows: Int) -> CGFloat { |
| | | let rowCount = max(1, rows) |
| | | return CGFloat(rowCount) * AppShelfMetrics.rowHeight |
| | | + CGFloat(rowCount - 1) * appShelfRowGap(forRows: rowCount) |
| | | } |
| | | |
| | | private func appShelfRowGap(forRows rows: Int) -> CGFloat { |
| | | rows > 1 ? AppShelfMetrics.rowGap : 0 |
| | | } |
| | | |
| | | private var appShelfSlotPadding: CGFloat { |
| | |
| | | let iconSize = appShelfLayout.iconSize |
| | | let gap = appShelfGap(forIconSize: iconSize) |
| | | let itemWidth = appShelfSlotWidth(forIconSize: iconSize) |
| | | let rowHeight: CGFloat = AppShelfMetrics.selectedIconSize + 18 |
| | | let rowGap: CGFloat = appShelfLayout.rows > 1 ? 8 : 0 |
| | | let rowHeight = AppShelfMetrics.rowHeight |
| | | let rowGap = appShelfRowGap(forRows: rows.count) |
| | | let innerWidth = max(1, shelfLayer.bounds.width - 24) |
| | | let maxRowWidth = rows |
| | | .map { widthForAppShelfItems(count: $0.count, itemWidth: itemWidth, gap: gap) } |
| | |
| | | height: shelfLayer.bounds.height |
| | | ) |
| | | |
| | | let totalRowsHeight = CGFloat(rows.count) * rowHeight + CGFloat(max(0, rows.count - 1)) * rowGap |
| | | let totalRowsHeight = appShelfRowsHeight(forRows: rows.count) |
| | | var y = shelfLayer.bounds.midY + totalRowsHeight / 2 - rowHeight |
| | | |
| | | for row in rows { |
| | |
| | | |
| | | row_y_values = {round(item.get("frame", {}).get("y", 0), 2) for item in items} |
| | | require(len(row_y_values) == expected_rows, "fixture App Shelf visible rows must match expected rows") |
| | | |
| | | def rect_number(rect, key): |
| | | value = rect.get(key, 0) |
| | | return value if isinstance(value, (int, float)) else 0 |
| | | |
| | | def rect_max_y(rect): |
| | | return rect_number(rect, "y") + rect_number(rect, "height") |
| | | |
| | | space_lane_frame = root.get("spaceLaneFrame", {}) |
| | | app_shelf_frame = root.get("appShelfFrame", {}) |
| | | waterfall_frame = root.get("waterfallFrame", {}) |
| | | app_shelf_height = rect_number(app_shelf_frame, "height") |
| | | expected_row_height = root.get("appShelfSelectedIconSize", 68) + 18 |
| | | expected_row_gap = 8 if expected_rows > 1 else 0 |
| | | expected_rows_height = expected_rows * expected_row_height + max(0, expected_rows - 1) * expected_row_gap |
| | | epsilon = 0.5 |
| | | require(app_shelf_height + epsilon >= expected_rows_height, "App Shelf frame must reserve enough height for all visible rows") |
| | | require(all(rect_number(item.get("frame", {}), "y") >= -epsilon for item in items), "App Shelf items must not overflow below the shelf frame") |
| | | require(all(rect_max_y(item.get("frame", {})) <= app_shelf_height + epsilon for item in items), "App Shelf items must not overflow above the shelf frame") |
| | | space_lane_gap = rect_number(space_lane_frame, "y") - rect_max_y(app_shelf_frame) |
| | | waterfall_gap = rect_number(app_shelf_frame, "y") - rect_max_y(waterfall_frame) |
| | | require(space_lane_gap >= 15.5, "App Shelf must keep the 16pt gap below Space Lane") |
| | | require(waterfall_gap >= 13.5, "Waterfall must keep the 14pt gap below App Shelf") |
| | | require([item.get("name") for item in items] == root.get("appShelfNames", []), "App Shelf item reports must follow App Shelf order") |
| | | selected_z = items[selected_index].get("zPosition", 0) |
| | | hover_z = items[hover_index].get("zPosition", 0) |
| | |
| | | "appCount": report.get("appCount"), |
| | | "rows": root.get("appShelfRows"), |
| | | "iconSize": root.get("appShelfIconSize"), |
| | | "spaceLaneFrame": space_lane_frame, |
| | | "appShelfFrame": app_shelf_frame, |
| | | "spaceLaneGap": space_lane_gap, |
| | | "waterfallGap": waterfall_gap, |
| | | "selectedIndex": selected_index, |
| | | "selectedIcon": items[selected_index].get("iconFrame"), |
| | | "hoveredIndex": root.get("appShelfHoveredIndex"), |
| | |
| | | # Changelog |
| | | |
| | | ## 0.1.13 / 20260814.0445 |
| | | |
| | | - 修复 Quick Switch 在 App Shelf 自动换成两行时,首行 App 图标被 Space Lane 裁挡的问题。 |
| | | - App Shelf 两行布局改为按真实行高预留 shelf 高度,并保持 Space Lane / App Shelf / Waterfall 的固定纵向间距。 |
| | | - 增强 App Shelf fixture QA,断言两行 App 图标必须完整落在 shelf 内,并保留 Space Lane 与 Waterfall 间距。 |
| | | |
| | | ## 0.1.12 / 20260625.2320 |
| | | |
| | | - 修复 App Shelf 图标点击误触关闭的问题:图标主体点击优先打开该 App 的第一个窗口,只有明确点到关闭按钮视觉区域才进入关闭确认。 |