codex-flow 2.1.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. codex_flow/__init__.py +28 -0
  2. codex_flow/__main__.py +9 -0
  3. codex_flow/cli.py +242 -0
  4. codex_flow/data/LICENSE +21 -0
  5. codex_flow/data/README.en.md +303 -0
  6. codex_flow/data/README.md +305 -0
  7. codex_flow/data/VERSION +1 -0
  8. codex_flow/data/apps/chatgpt-mcp/README.md +86 -0
  9. codex_flow/data/apps/chatgpt-mcp/__init__.py +1 -0
  10. codex_flow/data/apps/chatgpt-mcp/adapter.py +458 -0
  11. codex_flow/data/apps/chatgpt-mcp/server.py +358 -0
  12. codex_flow/data/apps/chatgpt-mcp/widget.html +927 -0
  13. codex_flow/data/apps/macos-overlay/README.en.md +121 -0
  14. codex_flow/data/apps/macos-overlay/README.md +123 -0
  15. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayRuntimeState.swift +126 -0
  16. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayScreenGeometry.swift +82 -0
  17. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayWindowController.swift +1052 -0
  18. codex_flow/data/apps/macos-overlay/Sources/Localization.swift +197 -0
  19. codex_flow/data/apps/macos-overlay/Sources/Models/TelemetryData.swift +1557 -0
  20. codex_flow/data/apps/macos-overlay/Sources/Services/AccountSnapshotService.swift +1101 -0
  21. codex_flow/data/apps/macos-overlay/Sources/Services/FlowPilotInstanceLock.swift +153 -0
  22. codex_flow/data/apps/macos-overlay/Sources/Services/IPCServer.swift +298 -0
  23. codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryQueryEngine.swift +800 -0
  24. codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryWatcher.swift +135 -0
  25. codex_flow/data/apps/macos-overlay/Sources/Services/UpdateService.swift +610 -0
  26. codex_flow/data/apps/macos-overlay/Sources/Views/AccountView.swift +610 -0
  27. codex_flow/data/apps/macos-overlay/Sources/Views/AnalyticsView.swift +566 -0
  28. codex_flow/data/apps/macos-overlay/Sources/Views/AutostartView.swift +293 -0
  29. codex_flow/data/apps/macos-overlay/Sources/Views/BubbleView.swift +317 -0
  30. codex_flow/data/apps/macos-overlay/Sources/Views/HistoryView.swift +1124 -0
  31. codex_flow/data/apps/macos-overlay/Sources/Views/HoverRevealText.swift +165 -0
  32. codex_flow/data/apps/macos-overlay/Sources/Views/InspectorSkillsToolsView.swift +121 -0
  33. codex_flow/data/apps/macos-overlay/Sources/Views/LogoView.swift +182 -0
  34. codex_flow/data/apps/macos-overlay/Sources/Views/SleekSwitch.swift +117 -0
  35. codex_flow/data/apps/macos-overlay/Sources/Views/StrategyModeView.swift +561 -0
  36. codex_flow/data/apps/macos-overlay/Sources/Views/SummaryView.swift +1273 -0
  37. codex_flow/data/apps/macos-overlay/Sources/Views/UpdateView.swift +352 -0
  38. codex_flow/data/apps/macos-overlay/Sources/main.swift +340 -0
  39. codex_flow/data/apps/macos-overlay/Tests/OverlayScreenGeometryTests.swift +163 -0
  40. codex_flow/data/apps/macos-overlay/Tests/TelemetryPhase1ContractTests.swift +357 -0
  41. codex_flow/data/apps/macos-overlay/Tests/TelemetryQueryEngineConcurrencyTests.swift +221 -0
  42. codex_flow/data/apps/macos-overlay/Tests/TelemetryQuotaSelectionTests.swift +158 -0
  43. codex_flow/data/apps/macos-overlay/Tests/TelemetryWorkerTokenTests.swift +122 -0
  44. codex_flow/data/apps/macos-overlay/build.sh +75 -0
  45. codex_flow/data/benchmark/corpus.json +103 -0
  46. codex_flow/data/benchmark/manifest.example.json +41 -0
  47. codex_flow/data/benchmark/manifest.schema.json +137 -0
  48. codex_flow/data/benchmark/prices/gpt-5.6-2026-08-30.json +5 -0
  49. codex_flow/data/benchmark/profiles.json +90 -0
  50. codex_flow/data/benchmark/schema.json +77 -0
  51. codex_flow/data/benchmark/tasks.json +50 -0
  52. codex_flow/data/completions/codex-flow.bash +34 -0
  53. codex_flow/data/completions/codex-flow.zsh +52 -0
  54. codex_flow/data/glama.json +6 -0
  55. codex_flow/data/install-release.ps1 +126 -0
  56. codex_flow/data/install-release.sh +155 -0
  57. codex_flow/data/install.ps1 +349 -0
  58. codex_flow/data/install.sh +362 -0
  59. codex_flow/data/policy/benchmark.toml +49 -0
  60. codex_flow/data/policy/defaults.toml +70 -0
  61. codex_flow/data/scripts/analyze-benchmark.py +510 -0
  62. codex_flow/data/scripts/benchmark-local.py +171 -0
  63. codex_flow/data/scripts/check-recommendation.py +277 -0
  64. codex_flow/data/scripts/doctor.py +449 -0
  65. codex_flow/data/scripts/generate-release-manifest.py +74 -0
  66. codex_flow/data/scripts/localization.py +192 -0
  67. codex_flow/data/scripts/manage-hooks.py +448 -0
  68. codex_flow/data/scripts/manage-instructions.py +389 -0
  69. codex_flow/data/scripts/manage-shell.py +151 -0
  70. codex_flow/data/scripts/materialize-corpus.py +193 -0
  71. codex_flow/data/scripts/menu.py +646 -0
  72. codex_flow/data/scripts/migrations/0001_update_settings.py +80 -0
  73. codex_flow/data/scripts/package-release.py +132 -0
  74. codex_flow/data/scripts/render-benchmark-report.py +292 -0
  75. codex_flow/data/scripts/run-benchmark.py +829 -0
  76. codex_flow/data/scripts/strategies/__init__.py +28 -0
  77. codex_flow/data/scripts/strategies/balanced.py +115 -0
  78. codex_flow/data/scripts/strategies/base.py +363 -0
  79. codex_flow/data/scripts/strategies/efficient.py +158 -0
  80. codex_flow/data/scripts/strategies/lifecycle_runtime.py +590 -0
  81. codex_flow/data/scripts/strategies/quality.py +209 -0
  82. codex_flow/data/scripts/strategies/speed.py +108 -0
  83. codex_flow/data/scripts/strategies/task_budget_runtime.py +644 -0
  84. codex_flow/data/scripts/strategies/task_phase_runtime.py +341 -0
  85. codex_flow/data/scripts/strategies/work_unit_runtime.py +421 -0
  86. codex_flow/data/scripts/strategy_runtime.py +1091 -0
  87. codex_flow/data/scripts/telemetry.py +400 -0
  88. codex_flow/data/scripts/telemetry_core/__init__.py +192 -0
  89. codex_flow/data/scripts/telemetry_core/app_server.py +1192 -0
  90. codex_flow/data/scripts/telemetry_core/collector.py +1247 -0
  91. codex_flow/data/scripts/telemetry_core/common.py +421 -0
  92. codex_flow/data/scripts/telemetry_core/latency.py +593 -0
  93. codex_flow/data/scripts/telemetry_core/query.py +427 -0
  94. codex_flow/data/scripts/telemetry_core/quota_ledger.py +598 -0
  95. codex_flow/data/scripts/telemetry_core/render.py +460 -0
  96. codex_flow/data/scripts/telemetry_core/repair.py +223 -0
  97. codex_flow/data/scripts/ui.py +266 -0
  98. codex_flow/data/scripts/update-homebrew-formula.py +146 -0
  99. codex_flow/data/scripts/update_runtime_config.py +134 -0
  100. codex_flow/data/scripts/updater.py +1718 -0
  101. codex_flow/data/smithery.yaml +18 -0
  102. codex_flow/data/templates/agents/worker-explorer.toml +24 -0
  103. codex_flow/data/templates/agents/worker-implementer.toml +49 -0
  104. codex_flow/data/templates/agents/worker-reviewer.toml +25 -0
  105. codex_flow/data/templates/flow-pilot-instructions.md +35 -0
  106. codex_flow/data/templates/skills/flow-pilot/SKILL.md +577 -0
  107. codex_flow/mcp.py +35 -0
  108. codex_flow-2.1.13.dist-info/METADATA +342 -0
  109. codex_flow-2.1.13.dist-info/RECORD +113 -0
  110. codex_flow-2.1.13.dist-info/WHEEL +5 -0
  111. codex_flow-2.1.13.dist-info/entry_points.txt +3 -0
  112. codex_flow-2.1.13.dist-info/licenses/LICENSE +21 -0
  113. codex_flow-2.1.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1,121 @@
