Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ relay-server/.env
relay-server/.env.*
relay-server/*.p8
relay-server/k8s/secrets.yaml
.playwright-mcp
12 changes: 10 additions & 2 deletions Packages/Sources/RxCodeChatKit/PlanSheetView.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import SwiftUI
import RxCodeCore
import Combine
#if os(macOS)
import AppKit
import Combine
#else
import UIKit
#endif

/// Test-only inspection emissary used by `PlanSheetView` so ViewInspector tests
/// can observe @State transitions (composer reveal, feedback text). At runtime
Expand Down Expand Up @@ -97,7 +100,9 @@ public struct PlanSheetView: View {
Divider()
footer
}
#if os(macOS)
.frame(minWidth: 640, idealWidth: 760, minHeight: 480, idealHeight: 640)
#endif
.background(ClaudeTheme.background)
.onReceive(inspection.notice) { inspection.visit(self, $0) }
}
Expand Down Expand Up @@ -307,9 +312,13 @@ public struct PlanSheetView: View {
}

private func copyMarkdown() {
#if os(macOS)
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(plan.markdown, forType: .string)
#else
UIPasteboard.general.string = plan.markdown
#endif
}

// MARK: - Button helper
Expand Down Expand Up @@ -366,4 +375,3 @@ public struct PlanSheetView: View {
.joined(separator: "-")
}
}
#endif
4 changes: 3 additions & 1 deletion Packages/Sources/RxCodeCore/Models/RateLimitUsage.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Foundation

/// Rate limit usage data passed through ChatBridge to avoid direct RateLimitService dependency in RxCodeChatKit.
public struct RateLimitUsage: Sendable {
///
/// `Codable` so it can travel over the mobile sync protocol inside a snapshot.
public struct RateLimitUsage: Sendable, Codable, Equatable {
public let fiveHourPercent: Double
public let sevenDayPercent: Double
public let twentyFourHourPercent: Double?
Expand Down
54 changes: 54 additions & 0 deletions Packages/Sources/RxCodeCore/Utilities/MarkdownStripping.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation

/// Convert lightweight Markdown to a single line of plain text suitable for
/// notification banners. APNs alerts and the macOS local banner render raw
/// Markdown syntax literally (`**bold**`, `` `code` ``, `[text](url)`,
/// `# heading`) rather than formatting it, so the syntax must be removed before
/// the text is shown.
///
/// This is a best-effort, heuristic stripper for short summary text — not a full
/// CommonMark parser. It drops the syntax characters and collapses the result
/// into a single trimmed line. Inline lookarounds keep `snake_case`
/// identifiers and arithmetic like `2 * 3` intact.
public func stripMarkdown(_ text: String) -> String {
var s = text

func replace(_ pattern: String, _ template: String) {
s = s.replacingOccurrences(
of: pattern,
with: template,
options: [.regularExpression]
)
}

// Fenced code block fences (``` or ```lang) — drop the fence line, keep code.
replace("(?m)^[ \\t]*```.*$", "")
// Images: ![alt](url) -> alt (before links — the syntax overlaps).
replace("!\\[([^\\]]*)\\]\\([^)]*\\)", "$1")
// Links: [text](url) -> text
replace("\\[([^\\]]*)\\]\\([^)]*\\)", "$1")
// ATX headings: leading #'s
replace("(?m)^[ \\t]*#{1,6}[ \\t]+", "")
// Blockquote markers
replace("(?m)^[ \\t]*>[ \\t]?", "")
// Horizontal rules
replace("(?m)^[ \\t]*[-*_]{3,}[ \\t]*$", "")
// Unordered list markers
replace("(?m)^[ \\t]*[-*+][ \\t]+", "")
// Ordered list markers
replace("(?m)^[ \\t]*\\d+\\.[ \\t]+", "")
// Strikethrough ~~text~~
replace("~~([\\s\\S]+?)~~", "$1")
// Bold: **text** and __text__
replace("\\*\\*([\\s\\S]+?)\\*\\*", "$1")
replace("(?<![A-Za-z0-9])__([\\s\\S]+?)__(?![A-Za-z0-9])", "$1")
// Italic: *text* and _text_ (lookarounds keep snake_case / a*b intact)
replace("(?<![*\\w])\\*([^*\\n]+?)\\*(?![*\\w])", "$1")
replace("(?<![A-Za-z0-9_])_([^_\\n]+?)_(?![A-Za-z0-9_])", "$1")
// Inline code backticks
replace("`", "")
// Collapse every whitespace run (incl. newlines) into a single space.
replace("\\s+", " ")

return s.trimmingCharacters(in: .whitespacesAndNewlines)
}
10 changes: 9 additions & 1 deletion Packages/Sources/RxCodeCore/WindowState.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import Foundation
import SwiftUI

// MARK: - AppStorageKeys

/// Keys for app-wide UserDefaults state read directly via `@AppStorage` in views.
public enum AppStorageKeys {
/// Persisted visibility of the right inspector sidebar. Views read this
/// directly with `@AppStorage` so the panel can be reliably toggled.
public static let showRightSidebar = "showRightSidebar"
}

// MARK: - InspectorTab

public enum InspectorTab: String, CaseIterable {
Expand Down Expand Up @@ -134,7 +143,6 @@ public final class WindowState {
// MARK: - UI State

public var interactiveTerminal: InteractiveTerminalState?
public var showInspector: Bool = false
/// Persisted to UserDefaults so the inspector remembers whether the user
/// last looked at Review or Inspector. Defaults to Review on first launch.
public var inspectorMode: InspectorMode = WindowState.loadInspectorMode() {
Expand Down
Loading