Fixed propper MD rendering +++++
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -112,3 +112,5 @@ iOSInjectionProject/
|
|||||||
Network Trash Folder
|
Network Trash Folder
|
||||||
Temporary Items
|
Temporary Items
|
||||||
.apdisk
|
.apdisk
|
||||||
|
|
||||||
|
CLAUDE.md
|
||||||
26
Package.swift
Normal file
26
Package.swift
Normal 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
2
Sources/oAI/oAI.swift
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// The Swift Programming Language
|
||||||
|
// https://docs.swift.org/swift-book
|
||||||
6
Tests/oAITests/oAITests.swift
Normal file
6
Tests/oAITests/oAITests.swift
Normal 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.
|
||||||
|
}
|
||||||
@@ -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" */;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ struct AboutView: View {
|
|||||||
.padding(.horizontal, 40)
|
.padding(.horizontal, 40)
|
||||||
|
|
||||||
VStack(spacing: 4) {
|
VStack(spacing: 4) {
|
||||||
Text("© 2026 [Rune Olsen](https://blog.rune.pm)")
|
Text("© 2026 [Rune Olsen](https://blog.rune.pm) | [Support](mailto:support@fubar.pm)")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user