Skip to content

Commit 0945ad7

Browse files
authored
Merge pull request #964 from DimensionDev/ios/20250530
add Text Render Engine
2 parents bf29632 + 5c3d834 commit 0945ad7

File tree

9 files changed

+155
-37
lines changed

9 files changed

+155
-37
lines changed

iosApp/iosApp/Localizable.xcstrings

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40872,7 +40872,36 @@
4087240872
}
4087340873
}
4087440874
}
40875+
},
40876+
"Text Render Engine" : {
40877+
"localizations" : {
40878+
"ar" : { "stringUnit" : { "state" : "translated", "value" : "محرك عرض النصوص" } },
40879+
"cs" : { "stringUnit" : { "state" : "translated", "value" : "Renderovací jádro textu" } },
40880+
"da" : { "stringUnit" : { "state" : "translated", "value" : "Tekstgengivelsesmotor" } },
40881+
"de" : { "stringUnit" : { "state" : "translated", "value" : "Text-Rendering-Engine" } },
40882+
"el" : { "stringUnit" : { "state" : "translated", "value" : "Μηχανή Απόδοσης Κειμένου" } },
40883+
"es" : { "stringUnit" : { "state" : "translated", "value" : "Motor de renderizado de texto" } },
40884+
"fi" : { "stringUnit" : { "state" : "translated", "value" : "Tekstin renderöintimoottori" } },
40885+
"fr" : { "stringUnit" : { "state" : "translated", "value" : "Moteur de rendu de texte" } },
40886+
"it" : { "stringUnit" : { "state" : "translated", "value" : "Motore di rendering del testo" } },
40887+
"ja" : { "stringUnit" : { "state" : "translated", "value" : "テキストレンダリングエンジン" } },
40888+
"nl" : { "stringUnit" : { "state" : "translated", "value" : "Tekstweergave-engine" } },
40889+
"no" : { "stringUnit" : { "state" : "translated", "value" : "Tekstgjengivelsesmotor" } },
40890+
"pl" : { "stringUnit" : { "state" : "translated", "value" : "Silnik renderowania tekstu" } },
40891+
"pt-BR" : { "stringUnit" : { "state" : "translated", "value" : "Mecanismo de renderização de texto" } },
40892+
"pt-PT" : { "stringUnit" : { "state" : "translated", "value" : "Motor de renderização de texto" } },
40893+
"ro" : { "stringUnit" : { "state" : "translated", "value" : "Motor de randare text" } },
40894+
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Система визуализации текста" } },
40895+
"sv" : { "stringUnit" : { "state" : "translated", "value" : "Textåtergivningsmotor" } },
40896+
"uk" : { "stringUnit" : { "state" : "translated", "value" : "Рушій візуалізації тексту" } },
40897+
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "文本渲染引擎" } },
40898+
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "文字渲染引擎" } }
4087540899
}
40900+
}
40901+
40902+
40903+
40904+
4087640905
},
4087740906
"version" : "1.0"
4087840907
}
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +0,0 @@
1-
//
2-
// FlareMarkdown.swift
3-
// iosApp
4-
//
5-
// Created by abujj on 5/26/25.
6-
// Copyright © 2025 orgName. All rights reserved.
7-
//
Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +0,0 @@
1-
//
2-
// FlareMarkdownEmoji.swift
3-
// iosApp
4-
//
5-
// Created by abujj on 5/26/25.
6-
// Copyright © 2025 orgName. All rights reserved.
7-
//
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import MarkdownUI
2+
import SwiftUI
3+
4+
// 扩展的是 MarkdownUI.Theme
5+
extension MarkdownUI.Theme {
6+
static func flareMarkdownStyle(using style: FlareTextStyle.Style, fontScale: Double) -> MarkdownUI.Theme {
7+
// 获取基础字体大小
8+
let baseFontSize = style.font.pointSize
9+
// 应用用户的字体缩放设置
10+
let scaledFontSize = baseFontSize * fontScale
11+
12+
return MarkdownUI.Theme()
13+
.text {
14+
FontSize(scaledFontSize)
15+
ForegroundColor(Color(style.textColor))
16+
17+
if let fontName = style.font.fontName as String?, fontName != ".SFUI-Regular" {
18+
FontFamily(.custom(fontName))
19+
}
20+
}
21+
.link {
22+
ForegroundColor(Color(style.linkColor))
23+
// UnderlineStyle(.single)
24+
}
25+
.strong {
26+
// 粗体文本
27+
FontWeight(.semibold)
28+
ForegroundColor(Color(style.textColor))
29+
}
30+
.emphasis {
31+
// 斜体文本
32+
FontStyle(.italic)
33+
ForegroundColor(Color(style.textColor))
34+
}
35+
.code {
36+
// 行内代码
37+
FontFamilyVariant(.monospaced)
38+
FontSize(scaledFontSize * 0.9)
39+
ForegroundColor(Color(style.textColor))
40+
// BackgroundColor(Color.gray.opacity(0.1))
41+
}
42+
}
43+
}

