Fixed propper MD rendering +++++

This commit is contained in:
2026-02-12 18:44:39 +01:00
parent 7265d22438
commit abd1dfddd4
13 changed files with 175 additions and 20 deletions

2
.gitignore vendored
View File

@@ -112,3 +112,5 @@ iOSInjectionProject/
Network Trash Folder Network Trash Folder
Temporary Items Temporary Items
.apdisk .apdisk
CLAUDE.md

26
Package.swift Normal file
View File

@@ -0,0 +1,26 @@
// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "oAI",
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "oAI",
targets: ["oAI"]
),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "oAI"
),
.testTarget(
name: "oAITests",
dependencies: ["oAI"]
),
]
)

2
Sources/oAI/oAI.swift Normal file
View File

@@ -0,0 +1,2 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book

View File

@@ -0,0 +1,6 @@
import Testing
@testable import oAI
@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
}

View File

@@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
A52B47522F3E45BA004200E2 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = A52B47512F3E45BA004200E2 /* MarkdownUI */; };
A550A8342F3C5C9300136F2B /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = A550A6812F3B730000136F2B /* GRDB */; }; A550A8342F3C5C9300136F2B /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = A550A6812F3B730000136F2B /* GRDB */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -28,6 +29,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A550A8342F3C5C9300136F2B /* GRDB in Frameworks */, A550A8342F3C5C9300136F2B /* GRDB in Frameworks */,
A52B47522F3E45BA004200E2 /* MarkdownUI in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -71,6 +73,7 @@
name = oAI; name = oAI;
packageProductDependencies = ( packageProductDependencies = (
A550A6812F3B730000136F2B /* GRDB */, A550A6812F3B730000136F2B /* GRDB */,
A52B47512F3E45BA004200E2 /* MarkdownUI */,
); );
productName = oAI; productName = oAI;
productReference = A550A6622F3B72EA00136F2B /* oAI.app */; productReference = A550A6622F3B72EA00136F2B /* oAI.app */;
@@ -102,6 +105,7 @@
minimizedProjectReferenceProxies = 1; minimizedProjectReferenceProxies = 1;
packageReferences = ( packageReferences = (
A550A6802F3B730000136F2B /* XCRemoteSwiftPackageReference "GRDB" */, A550A6802F3B730000136F2B /* XCRemoteSwiftPackageReference "GRDB" */,
A52B47502F3E45BA004200E2 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
); );
preferredProjectObjectVersion = 77; preferredProjectObjectVersion = 77;
productRefGroup = A550A6632F3B72EA00136F2B /* Products */; productRefGroup = A550A6632F3B72EA00136F2B /* Products */;
@@ -358,6 +362,14 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */ /* Begin XCRemoteSwiftPackageReference section */
A52B47502F3E45BA004200E2 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/gonzalezreal/swift-markdown-ui";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.4.1;
};
};
A550A6802F3B730000136F2B /* XCRemoteSwiftPackageReference "GRDB" */ = { A550A6802F3B730000136F2B /* XCRemoteSwiftPackageReference "GRDB" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/groue/GRDB.swift"; repositoryURL = "https://github.com/groue/GRDB.swift";
@@ -369,6 +381,11 @@
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
A52B47512F3E45BA004200E2 /* MarkdownUI */ = {
isa = XCSwiftPackageProductDependency;
package = A52B47502F3E45BA004200E2 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;
productName = MarkdownUI;
};
A550A6812F3B730000136F2B /* GRDB */ = { A550A6812F3B730000136F2B /* GRDB */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = A550A6802F3B730000136F2B /* XCRemoteSwiftPackageReference "GRDB" */; package = A550A6802F3B730000136F2B /* XCRemoteSwiftPackageReference "GRDB" */;

View File

@@ -1,5 +1,5 @@
{ {
"originHash" : "d77223ea3cadaebd2154378ec5005b6ebefcef3b34a4dafa368b0c4f16c0561c", "originHash" : "3d7837e8d2f446123857e5e76a8365b4c8fbf995fe10eb02a8b5592491655b0b",
"pins" : [ "pins" : [
{ {
"identity" : "grdb.swift", "identity" : "grdb.swift",
@@ -9,6 +9,33 @@
"revision" : "aa0079aeb82a4bf00324561a40bffe68c6fe1c26", "revision" : "aa0079aeb82a4bf00324561a40bffe68c6fe1c26",
"version" : "7.9.0" "version" : "7.9.0"
} }
},
{
"identity" : "networkimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/gonzalezreal/NetworkImage",
"state" : {
"revision" : "2849f5323265386e200484b0d0f896e73c3411b9",
"version" : "6.0.1"
}
},
{
"identity" : "swift-cmark",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-cmark",
"state" : {
"revision" : "5d9bdaa4228b381639fff09403e39a04926e2dbe",
"version" : "0.7.1"
}
},
{
"identity" : "swift-markdown-ui",
"kind" : "remoteSourceControl",
"location" : "https://github.com/gonzalezreal/swift-markdown-ui",
"state" : {
"revision" : "5f613358148239d0292c0cef674a3c2314737f9e",
"version" : "2.4.1"
}
} }
], ],
"version" : 3 "version" : 3

View File

@@ -543,7 +543,7 @@
</main> </main>
<footer> <footer>
<p>© 2026 oAI. For support or feedback, visit <a href="https://gitlab.pm/rune/oai-swift">gitlab.pm</a>.</p> <p>© 2026 oAI. For support or feedback, visit <a href="https://gitlab.pm/rune/oai-swift">gitlab.pm</a> or <a href="mailto:support@fubar.pm?subject=oAI Support&body=What can I help you with?">Contact Us</a>.</p>
</footer> </footer>
</div> </div>
</body> </body>

View File

@@ -57,6 +57,21 @@ You are a helpful AI assistant. Follow these guidelines:
5. **Be Direct**: Provide concise, relevant answers. Avoid unnecessary preambles or apologies. 5. **Be Direct**: Provide concise, relevant answers. Avoid unnecessary preambles or apologies.
6. **Use Markdown Formatting**: Always format your responses using standard Markdown syntax:
- Use **bold** for emphasis
- Use bullet points and numbered lists for organization
- Use code blocks with language tags for code (e.g., ```python)
- Use proper headings (##, ###) to structure long responses
- If the user requests output in other formats (HTML, JSON, XML, etc.), wrap those in appropriate code blocks
7. **Break Down Complex Tasks**: When working with tools (file access, search, etc.), break complex tasks into smaller, manageable steps. If a task requires many operations:
- Complete one logical step at a time
- Present findings or progress after each step
- Ask the user if you should continue to the next step
- Be mindful of tool usage limits (typically 25-30 tool calls per request)
8. **Incremental Progress**: For large codebases or complex analyses, work incrementally. Don't try to explore everything at once. Focus on what's immediately relevant to the user's question.
Remember: It's better to ask questions or admit uncertainty than to provide incorrect or fabricated information. Remember: It's better to ask questions or admit uncertainty than to provide incorrect or fabricated information.
""" """

View File

@@ -133,7 +133,12 @@ struct InputBar: View {
} }
return .ignored return .ignored
} }
.onKeyPress(.return) { .onKeyPress(.return, phases: .down) { press in
// Shift+Return: always insert newline (let system handle)
if press.modifiers.contains(.shift) {
return .ignored
}
// If command dropdown is showing, select the highlighted command // If command dropdown is showing, select the highlighted command
if showCommandDropdown { if showCommandDropdown {
let suggestions = CommandSuggestionsView.filteredCommands(for: text) let suggestions = CommandSuggestionsView.filteredCommands(for: text)

View File

@@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import MarkdownUI
#if canImport(AppKit) #if canImport(AppKit)
import AppKit import AppKit
#endif #endif
@@ -34,21 +35,16 @@ struct MarkdownContentView: View {
@ViewBuilder @ViewBuilder
private func markdownText(_ text: String) -> some View { private func markdownText(_ text: String) -> some View {
if let attrString = try? AttributedString(markdown: text, options: .init(interpretedSyntax: .full)) { Markdown(text)
Text(attrString) .markdownTextStyle {
.font(.system(size: fontSize)) FontSize(fontSize)
.foregroundColor(.oaiPrimary) ForegroundColor(.primary)
.lineSpacing(4) }
.frame(maxWidth: .infinity, alignment: .leading) .markdownBlockStyle(\.paragraph) { configuration in
.textSelection(.enabled) configuration.label
} else { .markdownMargin(top: 0, bottom: 8)
Text(text) }
.font(.system(size: fontSize)) .textSelection(.enabled)
.foregroundColor(.oaiPrimary)
.lineSpacing(4)
.frame(maxWidth: .infinity, alignment: .leading)
.textSelection(.enabled)
}
} }
// MARK: - Parsing // MARK: - Parsing

View File

@@ -20,6 +20,16 @@ struct MessageRow: View {
#endif #endif
var body: some View { var body: some View {
// Compact layout for system messages (tool calls)
if message.role == .system && !isErrorMessage {
compactSystemMessage
} else {
standardMessageLayout
}
}
@ViewBuilder
private var standardMessageLayout: some View {
HStack(alignment: .top, spacing: 12) { HStack(alignment: .top, spacing: 12) {
// Role icon // Role icon
roleIcon roleIcon
@@ -131,6 +141,34 @@ struct MessageRow: View {
#endif #endif
} }
// Close standardMessageLayout - the above closing braces close it
// The body: some View now handles the split between compact and standard
// MARK: - Compact System Message
@ViewBuilder
private var compactSystemMessage: some View {
HStack(spacing: 8) {
Image(systemName: "wrench.and.screwdriver")
.font(.system(size: 11))
.foregroundColor(.secondary)
Text(message.content)
.font(.system(size: 11))
.foregroundColor(.secondary)
Spacer()
Text(message.timestamp, style: .time)
.font(.system(size: 10))
.foregroundColor(.secondary.opacity(0.7))
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(Color.secondary.opacity(0.08))
.cornerRadius(6)
}
// MARK: - Message Content // MARK: - Message Content
@ViewBuilder @ViewBuilder
@@ -158,7 +196,13 @@ struct MessageRow: View {
.textSelection(.enabled) .textSelection(.enabled)
} }
case .user: case .user:
MarkdownContentView(content: message.content, fontSize: settings.dialogTextSize) // User messages: preserve line breaks as-is (plain text, not markdown)
Text(message.content)
.font(.system(size: settings.dialogTextSize))
.foregroundColor(.oaiPrimary)
.lineSpacing(4)
.frame(maxWidth: .infinity, alignment: .leading)
.textSelection(.enabled)
} }
} }

View File

@@ -44,7 +44,7 @@ struct AboutView: View {
.padding(.horizontal, 40) .padding(.horizontal, 40)
VStack(spacing: 4) { VStack(spacing: 4) {
Text("&copy; 2026 [Rune Olsen](https://blog.rune.pm)") Text("&copy; 2026 [Rune Olsen](https://blog.rune.pm) | [Support](mailto:support@fubar.pm)")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)

View File

@@ -43,6 +43,21 @@ You are a helpful AI assistant. Follow these guidelines:
5. **Be Direct**: Provide concise, relevant answers. Avoid unnecessary preambles or apologies. 5. **Be Direct**: Provide concise, relevant answers. Avoid unnecessary preambles or apologies.
6. **Use Markdown Formatting**: Always format your responses using standard Markdown syntax:
- Use **bold** for emphasis
- Use bullet points and numbered lists for organization
- Use code blocks with language tags for code (e.g., ```python)
- Use proper headings (##, ###) to structure long responses
- If the user requests output in other formats (HTML, JSON, XML, etc.), wrap those in appropriate code blocks
7. **Break Down Complex Tasks**: When working with tools (file access, search, etc.), break complex tasks into smaller, manageable steps. If a task requires many operations:
- Complete one logical step at a time
- Present findings or progress after each step
- Ask the user if you should continue to the next step
- Be mindful of tool usage limits (typically 25-30 tool calls per request)
8. **Incremental Progress**: For large codebases or complex analyses, work incrementally. Don't try to explore everything at once. Focus on what's immediately relevant to the user's question.
Remember: It's better to ask questions or admit uncertainty than to provide incorrect or fabricated information. Remember: It's better to ask questions or admit uncertainty than to provide incorrect or fabricated information.
""" """