Skip to content

Commit 66a547f

Browse files
committed
Add Markdown & Code Syntax Highlighting to iOS target
1 parent 5438a69 commit 66a547f

File tree

10 files changed

+936
-26
lines changed

10 files changed

+936
-26
lines changed

README.MD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
This is a native iOS, macOS, watchOS, tvOS App for interacting with ChatGPT built using SwiftUI and OpenAPI API. It's using Official ChatGPT endpoint with `gpt-3.5-turbo` model.
66

7+
It is also able to render response with markdown and code syntax highlighting.
78

89
## Video tutorial
910
- [iOS YouTube](https://youtu.be/PLEgTCT20zU)

Shared/CodeBlockView.swift

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//
2+
// CodeBlockView.swift
3+
// XCAChatGPT
4+
//
5+
// Created by Alfian Losari on 19/04/23.
6+
//
7+
8+
import SwiftUI
9+
import Markdown
10+
11+
enum HighlighterConstants {
12+
static let color = Color(red: 38/255, green: 38/255, blue: 38/255)
13+
}
14+
15+
struct CodeBlockView: View {
16+
17+
let parserResult: ParserResult
18+
@State var isCopied = false
19+
20+
var body: some View {
21+
VStack(alignment: .leading) {
22+
header
23+
.padding(.horizontal)
24+
.padding(.vertical, 8)
25+
.background(Color(red: 9/255, green: 49/255, blue: 69/255))
26+
27+
ScrollView(.horizontal, showsIndicators: true) {
28+
Text(parserResult.attributedString)
29+
.padding(.horizontal, 16)
30+
.textSelection(.enabled)
31+
}
32+
}
33+
.background(HighlighterConstants.color)
34+
.cornerRadius(8)
35+
}
36+
37+
var header: some View {
38+
HStack {
39+
if let codeBlockLanguage = parserResult.codeBlockLanguage {
40+
Text(codeBlockLanguage.capitalized)
41+
.font(.headline.monospaced())
42+
.foregroundColor(.white)
43+
}
44+
Spacer()
45+
button
46+
}
47+
}
48+
49+
@ViewBuilder
50+
var button: some View {
51+
if isCopied {
52+
HStack {
53+
Text("Copied")
54+
.foregroundColor(.white)
55+
.font(.subheadline.monospaced().bold())
56+
Image(systemName: "checkmark.circle.fill")
57+
.imageScale(.large)
58+
.symbolRenderingMode(.multicolor)
59+
}
60+
.frame(alignment: .trailing)
61+
} else {
62+
Button {
63+
let string = NSAttributedString(parserResult.attributedString).string
64+
UIPasteboard.general.string = string
65+
withAnimation {
66+
isCopied = true
67+
}
68+
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
69+
withAnimation {
70+
isCopied = false
71+
}
72+
}
73+
} label: {
74+
Image(systemName: "doc.on.doc")
75+
}
76+
.foregroundColor(.white)
77+
}
78+
}
79+
}
80+
81+
struct CodeBlockView_Previews: PreviewProvider {
82+
83+
static var markdownString = """
84+
```swift
85+
let api = ChatGPTAPI(apiKey: "API_KEY")
86+
87+
Task {
88+
do {
89+
let stream = try await api.sendMessageStream(text: "What is ChatGPT?")
90+
for try await line in stream {
91+
print(line)
92+
}
93+
} catch {
94+
print(error.localizedDescription)
95+
}
96+
}
97+
```
98+
"""
99+
100+
static let parserResult: ParserResult = {
101+
let document = Document(parsing: markdownString)
102+
var parser = MarkdownAttributedStringParser()
103+
return parser.parserResults(from: document)[0]
104+
}()
105+
106+
static var previews: some View {
107+
CodeBlockView(parserResult: parserResult)
108+
}
109+
}
110+
111+

0 commit comments

Comments
 (0)