iosApp/iosApp/UI/Page/Compose/Text/FlareText.swift

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import MarkdownUI
12
import SwiftUI
23
import TwitterText
34

@@ -7,6 +8,7 @@ public struct FlareText: View {
78
private let style: FlareTextStyle.Style
89
private var linkHandler: ((URL) -> Void)?
910
@Environment(FlareTheme.self) private var theme
11+
@Environment(\.appSettings) private var appSettings
1012

1113
public init(
1214
_ text: String,
@@ -25,16 +27,33 @@ public struct FlareText: View {
2527
}
2628

2729
public var body: some View {
28-
Text(AttributedString(processText(text, markdownText)))
29-
.multilineTextAlignment(.leading)
30-
.fixedSize(horizontal: false, vertical: true)
31-
.environment(\.openURL, OpenURLAction { url in
32-
if let handler = linkHandler {
33-
handler(url)
34-
}
35-
return .handled
36-
})
37-
// .environment(\.layoutDirection, isRTL() ? .rightToLeft : .leftToRight)
30+
if appSettings.appearanceSettings.renderEngine == .markdown {
31+
Markdown(markdownText)
32+
.markdownTheme(.flareMarkdownStyle(using: style, fontScale: theme.fontSizeScale))
33+
.markdownInlineImageProvider(.emoji)
34+
.relativeLineSpacing(.em(theme.lineSpacing - 1.0)) // 转换为相对行高
35+
.padding(.vertical, 4)
36+
.environment(\.layoutDirection, isRTL() ? .rightToLeft : .leftToRight)
37+
.environment(\.openURL, OpenURLAction { url in
38+
if let handler = linkHandler {
39+
handler(url)
40+
return .handled
41+
} else {
42+
return .systemAction
43+
}
44+
})
45+
} else {
46+
Text(AttributedString(processText(text, markdownText)))
47+
.multilineTextAlignment(.leading)
48+
.fixedSize(horizontal: false, vertical: true)
49+
.environment(\.openURL, OpenURLAction { url in
50+
if let handler = linkHandler {
51+
handler(url)
52+
}
53+
return .handled
54+
})
55+
.environment(\.layoutDirection, isRTL() ? .rightToLeft : .leftToRight)
56+
}
3857
}
3958

4059
private func processText(_ text: String, _ markdownText: String) -> NSAttributedString {
@@ -46,8 +65,9 @@ public struct FlareText: View {
4665
return NSAttributedString(attributedString)
4766
}
4867

49-
// private func isRTL() -> Bool {
50-
// // Arabic, Hebrew, Persian, Urdu, Kurdish, Azeri, Dhivehi
51-
// ["ar", "he", "fa", "ur", "ku", "az", "dv"].contains(language)
52-
// }
68+
private func isRTL() -> Bool {
69+
let language = Locale.current.language.languageCode?.identifier ?? ""
70+
let isRTL: Bool = ["ar", "he", "fa", "ur", "ku", "az", "dv"].contains(language)
71+
return isRTL
72+
}
5373
}

iosApp/iosApp/UI/Page/Compose/Text/FlareTextStyle.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,23 +161,23 @@ public enum FlareTextStyle {
161161
attributes[.link] = url
162162
attributes[.foregroundColor] = entity.type.color(style)
163163
case let .mention(username):
164-
// @
164+
// @ mention
165165
if let flareLink = markdownLinks[username] {
166166
attributes[.link] = flareLink
167167
} else if let url = URL(string: "https://twitter.com/\(username.dropFirst())") {
168168
attributes[.link] = url
169169
}
170170
attributes[.foregroundColor] = entity.type.color(style)
171171
case let .hashtag(tag):
172-
// #
172+
// # tag
173173
if let flareLink = markdownLinks[tag] {
174174
attributes[.link] = flareLink
175175
} else if let url = URL(string: "https://twitter.com/hashtag/\(tag.dropFirst())") {
176176
attributes[.link] = url
177177
}
178178
attributes[.foregroundColor] = entity.type.color(style)
179179
case let .cashtag(symbol):
180-
// $ todo:
180+
// $ search
181181
if let flareLink = markdownLinks[symbol] {
182182
attributes[.link] = flareLink
183183
} else if let url = URL(string: "https://twitter.com/search?q=%24\(symbol.dropFirst())") {

iosApp/iosApp/UI/Page/Settings/Model/AppearanceSettings.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Foundation
33
struct AppearanceSettings: Codable, Changeable {
44
var theme: Theme = .auto
55
var avatarShape: AvatarShape = .circle
6+
var renderEngine: RenderEngine = .markdown
67
var showActions: Bool = true
78
var showNumbers: Bool = true
89
var showLinkPreview: Bool = true
@@ -52,6 +53,20 @@ struct AppearanceSettings: Codable, Changeable {
5253
}
5354
}
5455

56+
enum RenderEngine: Codable, CaseIterable {
57+
case markdown
58+
case flareText
59+
60+
var title: String {
61+
switch self {
62+
case .markdown:
63+
"Markdown"
64+
case .flareText:
65+
"FlareText"
66+
}
67+
}
68+
}
69+
5570
enum Theme: Codable {
5671
case auto
5772
case light

iosApp/iosApp/UI/Page/Settings/View/AppearanceUIScreen.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ struct AppearanceUIScreen: View {
1515
ObservePresenter(presenter: presenter) { state in
1616
List {
1717
if case let .success(success) = onEnum(of: state.sampleStatus) {
18-
StatusItemView(
19-
data: success.data,
20-
detailKey: nil
21-
)
18+
VStack {
19+
StatusItemView(
20+
data: success.data,
21+
detailKey: nil
22+
)
23+
}.allowsHitTesting(false)
2224
}
2325

2426
// Theme部分
@@ -28,6 +30,21 @@ struct AppearanceUIScreen: View {
2830
// Font部分
2931
Section { fontSection }.listRowBackground(theme.primaryBackgroundColor)
3032

33+
// 渲染引擎选择部分
34+
Section("Text Render Engine") {
35+
Picker(selection: Binding(get: {
36+
appSettings.appearanceSettings.renderEngine
37+
}, set: { value in
38+
appSettings.update(newValue: appSettings.appearanceSettings.changing(path: \.renderEngine, to: value))
39+
}), content: {
40+
ForEach(RenderEngine.allCases, id: \.self) { engine in
41+
Text(engine.title).tag(engine)
42+
}
43+
}, label: {
44+
Text("Text Render Engine")
45+
})
46+
}.listRowBackground(theme.primaryBackgroundColor)
47+
3148
Section("settings_appearance_generic") {
3249
// 注释原主题选择
3350
/*

shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiStatus.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ private fun Token.toHtml(accountKey: MicroBlogKey): Node =
5151
}
5252
}
5353
}
54+
5455
is UrlToken ->
5556
Element("a").apply {
5657
attributes().put("href", value)
@@ -59,7 +60,14 @@ private fun Token.toHtml(accountKey: MicroBlogKey): Node =
5960

6061
is UserNameToken ->
6162
Element("a").apply {
62-
attributes().put("href", AppDeepLink.ProfileWithNameAndHost(accountKey, value.trimStart('@'), accountKey.host))
63+
attributes().put(
64+
"href",
65+
AppDeepLink.ProfileWithNameAndHost(
66+
accountKey,
67+
value.trimStart('@'),
68+
accountKey.host,
69+
),
70+
)
6371
addChildren(TextNode(value))
6472
}
6573
}
@@ -93,7 +101,7 @@ public fun createSampleStatus(user: UiUserV2): UiTimeline =
93101
.apply {
94102
appendChild(
95103
TextNode(
96-
"Sample content for ${user.name.raw} on ${user.key.host} 😊 \n https://github.com/dimensiondev/flare \n @realMaskNetwork #flare ",
104+
"Sample content for ${user.name.raw} on ${user.key.host} 😊 \n https://github.com/dimensiondev/flare \n \n [@realMaskNetwork](flare://ProfileWithNameAndHost/realMaskNetwork/twitter.com?accountKey=${user.key.id}) [#flare](flare://Search/%23flare?accountKey=${user.key.id}) [\$MASK](flare://Search/%23MASK?accountKey=${user.key.id}) ",
97105
),
98106
)
99107
}.toUi(),

0 commit comments

Comments
 (0)