1
+ # macOS Native Floating Widget (FlowPilot Overlay)
2
+
3
+ <div align="center">
4
+
5
+ [ 简体中文 ](README.md) | [ English ](README.en.md)
6
+
7
+ </div>
8
+
9
+ `codex-flow-overlay` is a 100% native macOS floating widget built with **SwiftUI + AppKit**, deeply integrating the entire suite of `codex-flow usage` capabilities (live inspection, rate-limit quota monitoring, multi-session history, and aggregate efficiency analytics).
10
+
11
+ ---
12
+
13
+ <div align="center">
14
+ <img src="../../docs/assets/promo/flowpilot_promo_poster.png" alt="FlowPilot Native Overlay Showcase" width="100%" style="border-radius: 12px; margin: 12px 0;" />
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## ✨ Core Highlights
20
+
21
+ - 🟢 **Micro Capsule (Idle State)**:
22
+ - 68px diameter Frosted Glass bubble (macOS `ultraThinMaterial`);
23
+ - Dynamic rainbow gradient border with real-time breathing status aura;
24
+ - Status indicator (🟢 Idle/Complete · 🔵 Task Running · 🟠 Alert/Error);
25
+ - Live token badge displaying latest turn tokens (e.g. `198.2k`);
26
+ - Automatic half-tuck edge docking and magnetic screen snapping.
27
+
28
+ - ⚡️ **Glass TabBar Control Center (Expanded State)**:
29
+ - **⚡️ Inspector**:
30
+ - **Header**: Project tag, Git branch badge, status pill, Pin lock, and collapse button;
31
+ - **Objective & Outcome**: Auto-extracted task goal and delivery conclusion;
32
+ - **3 KPI Rings**: Real-time circular gauges for Duration, Tokens, and Cost;
33
+ - **Rate Limits & Quota**: 5m / 1h / 1d / 7d quota progression bars and reset countdowns;
34
+ - **Token Distribution Bar**: Stacked breakdown of Prompt, Cached, Output, and Reasoning tokens;
35
+ - **Multi-Agent Topology**: Parent model reasoning effort and Worker subagent concurrency;
36
+ - **Skills & MCP Badges**: Automatic discovery and badge labeling of activated skills and MCP tools;
37
+ - **Historical Drilldown**: Inspect any past task with one-click `[⚡️ Jump to Live]`;
38
+ - **Action Bar**: Copy formatted summary, open Terminal console, Pin lock toggle.
39
+ - **📜 History**:
40
+ - Project filter, `All` / `Today` scope, and instant keyword search;
41
+ - **Chat Accordion**: Aggregates multi-turn runs into chronological chat sessions;
42
+ - **Session Turns**: Turn-level timeline with duration, tokens, worker badges, and quota deltas (`+1%` / `-1%`);
43
+ - Click any turn to inspect full details in the Inspector.
44
+ - **📊 Analytics**:
45
+ - Aggregate efficiency analysis with `7 Days` / `30 Days` toggles;
46
+ - Total tasks (dispatched vs. direct), active hours, and total attributed tokens;
47
+ - **Cache Efficiency**: Hit percentage and cached tokens saved;
48
+ - **Worker Offload**: Percentage of workload delegated to economic models;
49
+ - **Model Breakdown**: Calls, token proportions, and Parent/Worker roles;
50
+ - **Projects Distribution**: Multi-repository activity rankings.
51
+
52
+ ---
53
+
54
+ ## 🔒 Privacy & Demo Mode
55
+
56
+ FlowPilot includes built-in privacy protection (`isPrivacyMode`):
57
+ - All sensitive project names, prompt titles, task goals, and delivery conclusions are automatically frosted with smooth Gaussian blur filters (`blur(radius: 4.5)`).
58
+ - Ideal for public presentations, videos, and documentation screenshots.
59
+
60
+ ---
61
+
62
+ ## 🚀 Quick Start
63
+
64
+ ### 1. Build
65
+ ```bash
66
+ bash apps/macos-overlay/build.sh
67
+ ```
68
+ Binary output: `apps/macos-overlay/bin/FlowPilot`.
69
+
70
+ ### 2. Launch Daemon
71
+ ```bash
72
+ # Start background overlay widget
73
+ codex-flow overlay start
74
+
75
+ # Or run directly
76
+ ./apps/macos-overlay/bin/FlowPilot start &
77
+ ```
78
+
79
+ ### 3. CLI Commands
80
+ ```bash
81
+ # Check status
82
+ codex-flow overlay status
83
+
84
+ # Toggle expand / collapse
85
+ codex-flow overlay toggle
86
+ codex-flow overlay expand
87
+ codex-flow overlay collapse
88
+
89
+ # Switch tabs
90
+ codex-flow overlay tab inspector
91
+ codex-flow overlay tab history
92
+ codex-flow overlay tab analytics
93
+
94
+ # Inspect historical task
95
+ codex-flow overlay show 1
96
+
97
+ # Open analytics or history directly
98
+ codex-flow overlay stats 30
99
+ codex-flow overlay history
100
+
101
+ # Update telemetry data
102
+ codex-flow overlay update
103
+
104
+ # Stop overlay
105
+ codex-flow overlay stop
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 🖱 Mouse & Keyboard Interactions
111
+
112
+ | Action | Result |
113
+ | :--- | :--- |
114
+ | **Hover on Capsule (0.4s)** | Spring animation expands into full Glass Card |
115
+ | **Mouse Leave (0.8s)** | Smoothly collapses back to Capsule (when unpinned) |
116
+ | **Click Capsule / Header** | Toggle expanded/collapsed state immediately |
117
+ | **Click Pin Icon (`📌`)** | Lock widget permanently open on top of all windows |
118
+ | **Drag Anywhere** | Smooth desktop repositioning with screen-edge magnetic snap |
119
+ | **Right-Click Context Menu**| Quick access to View Switcher, Pin, Refresh, Terminal, and Quit |
120
+ | **Click "Copy Summary"** | Copies formatted run card to system clipboard |
121
+ | **Click "Console"** | Opens `codex-flow` interactive management menu in Terminal |
@@ -0,0 +1,123 @@
1
+ # macOS 原生悬浮窗 (Native Floating Widget)
2
+
3
+ <div align="center">
4
+
5
+ [ 简体中文 ](README.md) | [ English ](README.en.md)
6
+
7
+ </div>
8
+
9
+ `codex-flow-overlay` 是专为 macOS 设计的 100% 纯原生(基于 **SwiftUI + AppKit** 构建)桌面悬浮窗组件,深度集成了 `codex-flow usage` 的全部核心能力(实时巡检、配额监控、历史回溯、聚合效能分析)。
10
+
11
+ ---
12
+
13
+ <div align="center">
14
+ <img src="../../docs/assets/promo/flowpilot_promo_poster.png" alt="FlowPilot Native Overlay Showcase" width="100%" style="border-radius: 12px; margin: 12px 0;" />
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## ✨ 核心特性
20
+
21
+ - 🟢 **默认形态(灵动微胶囊 Micro Capsule)**:
22
+ - 直径 68px 的 Frosted Glass 毛玻璃拟物圆环(macOS `ultraThinMaterial`);
23
+ - 外圈动态彩虹渐变边框与实时呼吸灯;
24
+ - 实时状态角标(🟢 空闲/完成 · 🔵 任务运行中 · 🟠 异常/告警);
25
+ - 底部微缩显示最近任务的 Token 消耗徽章(如 `198.2k`);
26
+ - 边缘自动半收起(Half-Tuck)与屏幕防溢出磁吸贴靠。
27
+
28
+ - ⚡️ **展开态(全功能 Glass TabBar 监控台)**:
29
+ - **⚡️ Inspector(任务巡检)**:
30
+ - **Header**:项目名标签、Git 分支胶囊、任务状态徽章、Pin 锁定按钮与折叠按钮;
31
+ - **任务目标与交付结论卡**:智能提炼展示会话目标与交付成果;
32
+ - **KPI Rings**:3 组高精环形仪表盘(Time 耗时、Tokens 消耗、Cost 费用估算);
33
+ - **Rate Limits & Quota**:5m / 1h / 1d / 7d 账户配额消耗百分比进度与重置倒计时;
34
+ - **Token 细分条**:多色堆叠胶囊条,直观呈现 Prompt、Output、Cached 与 Reasoning 思考 Token 分布;
35
+ - **多 Agent 路由拓扑**:展示 Parent Agent(模型与 Reasoning 强度)及各个 Worker Subagents 的独立用量与状态;
36
+ - **技能与 MCP 标签**:自动标注调用的 Skills 与 MCP Server 工具;
37
+ - **历史查看态导航**:支持回溯任意历史任务,提供一键 `[⚡️ 查看最新]` 返回实时任务;
38
+ - **操作底栏**:一键复制 Summary 剪贴板(带 Copied 动画反馈)、唤起终端控制台、Pin 锁定常驻。
39
+ - **📜 History(历史回溯)**:
40
+ - 多维历史会话:支持按工程过滤、`All` / `Today` 切换与实时关键词检索;
41
+ - **会话手风琴 (Chat Accordion)**:按会话聚合多轮任务,展示会话总 Token 与最大并发;
42
+ - **轮次流水线 (Session Turns)**:精确展开每一轮次的时间、耗时、Worker 标签与配额消耗差值(`+1%` / `-1%`);
43
+ - 点击任意历史任务条目即刻在 Inspector 中展开深度详情。
44
+ - **📊 Analytics(效能看板)**:
45
+ - 集成聚合效能分析,支持 `7 Days` / `30 Days` 周期切换;
46
+ - 汇总总任务数(委派 vs 直接)、总活跃时长、总 Token 消耗与费用预估;
47
+ - **Cache Efficiency**:缓存命中率与节省 Token 统计;
48
+ - **Worker Offload**:Worker 算力委派比例仪表;
49
+ - **Model Breakdown**:各模型调用次数、Token 占比与角色标签(Parent/Worker);
50
+ - **Projects Distribution**:多仓库/多项目活跃度排行。
51
+
52
+ ---
53
+
54
+ ## 🔒 隐私脱敏与演示模式
55
+
56
+ 内置 `isPrivacyMode` 隐私保护:
57
+ - 所有敏感项目名、对话指令、任务目标与交付结论均自动采用平滑的高斯模糊滤镜(`blur(radius: 4.5)`)打码;
58
+ - 非常适合录屏演示、公开分享与制作文档插图。
59
+
60
+ ---
61
+
62
+ ## 🚀 快速使用
63
+
64
+ ### 1. 编译构建
65
+ ```bash
66
+ bash apps/macos-overlay/build.sh
67
+ ```
68
+ 编译产物位于 `apps/macos-overlay/bin/FlowPilot`。
69
+
70
+ ### 2. 启动悬浮窗守护进程
71
+ ```bash
72
+ # 启动后台常驻悬浮窗
73
+ codex-flow overlay start
74
+ # 或直接执行二进制
75
+ ./apps/macos-overlay/bin/FlowPilot start &
76
+ ```
77
+
78
+ ### 3. CLI 控制指令
79
+ ```bash
80
+ # 检查浮窗运行状态
81
+ codex-flow overlay status
82
+
83
+ # 切换展开 / 折叠
84
+ codex-flow overlay toggle
85
+ codex-flow overlay expand
86
+ codex-flow overlay collapse
87
+
88
+ # 切换视图选项卡
89
+ codex-flow overlay tab inspector
90
+ codex-flow overlay tab history
91
+ codex-flow overlay tab analytics
92
+
93
+ # 查看指定历史任务(支持 #1、#2 或 session_id)
94
+ codex-flow overlay show 1
95
+
96
+ # 打开统计看板(可指定天数)
97
+ codex-flow overlay stats 30
98
+
99
+ # 打开任务历史列表
100
+ codex-flow overlay history
101
+
102
+ # 推送并刷新最新 telemetry 运行数据
103
+ codex-flow overlay update
104
+
105
+ # 停止悬浮窗
106
+ codex-flow overlay stop
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 🖱 交互与快捷操作
112
+
113
+ | 动作 | 效果 |
114
+ |---|---|
115
+ | **光标移入气泡停留 0.4 秒** | 触发 Spring 弹性展开为 Telemetry 卡片 |
116
+ | **光标移出卡片区域** | 延迟 0.8 秒自动收起为圆形气泡(未 Pin 时) |
117
+ | **单击气泡 / 顶部折叠按钮** | 立即切换展开 / 收起状态 |
118
+ | **切换 Tab 选项卡** | 平滑切换 Inspector(任务详情)、History(任务列表)、Analytics(效能统计) |
119
+ | **点击 History 任务条目** | 立即在 Inspector 中回溯该任务的完整 Token、费用与配额明细 |
120
+ | **按住气泡拖拽** | 自由拖拽到屏幕任意角落(带边界贴靠保护) |
121
+ | **右键点击** | 弹出上下文菜单(Pin 锁定、折叠/展开、打开控制台、刷新、退出) |
122
+ | **点击「Copy Summary」** | 格式化复制任务摘要至系统剪贴板 |
123
+ | **点击「Console」** | 快速唤起终端打开 `codex-flow` 管理控制台 |
@@ -0,0 +1,126 @@
1
+ import Cocoa
2
+
3
+ enum OverlayGeometryActivity: Equatable {
4
+ case presentation
5
+ case snap
6
+ }
7
+
8
+ /// Small, deterministic state machine for the overlay's interaction/geometry
9
+ /// lifecycle. It deliberately knows nothing about NSWindow animation APIs.
10
+ ///
11
+ /// Invariants:
12
+ /// - only one geometry activity may own the window at a time;
13
+ /// - pointer interaction is independent of animation completion;
14
+ /// - presentation updates requested during drag/animation are coalesced and
15
+ /// replayed once the current owner releases the window.
16
+ struct OverlayRuntimeState {
17
+ private(set) var activeGeometry: OverlayGeometryActivity?
18
+ private(set) var pointerInteractionActive = false
19
+ private(set) var pendingPresentationUpdate = false
20
+
21
+ var isGeometryTransitioning: Bool { activeGeometry != nil }
22
+
23
+ mutating func beginPointerInteraction() -> Bool {
24
+ guard activeGeometry == nil, !pointerInteractionActive else { return false }
25
+ pointerInteractionActive = true
26
+ return true
27
+ }
28
+
29
+ mutating func endPointerInteraction() {
30
+ pointerInteractionActive = false
31
+ }
32
+
33
+ /// Returns true when the caller owns the geometry immediately and should
34
+ /// start the presentation transition now. Otherwise the request is coalesced.
35
+ mutating func requestPresentationGeometry() -> Bool {
36
+ guard activeGeometry == nil, !pointerInteractionActive else {
37
+ pendingPresentationUpdate = true
38
+ return false
39
+ }
40
+ activeGeometry = .presentation
41
+ return true
42
+ }
43
+
44
+ /// Snap is only valid after the pointer has been released. A snap is never
45
+ /// queued behind another geometry owner because a drag cannot begin while
46
+ /// geometry is transitioning.
47
+ mutating func beginSnapGeometry() -> Bool {
48
+ guard activeGeometry == nil, !pointerInteractionActive else { return false }
49
+ activeGeometry = .snap
50
+ return true
51
+ }
52
+
53
+ /// Releases the current geometry owner. Returns true when a coalesced
54
+ /// presentation update has now acquired ownership and should be run next.
55
+ mutating func completeGeometry() -> Bool {
56
+ activeGeometry = nil
57
+ return claimPendingPresentationIfIdle()
58
+ }
59
+
60
+ /// Used after a pointer interaction ends without a snap.
61
+ mutating func claimPendingPresentationIfIdle() -> Bool {
62
+ guard pendingPresentationUpdate,
63
+ activeGeometry == nil,
64
+ !pointerInteractionActive else { return false }
65
+ pendingPresentationUpdate = false
66
+ activeGeometry = .presentation
67
+ return true
68
+ }
69
+ }
70
+
71
+ /// Filters the rectangular 76x76 NSHostingView down to what the user can
72
+ /// actually see. This prevents the transparent host from behaving as a hover
73
+ /// target after the compact view changes shape.
74
+ enum OverlayCompactHitRegion {
75
+ static func contains(
76
+ _ point: NSPoint,
77
+ in hostBounds: NSRect,
78
+ expanded: Bool,
79
+ docked: Bool
80
+ ) -> Bool {
81
+ guard hostBounds.contains(point) else { return false }
82
+ if expanded { return true }
83
+
84
+ if docked {
85
+ let pillSize = NSSize(width: 44, height: 56)
86
+ let pillRect = NSRect(
87
+ x: hostBounds.maxX - pillSize.width,
88
+ y: hostBounds.midY - pillSize.height / 2,
89
+ width: pillSize.width,
90
+ height: pillSize.height
91
+ )
92
+ return pillRect.contains(point)
93
+ }
94
+
95
+ let center = NSPoint(x: hostBounds.midX, y: hostBounds.midY)
96
+ let dx = point.x - center.x
97
+ let dy = point.y - center.y
98
+ let radius: CGFloat = 31 // 58pt circle + a small allowance for its glow.
99
+ return dx * dx + dy * dy <= radius * radius
100
+ }
101
+ }
102
+
103
+ /// Synthetic AppKit tracking events may be emitted when a window resizes under
104
+ /// a stationary pointer. Require real pointer travel before hover behavior is
105
+ /// re-armed after a programmatic compacting/snap operation.
106
+ struct OverlayHoverGate {
107
+ private(set) var suppressedAt: NSPoint?
108
+ let rearmDistance: CGFloat
109
+
110
+ init(rearmDistance: CGFloat = 6) {
111
+ self.rearmDistance = rearmDistance
112
+ }
113
+
114
+ mutating func suppress(at point: NSPoint) {
115
+ suppressedAt = point
116
+ }
117
+
118
+ mutating func allowsHover(at point: NSPoint) -> Bool {
119
+ guard let anchor = suppressedAt else { return true }
120
+ let dx = point.x - anchor.x
121
+ let dy = point.y - anchor.y
122
+ guard dx * dx + dy * dy >= rearmDistance * rearmDistance else { return false }
123
+ suppressedAt = nil
124
+ return true
125
+ }
126
+ }
@@ -0,0 +1,82 @@
1
+ import Cocoa
2
+
3
+ /// Pure screen-geometry selection used by the overlay window controller.
4
+ ///
5
+ /// `NSWindow.screen` can be temporarily nil or point at a different display while
6
+ /// AppKit is animating/resizing a borderless panel. Geometry routing therefore
7
+ /// derives the destination display from global coordinates instead.
8
+ enum OverlayScreenGeometry {
9
+ static func bestVisibleFrame(for windowFrame: NSRect, among visibleFrames: [NSRect]) -> NSRect? {
10
+ guard !visibleFrames.isEmpty else { return nil }
11
+
12
+ let center = NSPoint(x: windowFrame.midX, y: windowFrame.midY)
13
+ if let containingCenter = visibleFrames.first(where: { $0.contains(center) }) {
14
+ return containingCenter
15
+ }
16
+
17
+ var bestIntersectionFrame: NSRect?
18
+ var bestIntersectionArea: CGFloat = 0
19
+ for candidate in visibleFrames {
20
+ let intersection = windowFrame.intersection(candidate)
21
+ guard !intersection.isNull, !intersection.isEmpty else { continue }
22
+ let area = intersection.width * intersection.height
23
+ if area > bestIntersectionArea {
24
+ bestIntersectionArea = area
25
+ bestIntersectionFrame = candidate
26
+ }
27
+ }
28
+ if let bestIntersectionFrame {
29
+ return bestIntersectionFrame
30
+ }
31
+
32
+ return visibleFrames.min {
33
+ distanceSquared(from: center, to: $0) < distanceSquared(from: center, to: $1)
34
+ }
35
+ }
36
+
37
+ /// Presentation expand/collapse is anchored at the window's top-trailing
38
+ /// corner. A wide panel may straddle two displays while its top-trailing
39
+ /// anchor still clearly belongs to the display where the compact bubble
40
+ /// lives. Using the panel center here can move the bubble to the neighbor.
41
+ static func presentationVisibleFrame(for windowFrame: NSRect, among visibleFrames: [NSRect]) -> NSRect? {
42
+ guard !visibleFrames.isEmpty else { return nil }
43
+ let anchor = topTrailingAnchor(for: windowFrame)
44
+ return visibleFrame(containing: anchor, among: visibleFrames)
45
+ }
46
+
47
+ static func topTrailingAnchor(for frame: NSRect, inset: CGFloat = 1) -> NSPoint {
48
+ NSPoint(
49
+ x: max(frame.minX, frame.maxX - inset),
50
+ y: max(frame.minY, frame.maxY - inset)
51
+ )
52
+ }
53
+
54
+ static func visibleFrame(containing point: NSPoint, among visibleFrames: [NSRect]) -> NSRect? {
55
+ guard !visibleFrames.isEmpty else { return nil }
56
+ if let containing = visibleFrames.first(where: { $0.contains(point) }) {
57
+ return containing
58
+ }
59
+ return visibleFrames.min {
60
+ distanceSquared(from: point, to: $0) < distanceSquared(from: point, to: $1)
61
+ }
62
+ }
63
+
64
+ static func clamp(_ origin: NSPoint, windowSize: NSSize, to visibleFrame: NSRect, margin: CGFloat = 0) -> NSPoint {
65
+ let minX = visibleFrame.minX + margin
66
+ let maxX = visibleFrame.maxX - windowSize.width - margin
67
+ let minY = visibleFrame.minY + margin
68
+ let maxY = visibleFrame.maxY - windowSize.height - margin
69
+ return NSPoint(
70
+ x: max(minX, min(origin.x, maxX)),
71
+ y: max(minY, min(origin.y, maxY))
72
+ )
73
+ }
74
+
75
+ private static func distanceSquared(from point: NSPoint, to rect: NSRect) -> CGFloat {
76
+ let x = max(rect.minX, min(point.x, rect.maxX))
77
+ let y = max(rect.minY, min(point.y, rect.maxY))
78
+ let dx = point.x - x
79
+ let dy = point.y - y
80
+ return dx * dx + dy * dy
81
+ }
82
+ }