From 8b2726396222b177fecc155e5b4ac93fa9cc80e0 Mon Sep 17 00:00:00 2001 From: TZGyn Date: Sun, 23 Aug 2026 14:00:00 +0800 Subject: [PATCH] Make stroke thickness per-stroke; restore Clear button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each drawn stroke now stores its own width and color, so changing the thickness slider no longer retroactively resizes existing strokes. The Clear button is back in the draw toolbar. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-0731 --- Sources/Kapture/ResultWindow.swift | 42 +++++++++++++++++++++--------- build-app.sh | 4 +-- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Sources/Kapture/ResultWindow.swift b/Sources/Kapture/ResultWindow.swift index 559bf0b..0530f92 100644 --- a/Sources/Kapture/ResultWindow.swift +++ b/Sources/Kapture/ResultWindow.swift @@ -91,9 +91,9 @@ final class CaptureModel: ObservableObject { /// Drag selection rect in IMAGE point space (nil = none) @Published var selection: CGRect? /// Freehand drawing strokes in IMAGE point space - @Published var strokes: [[CGPoint]] = [] + @Published var strokes: [Stroke] = [] /// Redo stack of removed strokes - @Published var redoStack: [[CGPoint]] = [] + @Published var redoStack: [Stroke] = [] /// Whether the pen is active (drag draws instead of selecting) @Published var isDrawing = false /// Current drawing color @@ -101,6 +101,12 @@ final class CaptureModel: ObservableObject { /// Stroke thickness in points (image space) @Published var strokeWidth: CGFloat = 3.0 + struct Stroke { + let points: [CGPoint] + let width: CGFloat + let color: NSColor + } + enum CaptureMode { case screenshot case text @@ -143,15 +149,15 @@ final class CaptureModel: ObservableObject { let ctx = NSGraphicsContext.current!.cgContext // CGImage draws with a flipped Y in NSGraphicsContext ctx.draw(image, in: NSRect(origin: .zero, size: size)) - drawColor.setStroke() - ctx.setLineWidth(strokeWidth) ctx.setLineCap(.round) ctx.setLineJoin(.round) for stroke in strokes { - guard stroke.count > 1 else { continue } + guard stroke.points.count > 1 else { continue } + stroke.color.setStroke() + ctx.setLineWidth(stroke.width) ctx.beginPath() - ctx.move(to: stroke[0]) - for point in stroke.dropFirst() { + ctx.move(to: stroke.points[0]) + for point in stroke.points.dropFirst() { ctx.addLine(to: point) } ctx.strokePath() @@ -162,7 +168,7 @@ final class CaptureModel: ObservableObject { func addStroke(_ points: [CGPoint]) { guard points.count > 1 else { return } - strokes.append(points) + strokes.append(Stroke(points: points, width: strokeWidth, color: drawColor)) redoStack = [] } @@ -387,6 +393,16 @@ struct ResultView: View { .disabled(!model.canRedo) .keyboardShortcut("z", modifiers: [.command, .shift]) .help("Redo stroke (⌘⇧Z)") + + Button { + model.clearAnnotations() + } label: { + Label("Clear", systemImage: "eraser") + } + .buttonStyle(.bordered) + .controlSize(.small) + .disabled(model.strokes.isEmpty) + .help("Remove all drawn annotations") } .padding(.horizontal, 14) .padding(.vertical, 8) @@ -490,15 +506,15 @@ struct ScreenshotPane: View { // Completed strokes Canvas { context, _ in - var path = Path() for stroke in model.strokes { - guard stroke.count > 1 else { continue } - path.move(to: CGPoint(x: origin.x + stroke[0].x * scale, y: origin.y + stroke[0].y * scale)) - for point in stroke.dropFirst() { + guard stroke.points.count > 1 else { continue } + var path = Path() + path.move(to: CGPoint(x: origin.x + stroke.points[0].x * scale, y: origin.y + stroke.points[0].y * scale)) + for point in stroke.points.dropFirst() { path.addLine(to: CGPoint(x: origin.x + point.x * scale, y: origin.y + point.y * scale)) } + context.stroke(path, with: .color(Color(nsColor: stroke.color)), lineWidth: stroke.width) } - context.stroke(path, with: .color(Color(nsColor: model.drawColor)), lineWidth: model.strokeWidth) } .allowsHitTesting(false) diff --git a/build-app.sh b/build-app.sh index 13b4921..84b7f90 100755 --- a/build-app.sh +++ b/build-app.sh @@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST' CFBundleIconFile Icon CFBundleShortVersionString - 1.4.1 + 1.4.2 CFBundleVersion - 41 + 42 LSUIElement NSHighResolutionCapable