import Foundation import Capacitor @objc(NativeInputPlugin) public class NativeInputPlugin: CAPPlugin { private var bar: ChatInputBar? private var currentChatId: String? // MARK: Helpers private func hostView() -> UIView? { // Pin the bar as a sibling of the web view inside the same parent so it // can float above the web content and track the keyboard. return webView?.superview ?? webView } private func ensureBar() -> ChatInputBar? { if let bar = bar { return bar } guard let host = hostView() else { return nil } let bar = ChatInputBar(hostView: host) bar.onTextChange = { [weak self] text in self?.notifyListeners("inputChange", data: ["text": text]) } bar.onSend = { [weak self] text in self?.notifyListeners("send", data: ["text": text]) } bar.onAttach = { [weak self] in self?.notifyListeners("attach", data: [:]) } bar.onFocusChange = { [weak self] focused in self?.notifyListeners("focusChange", data: ["focused": focused]) } bar.onInsetChange = { [weak self] inset in self?.notifyListeners("heightChange", data: ["bottomInset": Double(inset)]) } bar.attach() self.bar = bar return bar } // MARK: Plugin methods @objc func present(_ call: CAPPluginCall) { let chatId = call.getString("chatId") ?? "" let placeholder = call.getString("placeholder") ?? "" let text = call.getString("text") ?? "" let canSend = call.getBool("canSend") ?? true let theme = call.getObject("theme") DispatchQueue.main.async { guard let bar = self.ensureBar() else { call.reject("No host view"); return } bar.setVisible(true) if let theme = theme { bar.applyTheme(theme) } bar.setPlaceholder(placeholder) bar.setEnabled(canSend) // Only reset text when switching to a different chat; otherwise keep // whatever the user already had typed in this bar. if self.currentChatId != chatId { bar.text = text self.currentChatId = chatId } call.resolve() } } @objc func dismiss(_ call: CAPPluginCall) { DispatchQueue.main.async { self.bar?.removeFromHost() self.bar = nil self.currentChatId = nil call.resolve() } } @objc func setText(_ call: CAPPluginCall) { let text = call.getString("text") ?? "" DispatchQueue.main.async { self.bar?.text = text call.resolve() } } @objc func getText(_ call: CAPPluginCall) { DispatchQueue.main.async { call.resolve(["text": self.bar?.text ?? ""]) } } @objc func focus(_ call: CAPPluginCall) { DispatchQueue.main.async { self.bar?.focus(); call.resolve() } } @objc func blur(_ call: CAPPluginCall) { DispatchQueue.main.async { self.bar?.blur(); call.resolve() } } @objc func setPlaceholder(_ call: CAPPluginCall) { let placeholder = call.getString("placeholder") ?? "" DispatchQueue.main.async { self.bar?.setPlaceholder(placeholder); call.resolve() } } @objc func setEnabled(_ call: CAPPluginCall) { let enabled = call.getBool("enabled") ?? true DispatchQueue.main.async { self.bar?.setEnabled(enabled); call.resolve() } } @objc func setTheme(_ call: CAPPluginCall) { let theme = call.getObject("theme") ?? [:] DispatchQueue.main.async { self.bar?.applyTheme(theme); call.resolve() } } @objc func setVisible(_ call: CAPPluginCall) { let visible = call.getBool("visible") ?? true DispatchQueue.main.async { self.bar?.setVisible(visible); call.resolve() } } }