easy-codex 0.1.1 → 0.1.3
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.
package/README.md
CHANGED
|
@@ -51,10 +51,10 @@ macOS 14+ 安装后运行:
|
|
|
51
51
|
easy-codex overlay
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
独立 AppKit 浮窗在 Codex 位于前台时显示,切到其他应用、最小化或关闭 Codex 窗口后隐藏。默认位于 Codex
|
|
54
|
+
独立 AppKit 浮窗在 Codex 位于前台时显示,切到其他应用、最小化或关闭 Codex 窗口后隐藏。默认位于 Codex 窗口左下角头像上方;拖动可校准位置,偏移量保存在本机。它按窗口边界跟随,不自动识别头像;侧栏布局变化后可再次拖动。首版跟随最前面的一个 Codex 普通窗口。
|
|
55
55
|
|
|
56
56
|
- 圆环长度表示剩余百分比:大于 50% 绿色,20%–50% 橙色,低于 20% 红色。
|
|
57
|
-
-
|
|
57
|
+
- 浮窗为头像大小(32×32),背景透明,无底板和阴影。圆环只显示百分比,无数据时显示 `—`,不显示窗口时长或状态文字;时长与状态见悬停提示和菜单。
|
|
58
58
|
- 未知或过期数据为灰色;失败后的过期说明见悬停提示。已到重置时间时也先标为过期,等待重新查询,不擅自恢复满额。
|
|
59
59
|
- 浮窗可见时每 60 秒刷新,查询不会重叠;回到前台且距离上次查询超过 60 秒会刷新。
|
|
60
60
|
- 默认优先 codex 额度桶,显示其中剩余比例最低的窗口;右键可切换其他窗口,不把各窗口相加。
|
package/native/Overlay.swift
CHANGED
|
@@ -46,8 +46,8 @@ func appKitRect(_ rect: CGRect, primaryHeight: CGFloat) -> CGRect {
|
|
|
46
46
|
CGRect(x: rect.minX, y: primaryHeight - rect.maxY, width: rect.width, height: rect.height)
|
|
47
47
|
}
|
|
48
48
|
func ringOrigin(in rect: CGRect, inset: CGPoint) -> CGPoint {
|
|
49
|
-
CGPoint(x: max(rect.minX, min(rect.maxX -
|
|
50
|
-
y: max(rect.minY, min(rect.maxY -
|
|
49
|
+
CGPoint(x: max(rect.minX, min(rect.maxX - 32, rect.minX + inset.x)),
|
|
50
|
+
y: max(rect.minY, min(rect.maxY - 32, rect.minY + inset.y)))
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
@MainActor
|
|
@@ -81,28 +81,28 @@ final class RingView: NSView {
|
|
|
81
81
|
|
|
82
82
|
override func draw(_ dirtyRect: NSRect) {
|
|
83
83
|
let center = CGPoint(x: bounds.midX, y: bounds.midY)
|
|
84
|
-
let track = NSBezierPath(ovalIn: CGRect(x: center.x -
|
|
85
|
-
track.lineWidth =
|
|
84
|
+
let track = NSBezierPath(ovalIn: CGRect(x: center.x - 14, y: center.y - 14, width: 28, height: 28))
|
|
85
|
+
track.lineWidth = 2
|
|
86
86
|
NSColor.labelColor.withAlphaComponent(0.12).setStroke()
|
|
87
87
|
track.stroke()
|
|
88
88
|
let state = RingState.resolve(remaining, stale: stale)
|
|
89
89
|
if let remaining, remaining > 0, !stale {
|
|
90
90
|
let arc = NSBezierPath()
|
|
91
|
-
arc.lineWidth =
|
|
91
|
+
arc.lineWidth = 2
|
|
92
92
|
arc.lineCapStyle = .round
|
|
93
|
-
arc.appendArc(withCenter: center, radius:
|
|
93
|
+
arc.appendArc(withCenter: center, radius: 14, startAngle: 90,
|
|
94
94
|
endAngle: 90 - CGFloat(min(100, max(0, remaining))) * 3.6, clockwise: true)
|
|
95
95
|
state.color.setStroke()
|
|
96
96
|
arc.stroke()
|
|
97
97
|
}
|
|
98
98
|
let text = remaining.map { "\(Int($0.rounded()))%" } ?? "—"
|
|
99
|
-
drawText(text, y:
|
|
99
|
+
drawText(text, y: 10, font: .monospacedDigitSystemFont(ofSize: 9, weight: .semibold), color: state.color)
|
|
100
100
|
}
|
|
101
101
|
private func drawText(_ text: String, y: CGFloat, font: NSFont, color: NSColor) {
|
|
102
102
|
let style = NSMutableParagraphStyle()
|
|
103
103
|
style.alignment = .center
|
|
104
104
|
style.lineBreakMode = .byTruncatingTail
|
|
105
|
-
(text as NSString).draw(in: CGRect(x:
|
|
105
|
+
(text as NSString).draw(in: CGRect(x: 2, y: y, width: bounds.width - 4, height: 12),
|
|
106
106
|
withAttributes: [.font: font, .foregroundColor: color, .paragraphStyle: style])
|
|
107
107
|
}
|
|
108
108
|
override func mouseDown(with event: NSEvent) {
|
|
@@ -127,9 +127,9 @@ final class RingView: NSView {
|
|
|
127
127
|
|
|
128
128
|
@MainActor
|
|
129
129
|
final class Overlay: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
|
130
|
-
let panel = NSPanel(contentRect: CGRect(x: 0, y: 0, width:
|
|
130
|
+
let panel = NSPanel(contentRect: CGRect(x: 0, y: 0, width: 32, height: 32),
|
|
131
131
|
styleMask: [.borderless, .nonactivatingPanel], backing: .buffered, defer: false)
|
|
132
|
-
let ring = RingView(frame: CGRect(x: 0, y: 0, width:
|
|
132
|
+
let ring = RingView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
|
|
133
133
|
var status: NSStatusItem?
|
|
134
134
|
var timer: Timer?
|
|
135
135
|
var query: Process?
|
|
@@ -150,8 +150,8 @@ final class Overlay: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
|
|
150
150
|
node = args[1]; script = args[2]; searchPath = args[3]
|
|
151
151
|
codexHome = args.count > 4 ? args[4] : ""
|
|
152
152
|
let prefs = UserDefaults.standard
|
|
153
|
-
if prefs.object(forKey: "
|
|
154
|
-
inset = CGPoint(x: prefs.double(forKey: "
|
|
153
|
+
if prefs.object(forKey: "leftInset") != nil {
|
|
154
|
+
inset = CGPoint(x: prefs.double(forKey: "leftInset"), y: prefs.double(forKey: "bottomInset"))
|
|
155
155
|
}
|
|
156
156
|
selectedID = prefs.string(forKey: "selectedWindow")
|
|
157
157
|
panel.isOpaque = false
|
|
@@ -165,8 +165,8 @@ final class Overlay: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
|
|
165
165
|
ring.refresh = { [weak self] in self?.refresh() }
|
|
166
166
|
ring.moved = { [weak self] point in
|
|
167
167
|
guard let self, let rect = self.lastRect else { return }
|
|
168
|
-
self.inset = CGPoint(x:
|
|
169
|
-
prefs.set(self.inset.x, forKey: "
|
|
168
|
+
self.inset = CGPoint(x: point.x - rect.minX, y: point.y - rect.minY)
|
|
169
|
+
prefs.set(self.inset.x, forKey: "leftInset")
|
|
170
170
|
prefs.set(self.inset.y, forKey: "bottomInset")
|
|
171
171
|
}
|
|
172
172
|
ring.setAccessibilityElement(true)
|
|
@@ -275,7 +275,7 @@ final class Overlay: NSObject, NSApplicationDelegate, NSMenuDelegate {
|
|
|
275
275
|
}
|
|
276
276
|
@objc func resetPosition() {
|
|
277
277
|
inset = CGPoint(x: 10, y: 74)
|
|
278
|
-
UserDefaults.standard.removeObject(forKey: "
|
|
278
|
+
UserDefaults.standard.removeObject(forKey: "leftInset")
|
|
279
279
|
UserDefaults.standard.removeObject(forKey: "bottomInset")
|
|
280
280
|
tick()
|
|
281
281
|
}
|
|
@@ -299,8 +299,9 @@ struct Main {
|
|
|
299
299
|
assert(RingState.resolve(99, stale: true) == .unknown)
|
|
300
300
|
assert(appKitRect(CGRect(x: -900, y: 100, width: 800, height: 600), primaryHeight: 900).origin == CGPoint(x: -900, y: 200))
|
|
301
301
|
let bounds = CGRect(x: 100, y: 100, width: 800, height: 600)
|
|
302
|
-
assert(ringOrigin(in: bounds, inset: CGPoint(x: 10, y: 74)) == CGPoint(x:
|
|
303
|
-
assert(ringOrigin(in: bounds, inset: CGPoint(x: -100, y: -100)) == CGPoint(x:
|
|
302
|
+
assert(ringOrigin(in: bounds, inset: CGPoint(x: 10, y: 74)) == CGPoint(x: 110, y: 174))
|
|
303
|
+
assert(ringOrigin(in: bounds, inset: CGPoint(x: -100, y: -100)) == CGPoint(x: 100, y: 100))
|
|
304
|
+
assert(ringOrigin(in: bounds, inset: CGPoint(x: 9999, y: 9999)) == CGPoint(x: 868, y: 668))
|
|
304
305
|
print("Swift 圆环颜色与窗口坐标检查通过")
|
|
305
306
|
return
|
|
306
307
|
}
|
|
@@ -318,7 +319,7 @@ struct Main {
|
|
|
318
319
|
NSGraphicsContext.saveGraphicsState()
|
|
319
320
|
let transform = AffineTransform(translationByX: CGFloat(index * 84 + 4), byY: 8)
|
|
320
321
|
(transform as NSAffineTransform).concat()
|
|
321
|
-
let view = RingView(frame: CGRect(x: 0, y: 0, width:
|
|
322
|
+
let view = RingView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
|
|
322
323
|
view.remaining = value < 0 ? nil : value; view.stale = value < 0
|
|
323
324
|
view.caption = value < 0 ? "额度未知" : "7天"
|
|
324
325
|
view.draw(view.bounds)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<key>CFBundleIdentifier</key><string>com.unionschool.easy-codex.overlay</string>
|
|
6
6
|
<key>CFBundleName</key><string>Easy Codex Overlay</string>
|
|
7
7
|
<key>CFBundlePackageType</key><string>APPL</string>
|
|
8
|
-
<key>CFBundleShortVersionString</key><string>0.1.
|
|
8
|
+
<key>CFBundleShortVersionString</key><string>0.1.3</string>
|
|
9
9
|
<key>LSMinimumSystemVersion</key><string>14.0</string>
|
|
10
10
|
<key>LSUIElement</key><true/>
|
|
11
11
|
<key>NSHighResolutionCapable</key><true/>
|
|
Binary file
|