t3-code-android-nightly/apps/mobile/modules/t3-composer-editor/ios/T3ComposerEditorModule.swift
Julius Marminge de34391427
feat(orchestrator): introduce new orchestrator (#2829)
Co-authored-by: maria-rcks <maria@kuuro.net>
Co-authored-by: Bilal Bakr <62337003+Bil0000@users.noreply.github.com>
Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com>
Co-authored-by: Vitalii Yehorov <vitalyiegorov@gmail.com>
Co-authored-by: Jake Leventhal <jakeleventhal@me.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Alex Southwell <saphid@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Nicholas Wasmiller <derped@mineperial.com>
Co-authored-by: PB <poilmb@gmail.com>
Co-authored-by: Exotic <118054752+extoci@users.noreply.github.com>
Co-authored-by: Yash Singh <saiansh2525@gmail.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Theo Browne <me@t3.gg>
Co-authored-by: Gabriel De Andrade <30420087+gabrielelpidio@users.noreply.github.com>
Co-authored-by: Dara Adedeji <76637177+SunkenInTime@users.noreply.github.com>
Co-authored-by: scratchyone <scratchywon@gmail.com>
Co-authored-by: Dominic Roy <dominic@sdko.org>
Co-authored-by: chukfinley <chuk@chuk.dev>
Co-authored-by: Primož Ajdišek <bigpod@bigpod.si>
Co-authored-by: benthecarman <benthecarman@live.com>
Co-authored-by: NaveDanan <nave0712@gmail.com>
Co-authored-by: aaditagrawal <103925638+aaditagrawal@users.noreply.github.com>
Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com>
Co-authored-by: Nick Anisimov <n.anisimov.23@gmail.com>
Co-authored-by: MacKinley Smith <smithmackinley@gmail.com>
Co-authored-by: Yordis Prieto <yordis.prieto@gmail.com>
Co-authored-by: t3-code[bot] <269035359+t3-code[bot]@users.noreply.github.com>
Co-authored-by: AKolenda <akole779@mtroyal.ca>
Co-authored-by: Guillermo Casanova <75276669+Gigioxx@users.noreply.github.com>
Co-authored-by: Otavio Salvador <otavio@ossystems.com.br>
Co-authored-by: Shirish Pothi <183252392+shirishpothi@users.noreply.github.com>
Co-authored-by: Ishaan Kothari <ishaanko.mail@gmail.com>
Co-authored-by: Bob Fowler <bob@rjf.ca>
Co-authored-by: Anton Bezdenezhnykh <gamer392@yandex.ru>
Co-authored-by: ValeraZSD <48602572+ValeraZSD@users.noreply.github.com>
Co-authored-by: Ephraim <ephraim39hr14m@gmail.com>
Co-authored-by: Ryan Ilano <ryanilano@users.noreply.github.com>
Co-authored-by: Alex <me@pixp.cc>
Co-authored-by: maco <gosarmarcel7@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Tristan Knight <admin@snappeh.com>
Co-authored-by: PR Batch Tester <agent@local.test>
Co-authored-by: oliver <97427849+flamboh@users.noreply.github.com>
Co-authored-by: kamkm <99585688+Kamkmgamer@users.noreply.github.com>
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
2026-10-02 12:22:22 -07:00

134 lines
5.5 KiB
Swift

import ExpoModulesCore
import UIKit
enum T3ComposerClipboard {
static let fragmentType = "app.t3.context-fragment"
static func write(text: String, fragment: String) {
var items: [String: Any] = ["public.utf8-plain-text": text]
if let data = fragment.data(using: .utf8),
var payload = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let records = payload["records"] as? [[String: Any]] {
var selected = records.filter { record in
guard let id = record["contextId"] as? String else { return false }
return text.contains("/\(id))")
}
let screenshots = Set(selected.compactMap { $0["screenshotContextId"] as? String })
selected.append(contentsOf: records.filter { screenshots.contains($0["contextId"] as? String ?? "") && !text.contains("/\($0["contextId"] as? String ?? ""))") })
payload["records"] = selected
if !selected.isEmpty, let encoded = try? JSONSerialization.data(withJSONObject: payload), let raw = String(data: encoded, encoding: .utf8) {
let attribute = raw.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? ""
let escaped = text.replacingOccurrences(of: "&", with: "&amp;").replacingOccurrences(of: "<", with: "&lt;").replacingOccurrences(of: ">", with: "&gt;")
items[fragmentType] = encoded
items["public.html"] = Data("<pre data-t3-context-fragment=\"\(attribute)\">\(escaped)</pre>".utf8)
}
}
UIPasteboard.general.items = [items]
}
static func read() -> [String: String] {
let board = UIPasteboard.general
return [
"text": board.string ?? "",
"fragment": board.data(forPasteboardType: fragmentType).flatMap { String(data: $0, encoding: .utf8) } ?? "",
"html": board.data(forPasteboardType: "public.html").flatMap { String(data: $0, encoding: .utf8) } ?? "",
]
}
}
// Expo Modules 2.0: the clipboard function is a @JS member; the view stays in
// definition() until the 2.0 view API lands.
@ExpoModule("T3ComposerEditor")
public class T3ComposerEditorModule: Module {
// UIPasteboard is main-thread only, so the member is main-actor isolated.
@JS
@MainActor
func writeContextClipboard(text: String, fragment: String) async {
T3ComposerClipboard.write(text: text, fragment: fragment)
}
public func definition() -> ModuleDefinition {
View(T3ComposerEditorView.self) {
Prop("controlledDocumentJson") { (view: T3ComposerEditorView, documentJson: String) in
view.setControlledDocumentJson(documentJson)
}
Prop("themeJson") { (view: T3ComposerEditorView, themeJson: String) in
view.setThemeJson(themeJson)
}
Prop("clipboardFragment") { (view: T3ComposerEditorView, fragment: String) in
view.setClipboardFragment(fragment)
}
Prop("placeholder") { (view: T3ComposerEditorView, placeholder: String) in
view.setPlaceholder(placeholder)
}
Prop("fontFamily") { (view: T3ComposerEditorView, fontFamily: String) in
view.setFontFamily(fontFamily)
}
Prop("fontSize") { (view: T3ComposerEditorView, fontSize: Double) in
view.setFontSize(CGFloat(fontSize))
}
Prop("lineHeight") { (view: T3ComposerEditorView, lineHeight: Double) in
view.setLineHeight(CGFloat(lineHeight))
}
Prop("contentInsetVertical") { (view: T3ComposerEditorView, contentInsetVertical: Double) in
view.setContentInsetVertical(CGFloat(contentInsetVertical))
}
Prop("editable") { (view: T3ComposerEditorView, editable: Bool) in
view.setEditable(editable)
}
Prop("readOnly") { (view: T3ComposerEditorView, readOnly: Bool) in
view.setReadOnly(readOnly)
}
Prop("scrollEnabled") { (view: T3ComposerEditorView, scrollEnabled: Bool) in
view.setScrollEnabled(scrollEnabled)
}
Prop("autoFocus") { (view: T3ComposerEditorView, autoFocus: Bool) in
view.setAutoFocus(autoFocus)
}
Prop("autoCorrect") { (view: T3ComposerEditorView, autoCorrect: Bool) in
view.setAutoCorrect(autoCorrect)
}
Prop("spellCheck") { (view: T3ComposerEditorView, spellCheck: Bool) in
view.setSpellCheck(spellCheck)
}
Prop("submitTitle") { (view: T3ComposerEditorView, title: String) in
view.setSubmitTitle(title)
}
Prop("alternateSubmitTitle") { (view: T3ComposerEditorView, title: String) in
view.setAlternateSubmitTitle(title)
}
Prop("enterBehavior") { (view: T3ComposerEditorView, behavior: String) in
view.setEnterBehavior(behavior)
}
Prop("textPasteThresholdBytes") { (view: T3ComposerEditorView, threshold: Int) in
view.setTextPasteThresholdBytes(threshold)
}
Prop("maxInputChars") { (view: T3ComposerEditorView, maxInputChars: Int) in
view.setMaxInputChars(maxInputChars)
}
Events(
"onComposerChange",
"onComposerSelectionChange",
"onComposerFocus",
"onComposerBlur",
"onComposerSubmit",
"onComposerPasteImages",
"onComposerContextPress",
"onComposerPasteContext",
"onComposerPasteText",
"onComposerContentSizeChange"
)
AsyncFunction("focus") { (view: T3ComposerEditorView) in
view.focusEditor()
}
AsyncFunction("blur") { (view: T3ComposerEditorView) in
view.blurEditor()
}
AsyncFunction("setSelection") { (view: T3ComposerEditorView, start: Int, end: Int) in
view.setSelection(start: start, end: end)
}
}
}
}