Skip to content
Open
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
123 changes: 123 additions & 0 deletions BifcodePackage/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions BifcodePackage/Sources/BifcodeFeature/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ public struct ContentView: View {
@AppStorage("fontSize") private var fontSize: Double = 14
@AppStorage("selectedTheme") private var selectedThemeRaw: String = EditorThemeOption.atomOneDark.rawValue
@AppStorage("themeMode") private var themeModeRaw: String = ThemeMode.dark.rawValue
@AppStorage("showWatermark") private var showWatermark: Bool = true
@AppStorage("watermarkText") private var watermarkText: String = "bifcode"

private var layout: WindowLayout {
WindowLayout(rawValue: windowLayoutRaw) ?? .horizontal
Expand Down Expand Up @@ -223,6 +225,8 @@ public struct ContentView: View {
fontSize: fontSize,
theme: selectedTheme.editorTheme,
themeMode: themeMode,
showWatermark: showWatermark,
watermarkText: watermarkText,
panelWidth: panelWidth,
doPanelHeight: doPanelHeight,
dontPanelHeight: dontPanelHeight
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,20 @@ public extension Color {
}

// MARK: - Toast Colors

/// Toast success background - uses semantic green
static let toastSuccess = Color("IndicatorDo", bundle: .module)

/// Toast error background - uses semantic red
static let toastError = Color("IndicatorDont", bundle: .module)

// MARK: - Watermark Colors

/// Watermark text color for exported images
/// Semi-transparent gray that works on both light and dark backgrounds
static var watermarkText: Color {
Color.primary.opacity(0.35)
}

// MARK: - Theme Mode Aware Colors

Expand Down
46 changes: 35 additions & 11 deletions BifcodePackage/Sources/BifcodeFeature/Views/ExportView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,17 @@ struct ExportView: View {

/// The current theme mode for window styling.
let themeMode: ThemeMode


// Watermark settings

/// Whether to show the watermark in exports.
let showWatermark: Bool

/// The text to display as watermark.
let watermarkText: String

// Panel dimensions for export

/// Width of each panel in points.
let panelWidth: CGFloat

Expand All @@ -81,18 +89,30 @@ struct ExportView: View {
let dontPanelHeight: CGFloat

var body: some View {
Group {
if layout == .horizontal {
HStack(alignment: .top, spacing: 24) {
panels
}
} else {
VStack(spacing: 24) {
panels
ZStack(alignment: .bottom) {
Group {
if layout == .horizontal {
HStack(alignment: .top, spacing: 24) {
panels
}
} else {
VStack(spacing: 24) {
panels
}
}
}
.padding(24)

// Watermark overlay at bottom-center
if showWatermark, !watermarkText.isEmpty {
Text(watermarkText)
.font(.system(size: 11, weight: .medium))
.foregroundStyle(Color.watermarkText)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.padding(.bottom, 8)
}
}
.padding(24)
.background(Color.clear)
}

Expand Down Expand Up @@ -160,6 +180,8 @@ struct ExportView: View {
fontSize: 14,
theme: .atomOneDark,
themeMode: .dark,
showWatermark: true,
watermarkText: "bifcode",
panelWidth: 350,
doPanelHeight: 120,
dontPanelHeight: 120
Expand Down Expand Up @@ -195,6 +217,8 @@ struct ExportView: View {
fontSize: 14,
theme: .atomOneDark,
themeMode: .dark,
showWatermark: true,
watermarkText: "bifcode",
panelWidth: 400,
doPanelHeight: 120,
dontPanelHeight: 120
Expand Down
122 changes: 85 additions & 37 deletions BifcodePackage/Sources/BifcodeFeature/Views/ToolBarView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ public struct ToolBarView: View {
@AppStorage("fontSize") private var fontSize: Double = 14
@AppStorage("selectedTheme") private var selectedThemeRaw: String = EditorThemeOption.atomOneDark.rawValue
@AppStorage("themeMode") private var themeModeRaw: String = ThemeMode.dark.rawValue

@AppStorage("selectedLanguage") private var selectedLanguageRaw: String = "swift"

// Watermark settings
@AppStorage("showWatermark") private var showWatermark: Bool = true
@AppStorage("watermarkText") private var watermarkText: String = "bifcode"

@Environment(\.accessibilityReduceMotion) private var reduceMotion

Expand Down Expand Up @@ -523,45 +527,89 @@ public struct ToolBarView: View {
private let storeService = StoreService()

private var exportButton: some View {
VStack(spacing: 12) {
// Primary Export Action - HIG: Use .prominent for key actions
Button { onExport() } label: {
Image(systemName: "square.and.arrow.up")
.font(.system(size: 22, weight: .medium))
.frame(width: 48, height: 48)
}
.buttonStyle(.borderedProminent)
.clipShape(Circle())
.scaleEffect(isExportButtonHovered && !isExportDisabled ? 1.05 : 1.0)
.animation(reduceMotion ? nil : .easeInOut(duration: 0.15), value: isExportButtonHovered)
.onHover { hovering in
isExportButtonHovered = hovering
}
.disabled(isExportDisabled)
.help("Export as PNG")
.accessibilityLabel("Export as PNG")
.accessibilityHint(isExportDisabled
? "Disabled: Add code to enable export"
: "Save comparison image to selected folder")

// Secondary Actions
VStack(spacing: 8) {
Button {
chooseSaveLocation()
} label: {
Label("Location", systemImage: "folder")
.font(.caption)
HStack(alignment: .top, spacing: 16) {
// Watermark Settings
watermarkSettingsView()

// Export Actions
VStack(spacing: 12) {
// Primary Export Action - HIG: Use .prominent for key actions
Button { onExport() } label: {
Image(systemName: "square.and.arrow.up")
.font(.system(size: 22, weight: .medium))
.frame(width: 48, height: 48)
}
.buttonStyle(.borderedProminent)
.clipShape(Circle())
.scaleEffect(isExportButtonHovered && !isExportDisabled ? 1.05 : 1.0)
.animation(reduceMotion ? nil : .easeInOut(duration: 0.15), value: isExportButtonHovered)
.onHover { hovering in
isExportButtonHovered = hovering
}
.disabled(isExportDisabled)
.help("Export as PNG")
.accessibilityLabel("Export as PNG")
.accessibilityHint(isExportDisabled
? "Disabled: Add code to enable export"
: "Save comparison image to selected folder")

// Secondary Actions
VStack(spacing: 8) {
Button {
chooseSaveLocation()
} label: {
Label("Location", systemImage: "folder")
.font(.caption)
}
.buttonStyle(.borderless)
.help("Choose save location")
.accessibilityLabel("Choose Save Location")
.accessibilityHint("Select folder for exported images")

storeButton
.padding(.top, 32)
}
.buttonStyle(.borderless)
.help("Choose save location")
.accessibilityLabel("Choose Save Location")
.accessibilityHint("Select folder for exported images")

storeButton
.padding(.top, 32)
}
.frame(minWidth: 80)
}
.frame(minWidth: 80)
}

@ViewBuilder
private func watermarkSettingsView() -> some View {
VStack(alignment: .leading, spacing: 12) {
// Section Header
Text("Watermark")
.font(.subheadline.weight(.semibold))
.foregroundStyle(Color.sectionHeader)

// Enable/Disable Toggle
Toggle("Show", isOn: $showWatermark)
.toggleStyle(.checkbox)
.font(.caption)
.accessibilityLabel("Show Watermark")
.accessibilityHint("Toggle watermark visibility in exports")

// Watermark Text Field
VStack(alignment: .leading, spacing: 4) {
Text("Text")
.font(.caption)
.foregroundStyle(Color.tertiaryText)
TextField("bifcode", text: $watermarkText)
.textFieldStyle(.roundedBorder)
.frame(width: 100)
.disabled(!showWatermark)
.onChange(of: watermarkText) { _, newValue in
if newValue.count > 30 {
watermarkText = String(newValue.prefix(30))
}
}
.accessibilityLabel("Watermark Text")
.accessibilityHint(showWatermark
? "Custom text shown at bottom of exports"
: "Disabled: Enable watermark to edit")
}
}
.fixedSize(horizontal: true, vertical: false)
}

private var storeButton: some View {
Expand Down