mirror of
https://github.com/h3pdesign/Neon-Vision-Editor
synced 2026-04-21 13:27:16 +00:00
Prepare v0.5.3 release updates and UI polish
This commit is contained in:
parent
2ab0af9820
commit
c655cc9917
8 changed files with 298 additions and 175 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -4,6 +4,25 @@ All notable changes to **Neon Vision Editor** are documented in this file.
|
|||
|
||||
The format follows *Keep a Changelog*. Versions use semantic versioning with prerelease tags.
|
||||
|
||||
## [v0.5.3] - 2026-03-10
|
||||
|
||||
### Added
|
||||
- Added a new high-readability colorful light theme preset: `Prism Daylight` (also selectable while app appearance is set to dark).
|
||||
- Added double-click-to-close behavior for tabs on macOS tab strips.
|
||||
- Added split editor settings sections (`Basics` / `Behavior`) to reduce scrolling in the Editor tab.
|
||||
|
||||
### Improved
|
||||
- Improved custom theme vibrancy by applying the vivid neon syntax profile to `Custom`, so syntax colors remain bright and saturated.
|
||||
- Improved Cyber Lime readability in light mode by reducing overly bright green token intensity and switching to a blue cursor accent.
|
||||
- Improved toolbar symbol color options on macOS with clearer separation between `Dark Gray` and `Black`, plus near-white rendering in dark mode for both options.
|
||||
- Improved translucent macOS toolbar consistency by enforcing `0.8` opacity for toolbar surfaces in translucency mode.
|
||||
|
||||
### Fixed
|
||||
- Fixed toolbar-symbol contrast edge cases in dark mode where gray/black variants could appear too similar.
|
||||
|
||||
### Release
|
||||
- Notarized release published via `scripts/release_all.sh v0.5.3 notarized`.
|
||||
|
||||
## [v0.5.2] - 2026-03-09
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@
|
|||
CODE_SIGNING_ALLOWED = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 489;
|
||||
CURRENT_PROJECT_VERSION = 490;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = CS727NF72U;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
|
|
@ -444,7 +444,7 @@
|
|||
CODE_SIGNING_ALLOWED = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 489;
|
||||
CURRENT_PROJECT_VERSION = 490;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = CS727NF72U;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,24 @@ extension ContentView {
|
|||
"AI Provider for Code Completion"
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private var macToolbarSymbolColor: Color {
|
||||
let isDarkMode = colorScheme == .dark
|
||||
switch toolbarSymbolsColorMacRaw {
|
||||
case "black":
|
||||
return isDarkMode
|
||||
? Color(.sRGB, white: 0.94, opacity: 1.0)
|
||||
: .black
|
||||
case "darkGray":
|
||||
return isDarkMode
|
||||
? Color(.sRGB, white: 0.84, opacity: 1.0)
|
||||
: Color(.sRGB, white: 0.40, opacity: 1.0)
|
||||
default:
|
||||
return NeonUIStyle.accentBlue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
private var iOSToolbarChromeStyle: GlassChromeStyle { .single }
|
||||
private var iOSToolbarTintColor: Color {
|
||||
|
|
@ -941,19 +959,19 @@ extension ContentView {
|
|||
ToolbarItemGroup(placement: .primaryAction) {
|
||||
Button(action: { openFileFromToolbar() }) {
|
||||
Label("Open", systemImage: "folder")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Open File… (Cmd+O)")
|
||||
|
||||
Button(action: { viewModel.addNewTab() }) {
|
||||
Label("New Tab", systemImage: "plus.square.on.square")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("New Tab (Cmd+T)")
|
||||
|
||||
Button(action: { requestCloseAllTabsFromToolbar() }) {
|
||||
Label("Close All Tabs", systemImage: "xmark.square")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Close All Tabs")
|
||||
|
||||
|
|
@ -961,7 +979,7 @@ extension ContentView {
|
|||
saveCurrentTabFromToolbar()
|
||||
}) {
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.disabled(viewModel.selectedTab == nil)
|
||||
.help("Save File (Cmd+S)")
|
||||
|
|
@ -970,7 +988,7 @@ extension ContentView {
|
|||
showFindReplace = true
|
||||
}) {
|
||||
Label("Find", systemImage: "magnifyingglass")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Find & Replace (Cmd+F)")
|
||||
|
||||
|
|
@ -978,7 +996,7 @@ extension ContentView {
|
|||
openSettings()
|
||||
}) {
|
||||
Label("Settings", systemImage: "gearshape")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Settings")
|
||||
}
|
||||
|
|
@ -1036,7 +1054,7 @@ extension ContentView {
|
|||
toggleMarkdownPreviewFromToolbar()
|
||||
}) {
|
||||
Label("Markdown Preview", systemImage: showMarkdownPreviewPane ? "doc.richtext.fill" : "doc.richtext")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.disabled(currentLanguage != "markdown")
|
||||
.help("Toggle Markdown Preview")
|
||||
|
|
@ -1049,7 +1067,7 @@ extension ContentView {
|
|||
Button("Compact") { markdownPreviewTemplateRaw = "compact" }
|
||||
} label: {
|
||||
Label("Preview Style", systemImage: "textformat.size")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Markdown Preview Template")
|
||||
}
|
||||
|
|
@ -1058,7 +1076,7 @@ extension ContentView {
|
|||
|
||||
Button(action: { undoFromToolbar() }) {
|
||||
Label("Undo", systemImage: "arrow.uturn.backward")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Undo (Cmd+Z)")
|
||||
.keyboardShortcut("z", modifiers: .command)
|
||||
|
|
@ -1068,7 +1086,7 @@ extension ContentView {
|
|||
showUpdaterDialog(checkNow: true)
|
||||
}) {
|
||||
Label("Updates", systemImage: "arrow.triangle.2.circlepath.circle")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Check for Updates")
|
||||
}
|
||||
|
|
@ -1078,20 +1096,20 @@ extension ContentView {
|
|||
openWindow(id: "blank-window")
|
||||
}) {
|
||||
Label("New Window", systemImage: "macwindow.badge.plus")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("New Window (Cmd+N)")
|
||||
#endif
|
||||
|
||||
Button(action: { adjustEditorFontSize(-1) }) {
|
||||
Label("Font -", systemImage: "textformat.size.smaller")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Decrease Font Size")
|
||||
|
||||
Button(action: { adjustEditorFontSize(1) }) {
|
||||
Label("Font +", systemImage: "textformat.size.larger")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Increase Font Size")
|
||||
|
||||
|
|
@ -1099,7 +1117,7 @@ extension ContentView {
|
|||
requestClearEditorContent()
|
||||
}) {
|
||||
Label("Clear", systemImage: "eraser")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Clear Editor")
|
||||
|
||||
|
|
@ -1107,7 +1125,7 @@ extension ContentView {
|
|||
insertTemplateForCurrentLanguage()
|
||||
}) {
|
||||
Label("Template", systemImage: "doc.badge.plus")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Insert Template for Current Language")
|
||||
|
||||
|
|
@ -1115,7 +1133,7 @@ extension ContentView {
|
|||
toggleSidebarFromToolbar()
|
||||
}) {
|
||||
Label("Sidebar", systemImage: "sidebar.left")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
.symbolVariant(viewModel.showSidebar ? .fill : .none)
|
||||
}
|
||||
.help("Toggle Sidebar (Cmd+Opt+S)")
|
||||
|
|
@ -1124,7 +1142,7 @@ extension ContentView {
|
|||
toggleProjectSidebarFromToolbar()
|
||||
}) {
|
||||
Label("Project", systemImage: "sidebar.right")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
.symbolVariant(showProjectStructureSidebar ? .fill : .none)
|
||||
}
|
||||
.help("Toggle Project Structure Sidebar")
|
||||
|
|
@ -1133,7 +1151,7 @@ extension ContentView {
|
|||
toggleAutoCompletion()
|
||||
}) {
|
||||
Label("AI", systemImage: "bolt.horizontal.circle")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
.symbolVariant(isAutoCompletionEnabled ? .fill : .none)
|
||||
}
|
||||
.help(isAutoCompletionEnabled ? "Disable Code Completion" : "Enable Code Completion")
|
||||
|
|
@ -1143,7 +1161,7 @@ extension ContentView {
|
|||
showBracketHelperBarMac.toggle()
|
||||
}) {
|
||||
Label("Brackets", systemImage: "chevron.left.chevron.right")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
.symbolVariant(showBracketHelperBarMac ? .fill : .none)
|
||||
}
|
||||
.help(showBracketHelperBarMac ? "Hide Bracket Helper Bar" : "Show Bracket Helper Bar")
|
||||
|
|
@ -1154,7 +1172,7 @@ extension ContentView {
|
|||
UserDefaults.standard.set(viewModel.isBrainDumpMode, forKey: "BrainDumpModeEnabled")
|
||||
}) {
|
||||
Label("Brain Dump", systemImage: "note.text")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
.symbolVariant(viewModel.isBrainDumpMode ? .fill : .none)
|
||||
}
|
||||
.help("Brain Dump Mode")
|
||||
|
|
@ -1166,7 +1184,7 @@ extension ContentView {
|
|||
NotificationCenter.default.post(name: .toggleTranslucencyRequested, object: enableTranslucentWindow)
|
||||
}) {
|
||||
Label("Translucency", systemImage: enableTranslucentWindow ? "rectangle.fill" : "rectangle")
|
||||
.foregroundStyle(NeonUIStyle.accentBlue)
|
||||
.foregroundStyle(macToolbarSymbolColor)
|
||||
}
|
||||
.help("Toggle Translucent Window Background")
|
||||
.accessibilityLabel("Translucent Window Background")
|
||||
|
|
|
|||
|
|
@ -331,6 +331,7 @@ struct ContentView: View {
|
|||
#if os(macOS)
|
||||
@State private var hostWindowNumber: Int? = nil
|
||||
@AppStorage("ShowBracketHelperBarMac") var showBracketHelperBarMac: Bool = false
|
||||
@AppStorage("SettingsToolbarSymbolsColorMac") var toolbarSymbolsColorMacRaw: String = "blue"
|
||||
@State private var windowCloseConfirmationDelegate: WindowCloseConfirmationDelegate? = nil
|
||||
#endif
|
||||
@State var showMarkdownPreviewPane: Bool = false
|
||||
|
|
@ -433,6 +434,13 @@ struct ContentView: View {
|
|||
}
|
||||
return AnyShapeStyle(Color(nsColor: .textBackgroundColor))
|
||||
}
|
||||
|
||||
private var macToolbarBackgroundStyle: AnyShapeStyle {
|
||||
if enableTranslucentWindow {
|
||||
return AnyShapeStyle(macTranslucencyMode.material.opacity(0.8))
|
||||
}
|
||||
return AnyShapeStyle(Color(nsColor: .textBackgroundColor))
|
||||
}
|
||||
#elseif os(iOS)
|
||||
var primaryGlassMaterial: Material { colorScheme == .dark ? .regularMaterial : .ultraThinMaterial }
|
||||
var toolbarFallbackColor: Color {
|
||||
|
|
@ -4066,7 +4074,7 @@ struct ContentView: View {
|
|||
}
|
||||
#if os(macOS)
|
||||
.toolbarBackground(
|
||||
macChromeBackgroundStyle,
|
||||
macToolbarBackgroundStyle,
|
||||
for: ToolbarPlacement.windowToolbar
|
||||
)
|
||||
.toolbarBackgroundVisibility(Visibility.visible, for: ToolbarPlacement.windowToolbar)
|
||||
|
|
@ -4695,6 +4703,12 @@ struct ContentView: View {
|
|||
.padding(.vertical, 6)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
#if os(macOS)
|
||||
.simultaneousGesture(
|
||||
TapGesture(count: 2)
|
||||
.onEnded { requestCloseTab(tab) }
|
||||
)
|
||||
#endif
|
||||
|
||||
Button {
|
||||
requestCloseTab(tab)
|
||||
|
|
@ -4722,7 +4736,7 @@ struct ContentView: View {
|
|||
}
|
||||
.frame(minHeight: 42, maxHeight: 42, alignment: .center)
|
||||
#if os(macOS)
|
||||
.background(macChromeBackgroundStyle)
|
||||
.background(macToolbarBackgroundStyle)
|
||||
#else
|
||||
.background(
|
||||
enableTranslucentWindow
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct NeonSettingsView: View {
|
|||
@AppStorage("SettingsEditorFontSize") private var editorFontSize: Double = 14
|
||||
@AppStorage("SettingsLineHeight") private var lineHeight: Double = 1.0
|
||||
@AppStorage("SettingsAppearance") private var appearance: String = "system"
|
||||
@AppStorage("SettingsToolbarSymbolsColorMac") private var toolbarSymbolsColorMacRaw: String = "blue"
|
||||
#if os(iOS)
|
||||
@AppStorage("EnableTranslucentWindow") private var translucentWindow: Bool = true
|
||||
#else
|
||||
|
|
@ -73,6 +74,7 @@ struct NeonSettingsView: View {
|
|||
@State private var showDataDisclosureDialog: Bool = false
|
||||
@State private var availableEditorFonts: [String] = []
|
||||
@State private var moreSectionTab: String = "support"
|
||||
@State private var editorSectionTab: String = "basics"
|
||||
@State private var diagnosticsCopyStatus: String = ""
|
||||
@State private var supportRefreshTask: Task<Void, Never>?
|
||||
@State private var isDiscoveringFonts: Bool = false
|
||||
|
|
@ -271,7 +273,8 @@ struct NeonSettingsView: View {
|
|||
minSize: macSettingsWindowSize.min,
|
||||
idealSize: macSettingsWindowSize.ideal,
|
||||
translucentEnabled: supportsTranslucency && translucentWindow,
|
||||
translucencyModeRaw: macTranslucencyModeRaw
|
||||
translucencyModeRaw: macTranslucencyModeRaw,
|
||||
appearanceRaw: appearance
|
||||
)
|
||||
)
|
||||
#endif
|
||||
|
|
@ -512,6 +515,17 @@ struct NeonSettingsView: View {
|
|||
.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
HStack(alignment: .center, spacing: UI.space12) {
|
||||
Text("Toolbar Symbols")
|
||||
.frame(width: isCompactSettingsLayout ? nil : standardLabelWidth, alignment: .leading)
|
||||
Picker("", selection: $toolbarSymbolsColorMacRaw) {
|
||||
Text("Blue").tag("blue")
|
||||
Text("Dark Gray").tag("darkGray")
|
||||
Text("Black").tag("black")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
|
||||
if supportsTranslucency {
|
||||
Toggle("Translucent Window", isOn: $translucentWindow)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
|
@ -961,13 +975,88 @@ struct NeonSettingsView: View {
|
|||
title: "Editor",
|
||||
subtitle: "Display, indentation, editing behavior, and completion sources."
|
||||
)
|
||||
editorSectionPicker
|
||||
if editorSectionTab == "basics" {
|
||||
editorBasicsSettings
|
||||
} else {
|
||||
editorBehaviorSettings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var editorSectionPicker: some View {
|
||||
VStack(alignment: .leading, spacing: UI.space8) {
|
||||
Text("Section")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Picker("Section", selection: $editorSectionTab) {
|
||||
Text("Basics").tag("basics")
|
||||
Text("Behavior").tag("behavior")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
private var editorBasicsSettings: some View {
|
||||
#if os(iOS)
|
||||
VStack(spacing: UI.space16) {
|
||||
settingsCardSection(
|
||||
title: "Display",
|
||||
icon: "eye",
|
||||
tip: "Scope visuals are best used with line wrap disabled."
|
||||
) {
|
||||
VStack(spacing: UI.space16) {
|
||||
settingsCardSection(
|
||||
title: "Display",
|
||||
icon: "eye",
|
||||
tip: "Scope visuals are best used with line wrap disabled."
|
||||
) {
|
||||
Toggle("Show Line Numbers", isOn: $showLineNumbers)
|
||||
Toggle("Highlight Current Line", isOn: $highlightCurrentLine)
|
||||
Toggle("Highlight Matching Brackets", isOn: $highlightMatchingBrackets)
|
||||
Toggle("Show Scope Guides (Non-Swift)", isOn: $showScopeGuides)
|
||||
Toggle("Highlight Scoped Region", isOn: $highlightScopeBackground)
|
||||
Toggle("Line Wrap", isOn: $lineWrapEnabled)
|
||||
Text("When Line Wrap is enabled, scope guides/scoped region are turned off to avoid layout conflicts.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Scope guides are intended for non-Swift languages. Swift favors matching-token highlight.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Invisible character markers are disabled to avoid whitespace glyph artifacts.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
settingsCardSection(
|
||||
title: "Indentation",
|
||||
icon: "increase.indent",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Picker("Indent Style", selection: $indentStyle) {
|
||||
Text("Spaces").tag("spaces")
|
||||
Text("Tabs").tag("tabs")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
Stepper(value: $indentWidth, in: 2...8, step: 1) {
|
||||
Text(localized("Indent Width: %lld", Int64(indentWidth)))
|
||||
}
|
||||
}
|
||||
|
||||
settingsCardSection(
|
||||
title: "Layout",
|
||||
icon: "sidebar.left",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Picker("Project Navigator Position", selection: $projectNavigatorPlacementRaw) {
|
||||
Text("Left").tag(ContentView.ProjectNavigatorPlacement.leading.rawValue)
|
||||
Text("Right").tag(ContentView.ProjectNavigatorPlacement.trailing.rawValue)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
}
|
||||
#else
|
||||
GroupBox("Editor Basics") {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Display")
|
||||
.font(Typography.sectionHeadline)
|
||||
Toggle("Show Line Numbers", isOn: $showLineNumbers)
|
||||
Toggle("Highlight Current Line", isOn: $highlightCurrentLine)
|
||||
Toggle("Highlight Matching Brackets", isOn: $highlightMatchingBrackets)
|
||||
|
|
@ -984,12 +1073,13 @@ struct NeonSettingsView: View {
|
|||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
settingsCardSection(
|
||||
title: "Indentation",
|
||||
icon: "increase.indent",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Indentation")
|
||||
.font(Typography.sectionHeadline)
|
||||
Picker("Indent Style", selection: $indentStyle) {
|
||||
Text("Spaces").tag("spaces")
|
||||
Text("Tabs").tag("tabs")
|
||||
|
|
@ -1000,24 +1090,76 @@ struct NeonSettingsView: View {
|
|||
Text(localized("Indent Width: %lld", Int64(indentWidth)))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
settingsCardSection(
|
||||
title: "Layout",
|
||||
icon: "sidebar.left",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Layout")
|
||||
.font(Typography.sectionHeadline)
|
||||
Picker("Project Navigator Position", selection: $projectNavigatorPlacementRaw) {
|
||||
Text("Left").tag(ContentView.ProjectNavigatorPlacement.leading.rawValue)
|
||||
Text("Right").tag(ContentView.ProjectNavigatorPlacement.trailing.rawValue)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(UI.groupPadding)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
settingsCardSection(
|
||||
title: "Performance",
|
||||
icon: "speedometer",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
private var editorBehaviorSettings: some View {
|
||||
#if os(iOS)
|
||||
VStack(spacing: UI.space16) {
|
||||
settingsCardSection(
|
||||
title: "Performance",
|
||||
icon: "speedometer",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Picker("Preset", selection: $performancePresetRaw) {
|
||||
Text("Balanced").tag(ContentView.PerformancePreset.balanced.rawValue)
|
||||
Text("Large Files").tag(ContentView.PerformancePreset.largeFiles.rawValue)
|
||||
Text("Battery").tag(ContentView.PerformancePreset.battery.rawValue)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
Text("Balanced keeps default behavior. Large Files and Battery enter performance mode earlier.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
settingsCardSection(
|
||||
title: "Editing",
|
||||
icon: "keyboard",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Toggle("Auto Indent", isOn: $autoIndent)
|
||||
Toggle("Auto Close Brackets", isOn: $autoCloseBrackets)
|
||||
Toggle("Trim Trailing Whitespace", isOn: $trimTrailingWhitespace)
|
||||
Toggle("Trim Edges for Syntax Detection", isOn: $trimWhitespaceForSyntaxDetection)
|
||||
}
|
||||
|
||||
settingsCardSection(
|
||||
title: "Completion",
|
||||
icon: "sparkles",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Toggle("Enable Completion", isOn: $completionEnabled)
|
||||
Toggle("Include Words in Document", isOn: $completionFromDocument)
|
||||
Toggle("Include Syntax Keywords", isOn: $completionFromSyntax)
|
||||
Text("For lower latency on large files, keep only one completion source enabled.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
#else
|
||||
GroupBox("Editor Behavior") {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Performance")
|
||||
.font(Typography.sectionHeadline)
|
||||
Picker("Preset", selection: $performancePresetRaw) {
|
||||
Text("Balanced").tag(ContentView.PerformancePreset.balanced.rawValue)
|
||||
Text("Large Files").tag(ContentView.PerformancePreset.largeFiles.rawValue)
|
||||
|
|
@ -1028,23 +1170,25 @@ struct NeonSettingsView: View {
|
|||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
settingsCardSection(
|
||||
title: "Editing",
|
||||
icon: "keyboard",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Editing")
|
||||
.font(Typography.sectionHeadline)
|
||||
Toggle("Auto Indent", isOn: $autoIndent)
|
||||
Toggle("Auto Close Brackets", isOn: $autoCloseBrackets)
|
||||
Toggle("Trim Trailing Whitespace", isOn: $trimTrailingWhitespace)
|
||||
Toggle("Trim Edges for Syntax Detection", isOn: $trimWhitespaceForSyntaxDetection)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
settingsCardSection(
|
||||
title: "Completion",
|
||||
icon: "sparkles",
|
||||
emphasis: .secondary
|
||||
) {
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Completion")
|
||||
.font(Typography.sectionHeadline)
|
||||
Toggle("Enable Completion", isOn: $completionEnabled)
|
||||
Toggle("Include Words in Document", isOn: $completionFromDocument)
|
||||
Toggle("Include Syntax Keywords", isOn: $completionFromSyntax)
|
||||
|
|
@ -1052,109 +1196,12 @@ struct NeonSettingsView: View {
|
|||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
#else
|
||||
GroupBox("Editor") {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Display")
|
||||
.font(Typography.sectionHeadline)
|
||||
Toggle("Show Line Numbers", isOn: $showLineNumbers)
|
||||
Toggle("Highlight Current Line", isOn: $highlightCurrentLine)
|
||||
Toggle("Highlight Matching Brackets", isOn: $highlightMatchingBrackets)
|
||||
Toggle("Show Scope Guides (Non-Swift)", isOn: $showScopeGuides)
|
||||
Toggle("Highlight Scoped Region", isOn: $highlightScopeBackground)
|
||||
Toggle("Line Wrap", isOn: $lineWrapEnabled)
|
||||
Text("When Line Wrap is enabled, scope guides/scoped region are turned off to avoid layout conflicts.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Scope guides are intended for non-Swift languages. Swift favors matching-token highlight.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
Text("Invisible character markers are disabled to avoid whitespace glyph artifacts.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Indentation")
|
||||
.font(Typography.sectionHeadline)
|
||||
Picker("Indent Style", selection: $indentStyle) {
|
||||
Text("Spaces").tag("spaces")
|
||||
Text("Tabs").tag("tabs")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
|
||||
Stepper(value: $indentWidth, in: 2...8, step: 1) {
|
||||
Text(localized("Indent Width: %lld", Int64(indentWidth)))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Layout")
|
||||
.font(Typography.sectionHeadline)
|
||||
Picker("Project Navigator Position", selection: $projectNavigatorPlacementRaw) {
|
||||
Text("Left").tag(ContentView.ProjectNavigatorPlacement.leading.rawValue)
|
||||
Text("Right").tag(ContentView.ProjectNavigatorPlacement.trailing.rawValue)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Performance")
|
||||
.font(Typography.sectionHeadline)
|
||||
Picker("Preset", selection: $performancePresetRaw) {
|
||||
Text("Balanced").tag(ContentView.PerformancePreset.balanced.rawValue)
|
||||
Text("Large Files").tag(ContentView.PerformancePreset.largeFiles.rawValue)
|
||||
Text("Battery").tag(ContentView.PerformancePreset.battery.rawValue)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
Text("Balanced keeps default behavior. Large Files and Battery enter performance mode earlier.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Editing")
|
||||
.font(Typography.sectionHeadline)
|
||||
Toggle("Auto Indent", isOn: $autoIndent)
|
||||
Toggle("Auto Close Brackets", isOn: $autoCloseBrackets)
|
||||
Toggle("Trim Trailing Whitespace", isOn: $trimTrailingWhitespace)
|
||||
Toggle("Trim Edges for Syntax Detection", isOn: $trimWhitespaceForSyntaxDetection)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(alignment: .leading, spacing: UI.space10) {
|
||||
Text("Completion")
|
||||
.font(Typography.sectionHeadline)
|
||||
Toggle("Enable Completion", isOn: $completionEnabled)
|
||||
Toggle("Include Words in Document", isOn: $completionFromDocument)
|
||||
Toggle("Include Syntax Keywords", isOn: $completionFromSyntax)
|
||||
Text("For lower latency on large files, keep only one completion source enabled.")
|
||||
.font(Typography.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(UI.groupPadding)
|
||||
}
|
||||
#endif
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(UI.groupPadding)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private var templateTab: some View {
|
||||
|
|
@ -2311,6 +2358,7 @@ struct SettingsWindowConfigurator: NSViewRepresentable {
|
|||
let idealSize: NSSize
|
||||
let translucentEnabled: Bool
|
||||
let translucencyModeRaw: String
|
||||
let appearanceRaw: String
|
||||
|
||||
final class Coordinator {
|
||||
var didInitialApply = false
|
||||
|
|
@ -2409,7 +2457,15 @@ struct SettingsWindowConfigurator: NSViewRepresentable {
|
|||
|
||||
private func translucencyEnabledColor(enabled: Bool, window: NSWindow) -> NSColor {
|
||||
guard enabled else { return NSColor.windowBackgroundColor }
|
||||
let isDark = window.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
|
||||
let isDark: Bool
|
||||
switch appearanceRaw {
|
||||
case "light":
|
||||
isDark = false
|
||||
case "dark":
|
||||
isDark = true
|
||||
default:
|
||||
isDark = window.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
|
||||
}
|
||||
let whiteLevel: CGFloat
|
||||
switch translucencyModeRaw {
|
||||
case "subtle":
|
||||
|
|
|
|||
|
|
@ -341,12 +341,12 @@ struct WelcomeTourView: View {
|
|||
private let pages: [TourPage] = [
|
||||
TourPage(
|
||||
title: "What’s New in This Release",
|
||||
subtitle: "Major changes since v0.5.1:",
|
||||
subtitle: "Major changes since v0.5.2:",
|
||||
bullets: [
|
||||
"Added editor performance presets in Settings (`Balanced`, `Large Files`, `Battery`) with shared runtime mapping.",
|
||||
"Added configurable project navigator placement (`Left`/`Right`) for project-structure sidebar layout.",
|
||||
"Added richer updater diagnostics details in Settings: staged update summary, last install-attempt summary, and recent sanitized log snippet.",
|
||||
"Added CSV/TSV table mode with a `Table`/`Text` switch, lazy row rendering, and background parsing for larger datasets."
|
||||
"Added a new high-readability colorful light theme preset: `Prism Daylight` (also selectable while app appearance is set to dark).",
|
||||
"Added double-click-to-close behavior for tabs on macOS tab strips.",
|
||||
"Added split editor settings sections (`Basics` / `Behavior`) to reduce scrolling in the Editor tab.",
|
||||
"Improved custom theme vibrancy by applying the vivid neon syntax profile to `Custom`, so syntax colors remain bright and saturated."
|
||||
],
|
||||
iconName: "sparkles.rectangle.stack",
|
||||
colors: [Color(red: 0.40, green: 0.28, blue: 0.90), Color(red: 0.96, green: 0.46, blue: 0.55)],
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ let editorThemeNames: [String] = [
|
|||
"Plasma Storm",
|
||||
"Inferno Neon",
|
||||
"Ultraviolet Flux",
|
||||
"Prism Daylight",
|
||||
"Custom",
|
||||
"Dracula",
|
||||
"One Dark Pro",
|
||||
|
|
@ -259,14 +260,14 @@ private func paletteForThemeName(_ name: String, defaults: UserDefaults) -> Them
|
|||
return ThemePalette(
|
||||
text: Color(red: 0.94, green: 0.98, blue: 0.92),
|
||||
background: Color(red: 0.07, green: 0.09, blue: 0.06),
|
||||
cursor: Color(red: 0.68, green: 1.00, blue: 0.20),
|
||||
cursor: Color(red: 0.18, green: 0.48, blue: 0.96),
|
||||
selection: Color(red: 0.18, green: 0.25, blue: 0.14),
|
||||
keyword: Color(red: 0.68, green: 1.00, blue: 0.20),
|
||||
string: Color(red: 0.20, green: 1.00, blue: 0.78),
|
||||
keyword: Color(red: 0.45, green: 0.78, blue: 0.16),
|
||||
string: Color(red: 0.13, green: 0.70, blue: 0.50),
|
||||
number: Color(red: 1.00, green: 0.86, blue: 0.22),
|
||||
comment: Color(red: 0.54, green: 0.62, blue: 0.50),
|
||||
type: Color(red: 0.48, green: 0.86, blue: 1.00),
|
||||
property: Color(red: 0.88, green: 1.00, blue: 0.36),
|
||||
property: Color(red: 0.56, green: 0.78, blue: 0.28),
|
||||
builtin: Color(red: 1.00, green: 0.48, blue: 0.40)
|
||||
)
|
||||
case "Plasma Storm":
|
||||
|
|
@ -311,6 +312,20 @@ private func paletteForThemeName(_ name: String, defaults: UserDefaults) -> Them
|
|||
property: Color(red: 0.68, green: 0.72, blue: 1.00),
|
||||
builtin: Color(red: 1.00, green: 0.28, blue: 0.56)
|
||||
)
|
||||
case "Prism Daylight":
|
||||
return ThemePalette(
|
||||
text: Color(red: 0.08, green: 0.10, blue: 0.14),
|
||||
background: Color(red: 0.96, green: 0.97, blue: 0.99),
|
||||
cursor: Color(red: 0.14, green: 0.34, blue: 0.84),
|
||||
selection: Color(red: 0.84, green: 0.89, blue: 0.99),
|
||||
keyword: Color(red: 0.48, green: 0.13, blue: 0.71),
|
||||
string: Color(red: 0.05, green: 0.48, blue: 0.33),
|
||||
number: Color(red: 0.71, green: 0.28, blue: 0.03),
|
||||
comment: Color(red: 0.37, green: 0.42, blue: 0.49),
|
||||
type: Color(red: 0.00, green: 0.34, blue: 0.76),
|
||||
property: Color(red: 0.64, green: 0.11, blue: 0.67),
|
||||
builtin: Color(red: 0.76, green: 0.25, blue: 0.05)
|
||||
)
|
||||
case "Dracula":
|
||||
return ThemePalette(
|
||||
text: Color(red: 0.97, green: 0.97, blue: 0.95),
|
||||
|
|
@ -606,7 +621,8 @@ func currentEditorTheme(colorScheme: ColorScheme) -> EditorTheme {
|
|||
"Cyber Lime",
|
||||
"Plasma Storm",
|
||||
"Inferno Neon",
|
||||
"Ultraviolet Flux"
|
||||
"Ultraviolet Flux",
|
||||
"Custom"
|
||||
]
|
||||
return vividNeonThemes.contains(name) ? .neonRaw : .standard
|
||||
}()
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -64,7 +64,7 @@
|
|||
|
||||
|
||||
> Status: **active release**
|
||||
> Latest release: **v0.5.2**
|
||||
> Latest release: **v0.5.3**
|
||||
> Platform target: **macOS 26 (Tahoe)** compatible with **macOS Sequoia**
|
||||
> Apple Silicon: tested / Intel: not tested
|
||||
> Last updated (README): **2026-03-10** for release line **v0.5.2**
|
||||
|
|
@ -161,7 +161,7 @@
|
|||
- Security policy: [`SECURITY.md`](SECURITY.md)
|
||||
- Release checklists: [`release/`](release/) — TestFlight & App Store preflight docs
|
||||
|
||||
## What's New Since v0.5.1
|
||||
## What's New Since v0.5.2
|
||||
|
||||
- Added `Close All Tabs` actions across macOS, iOS, and iPadOS with a confirmation safeguard.
|
||||
- Added project-sidebar quick actions (`Expand All` / `Collapse All`) and a default-on `Show Supported Files Only` filter.
|
||||
|
|
@ -195,7 +195,7 @@ Prebuilt binaries are available on [GitHub Releases](https://github.com/h3pdesig
|
|||
Best for direct notarized builds and fastest access to new stable versions.
|
||||
|
||||
- Download: [GitHub Releases](https://github.com/h3pdesign/Neon-Vision-Editor/releases)
|
||||
- Latest release: **v0.5.2**
|
||||
- Latest release: **v0.5.3**
|
||||
- Channel: **Stable**
|
||||
- Architecture: Apple Silicon (Intel not tested)
|
||||
|
||||
|
|
@ -598,15 +598,15 @@ All shortcuts use `Cmd` (`⌘`). iPad/iOS require a hardware keyboard.
|
|||
|
||||
## Changelog
|
||||
|
||||
Latest stable: **v0.5.2** (2026-03-09)
|
||||
Latest stable: **v0.5.3** (2026-03-10)
|
||||
|
||||
### Recent Releases (At a glance)
|
||||
|
||||
| Version | Date | Highlights | Fixes | Breaking changes | Migration |
|
||||
|---|---|---|---|---|---|
|
||||
| [`v0.5.3`](https://github.com/h3pdesign/Neon-Vision-Editor/releases/tag/v0.5.3) | 2026-03-10 | a new high-readability colorful light theme preset: `Prism Daylight` (also selectable while app appearance is set to dark); double-click-to-close behavior for tabs on macOS tab strips; custom theme vibrancy by applying the vivid neon syntax profile to `Custom`, so syntax colors remain bright and saturated | toolbar-symbol contrast edge cases in dark mode where gray/black variants could appear too similar | None noted | None required |
|
||||
| [`v0.5.2`](https://github.com/h3pdesign/Neon-Vision-Editor/releases/tag/v0.5.2) | 2026-03-09 | editor performance presets in Settings (`Balanced`, `Large Files`, `Battery`) with shared runtime mapping; configurable project navigator placement (`Left`/`Right`) for project-structure sidebar layout; iOS/iPadOS large-file responsiveness by lowering automatic large-file thresholds and applying preset-based tuning | missing diagnostics reset workflow by adding a dedicated `Clear Diagnostics` action that also clears file-open timing snapshots; macOS editor-window top-bar jumping when toggling the toolbar translucency control by keeping chrome flags stable; CSV/TSV mode header transparency so the mode bar now uses a solid standard window background | None noted | None required |
|
||||
| [`v0.5.1`](https://github.com/h3pdesign/Neon-Vision-Editor/releases/tag/v0.5.1) | 2026-03-08 | bulk `Close All Tabs` actions to toolbar surfaces (macOS, iOS, iPadOS), including a confirmation step before closing; project-structure quick actions to expand all folders or collapse all folders in one step; Markdown preview stability by preserving relative scroll position during preview refreshes | diagnostics export safety by redacting token-like updater status fragments before copying; Markdown regression coverage with new tests for Claude-style mixed-content Markdown and code-fence matching behavior; accidental destructive tab-bulk-close behavior by requiring explicit user confirmation before closing all tabs | None noted | None required |
|
||||
| [`v0.5.0`](https://github.com/h3pdesign/Neon-Vision-Editor/releases/tag/v0.5.0) | 2026-03-06 | updater staging hardening with retry/fallback behavior and staged-bundle integrity checks; explicit accessibility labels/hints for key toolbar actions and updater log/progress controls; CSV handling by enabling fast syntax profile earlier and for long-line CSV files to reduce freeze risk | updater staging resilience when `ditto` fails by retrying and falling back safely to copy-based staging; release preflight to fail on unresolved placeholder entries and stale README download metrics; inconsistent reappearance of the macOS settings tab title in the upper-left window title area | None noted | None required |
|
||||
|
||||
- Full release history: [`CHANGELOG.md`](CHANGELOG.md)
|
||||
- Compare recent changes: [v0.5.0...v0.5.2](https://github.com/h3pdesign/Neon-Vision-Editor/compare/v0.5.0...v0.5.2)
|
||||
|
|
@ -628,12 +628,12 @@ Latest stable: **v0.5.2** (2026-03-09)
|
|||
|
||||
## Release Integrity
|
||||
|
||||
- Tag: `v0.5.2`
|
||||
- Tag: `v0.5.3`
|
||||
- Tagged commit: `1c31306`
|
||||
- Verify local tag target:
|
||||
|
||||
```bash
|
||||
git rev-parse --verify v0.5.2
|
||||
git rev-parse --verify v0.5.3
|
||||
```
|
||||
|
||||
- Verify downloaded artifact checksum locally:
|
||||
|
|
|
|||
Loading…
Reference in a new issue