mirror of
https://github.com/hanzoai/bot.git
synced 2026-10-30 17:24:26 +00:00
Three coordinated things:
1. Resolve the committed upstream/main merge conflicts. 94 files were
sitting with raw <<<<<<< HEAD ... >>>>>>> upstream/main blocks. Every
conflict had HanzoBot/Bot on HEAD and OpenClaw on upstream. Resolved
by keeping HEAD everywhere — 540 conflict blocks.
2. Rewrite remaining OpenClaw text refs.
OPENCLAW → HANZO_BOT
OpenClaw → HanzoBot (PascalCase type identifiers)
openclaw → bot (codebase convention — botToken,
botUsername etc.; avoids invalid
hyphenated JS identifiers)
openclaw[_]/openclaw[A-Z] → bot[_]/bot[A-Z] (compound)
ai.openclaw.x → ai.hanzo.bot.x (JVM package path)
3. Delete or rename openclaw-named file paths (116 of them). Dead
duplicates with a Bot-named canonical from the partial migration:
deleted. Otherwise renamed.
ai/openclaw/** → deleted (ai/hanzo/bot/** canonical)
Sources/OpenClaw*/ → deleted (Sources/Bot* canonical)
Tests/OpenClawIPCTests/ → deleted (Tests/BotIPCTests canonical)
OpenClawKit/ → deleted (BotKit canonical)
openclaw-tools.*.ts → deleted (bot-tools.*.ts canonical)
openclaw-root.ts etc. → deleted (bot-root.ts canonical)
types.openclaw.ts → deleted (types.bot.ts canonical)
extensions/*/openclaw.plugin.json → renamed hanzo-bot.plugin.json
docs/start/openclaw.md → renamed hanzo-bot.md (+ zh-CN)
docs/assets/openclaw-*.png, whatsapp-openclaw*.jpg → deleted
scripts/.../openclaw-* → deleted (bot/hanzo-bot parallels)
openclaw.mjs → deleted (hanzo-bot.mjs is package.bin)
Then patched 65 broken JS/TS import strings where hanzo-bot had ended
up inside an import path ('./types.hanzo-bot.js' → './types.bot.js'
etc., since the on-disk filename uses the brand-neutral bot- prefix).
Pre-existing lint debt cleaned up to get oxlint --type-aware to 0/0:
removed the dead i18n test that referenced a path no longer existing;
defined the missing DIDConfig and WalletConfig types in types.base.ts
(they were imported but never declared); dropped unused imports;
collapsed redundant type assertion and a tautological meta.bot lookup
that the openclaw→bot rename made redundant.
The a2ui.bundle.js generated artifact is kept at its pre-rebrand
contents — it gets regenerated by 'pnpm canvas:a2ui:bundle' from
sources I cannot rebuild in this commit; the source side is clean.
Verified: 0 occurrences of openclaw (case-insensitive) in tree source
content, 0 file or directory paths with openclaw in the name; oxlint
--type-aware src test reports 0/0.
194 lines
6.5 KiB
Swift
194 lines
6.5 KiB
Swift
import BotKit
|
|
import SwiftUI
|
|
import WebKit
|
|
|
|
struct ScreenWebView: UIViewRepresentable {
|
|
var controller: ScreenController
|
|
|
|
func makeCoordinator() -> ScreenWebViewCoordinator {
|
|
ScreenWebViewCoordinator(controller: self.controller)
|
|
}
|
|
|
|
func makeUIView(context: Context) -> UIView {
|
|
context.coordinator.makeContainerView()
|
|
}
|
|
|
|
func updateUIView(_: UIView, context: Context) {
|
|
context.coordinator.updateController(self.controller)
|
|
}
|
|
|
|
static func dismantleUIView(_: UIView, coordinator: ScreenWebViewCoordinator) {
|
|
coordinator.teardown()
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
final class ScreenWebViewCoordinator: NSObject {
|
|
private weak var controller: ScreenController?
|
|
private let navigationDelegate = ScreenNavigationDelegate()
|
|
private let a2uiActionHandler = CanvasA2UIActionMessageHandler()
|
|
private let userContentController = WKUserContentController()
|
|
|
|
private(set) var managedWebView: WKWebView?
|
|
private weak var containerView: UIView?
|
|
|
|
init(controller: ScreenController) {
|
|
self.controller = controller
|
|
super.init()
|
|
self.navigationDelegate.controller = controller
|
|
self.a2uiActionHandler.controller = controller
|
|
}
|
|
|
|
func makeContainerView() -> UIView {
|
|
if let containerView {
|
|
return containerView
|
|
}
|
|
|
|
let container = UIView(frame: .zero)
|
|
container.backgroundColor = .black
|
|
|
|
let webView = Self.makeWebView(userContentController: self.userContentController)
|
|
webView.navigationDelegate = self.navigationDelegate
|
|
self.installA2UIHandlers()
|
|
|
|
webView.translatesAutoresizingMaskIntoConstraints = false
|
|
container.addSubview(webView)
|
|
NSLayoutConstraint.activate([
|
|
webView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
|
|
webView.trailingAnchor.constraint(equalTo: container.trailingAnchor),
|
|
webView.topAnchor.constraint(equalTo: container.topAnchor),
|
|
webView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
|
|
])
|
|
|
|
self.managedWebView = webView
|
|
self.containerView = container
|
|
self.controller?.attachWebView(webView)
|
|
return container
|
|
}
|
|
|
|
func updateController(_ controller: ScreenController) {
|
|
let previousController = self.controller
|
|
let controllerChanged = self.controller !== controller
|
|
self.controller = controller
|
|
self.navigationDelegate.controller = controller
|
|
self.a2uiActionHandler.controller = controller
|
|
if controllerChanged, let managedWebView {
|
|
previousController?.detachWebView(managedWebView)
|
|
controller.attachWebView(managedWebView)
|
|
}
|
|
}
|
|
|
|
func teardown() {
|
|
if let managedWebView {
|
|
self.controller?.detachWebView(managedWebView)
|
|
managedWebView.navigationDelegate = nil
|
|
}
|
|
self.removeA2UIHandlers()
|
|
self.navigationDelegate.controller = nil
|
|
self.a2uiActionHandler.controller = nil
|
|
self.managedWebView = nil
|
|
self.containerView = nil
|
|
}
|
|
|
|
private static func makeWebView(userContentController: WKUserContentController) -> WKWebView {
|
|
let config = WKWebViewConfiguration()
|
|
config.websiteDataStore = .nonPersistent()
|
|
config.userContentController = userContentController
|
|
|
|
let webView = WKWebView(frame: .zero, configuration: config)
|
|
// Canvas scaffold is a fully self-contained HTML page; avoid relying on transparency underlays.
|
|
webView.isOpaque = true
|
|
webView.backgroundColor = .black
|
|
|
|
let scrollView = webView.scrollView
|
|
scrollView.backgroundColor = .black
|
|
scrollView.contentInsetAdjustmentBehavior = .never
|
|
scrollView.contentInset = .zero
|
|
scrollView.scrollIndicatorInsets = .zero
|
|
scrollView.automaticallyAdjustsScrollIndicatorInsets = false
|
|
|
|
return webView
|
|
}
|
|
|
|
private func installA2UIHandlers() {
|
|
for name in CanvasA2UIActionMessageHandler.handlerNames {
|
|
self.userContentController.add(self.a2uiActionHandler, name: name)
|
|
}
|
|
}
|
|
|
|
private func removeA2UIHandlers() {
|
|
for name in CanvasA2UIActionMessageHandler.handlerNames {
|
|
self.userContentController.removeScriptMessageHandler(forName: name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Navigation Delegate
|
|
|
|
/// Handles navigation policy to intercept bot:// deep links from canvas
|
|
@MainActor
|
|
private final class ScreenNavigationDelegate: NSObject, WKNavigationDelegate {
|
|
weak var controller: ScreenController?
|
|
|
|
func webView(
|
|
_: WKWebView,
|
|
decidePolicyFor navigationAction: WKNavigationAction,
|
|
decisionHandler: @escaping @MainActor @Sendable (WKNavigationActionPolicy) -> Void)
|
|
{
|
|
guard let url = navigationAction.request.url else {
|
|
decisionHandler(.allow)
|
|
return
|
|
}
|
|
|
|
// Intercept bot:// deep links.
|
|
if url.scheme?.lowercased() == "hanzo-bot" {
|
|
decisionHandler(.cancel)
|
|
self.controller?.onDeepLink?(url)
|
|
return
|
|
}
|
|
|
|
decisionHandler(.allow)
|
|
}
|
|
|
|
func webView(
|
|
_: WKWebView,
|
|
didFailProvisionalNavigation _: WKNavigation?,
|
|
withError error: any Error)
|
|
{
|
|
self.controller?.errorText = error.localizedDescription
|
|
}
|
|
|
|
func webView(_: WKWebView, didFinish _: WKNavigation?) {
|
|
self.controller?.errorText = nil
|
|
self.controller?.applyDebugStatusIfNeeded()
|
|
}
|
|
|
|
func webView(_: WKWebView, didFail _: WKNavigation?, withError error: any Error) {
|
|
self.controller?.errorText = error.localizedDescription
|
|
}
|
|
}
|
|
|
|
private final class CanvasA2UIActionMessageHandler: NSObject, WKScriptMessageHandler {
|
|
static let messageName = "botCanvasA2UIAction"
|
|
static let handlerNames = [messageName]
|
|
|
|
weak var controller: ScreenController?
|
|
|
|
func userContentController(_: WKUserContentController, didReceive message: WKScriptMessage) {
|
|
guard Self.handlerNames.contains(message.name) else { return }
|
|
guard let controller else { return }
|
|
|
|
guard let url = message.webView?.url else { return }
|
|
if url.isFileURL {
|
|
guard controller.isTrustedCanvasUIURL(url) else { return }
|
|
} else {
|
|
// For security, only accept actions from local-network pages (e.g. the canvas host).
|
|
guard controller.isLocalNetworkCanvasURL(url) else { return }
|
|
}
|
|
|
|
guard let body = ScreenController.parseA2UIActionBody(message.body) else { return }
|
|
|
|
controller.onA2UIAction?(body)
|
|
}
|
|
}
|