mirror of
https://github.com/block/buzz.git
synced 2026-11-06 17:31:19 +00:00
## Summary Adds Buzzies and refreshes the agent avatar picker with larger previews, rounded option strips, and clearer category choices. Also moves Agents above Appearance in the home menu and adds a custom agent icon. ### Related issue None found. ### Testing Analysis, 4,138 app tests, package tests, and 121 configured-auth tests pass. Earlier visual build exercised on Pixel and iOS simulator, with iPhone review; snapshots below show that build. | Android · Pixel | iOS · simulator preview | | --- | --- | |  |  | --------- Signed-off-by: kenny lopez <klopez4212@gmail.com> Co-authored-by: Terp <5856f6900e8e0a4e0ea1f4f8d2ee33e4753d2119bc122f94ecafc660b46c0511@buzz.block.builderlab.xyz>
127 lines
5.2 KiB
Swift
127 lines
5.2 KiB
Swift
import UIKit
|
|
import CoreText
|
|
import Flutter
|
|
|
|
/// Renders the same bundled Tabler font used by Flutter into template images.
|
|
/// Legacy symbol keys are kept at the platform-channel boundary only.
|
|
enum NativeTabler {
|
|
private static let fontName: String? = {
|
|
let key = FlutterDartProject.lookupKey(forAsset: "assets/fonts/TablerIcons.ttf")
|
|
let appBundle = Bundle(url: Bundle.main.bundleURL.appendingPathComponent("Frameworks/App.framework"))
|
|
guard let url = appBundle?.url(forResource: key, withExtension: nil) ?? Bundle.main.url(forResource: key, withExtension: nil),
|
|
let provider = CGDataProvider(url: url as CFURL),
|
|
let font = CGFont(provider) else { return nil }
|
|
CTFontManagerRegisterGraphicsFont(font, nil)
|
|
return font.postScriptName as String?
|
|
}()
|
|
|
|
static func image(_ symbol: String, size: CGFloat = 20) -> UIImage? {
|
|
if symbol == "agent" { return agentImage(size: size) }
|
|
guard let code = glyphs[symbol], let scalar = UnicodeScalar(code),
|
|
let name = fontName, let font = UIFont(name: name, size: size) else {
|
|
assertionFailure("Missing Tabler icon: \(symbol)")
|
|
return nil
|
|
}
|
|
let text = String(scalar) as NSString
|
|
let attributes: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: UIColor.black]
|
|
let measured = text.size(withAttributes: attributes)
|
|
return UIGraphicsImageRenderer(size: CGSize(width: size, height: size)).image { _ in
|
|
text.draw(at: CGPoint(x: (size - measured.width) / 2, y: (size - measured.height) / 2), withAttributes: attributes)
|
|
}.withRenderingMode(.alwaysTemplate)
|
|
}
|
|
|
|
/// Matches Flutter's AgentNavigationIcon and the desktop agent mark.
|
|
private static func agentImage(size: CGFloat) -> UIImage {
|
|
UIGraphicsImageRenderer(size: CGSize(width: size, height: size)).image { renderer in
|
|
renderer.cgContext.scaleBy(x: size / 24, y: size / 24)
|
|
UIColor.black.setStroke()
|
|
UIColor.black.setFill()
|
|
let head = UIBezierPath(roundedRect: CGRect(x: 4.75, y: 7, width: 14.5, height: 13.25), cornerRadius: 4.5)
|
|
head.lineWidth = 2
|
|
head.stroke()
|
|
let lines = UIBezierPath()
|
|
lines.lineWidth = 2
|
|
lines.lineCapStyle = .round
|
|
for (start, end) in [
|
|
(CGPoint(x: 12, y: 7), CGPoint(x: 12, y: 2.5)),
|
|
(CGPoint(x: 3.25, y: 11.5), CGPoint(x: 3.25, y: 15.75)),
|
|
(CGPoint(x: 20.75, y: 11.5), CGPoint(x: 20.75, y: 15.75))
|
|
] {
|
|
lines.move(to: start)
|
|
lines.addLine(to: end)
|
|
}
|
|
lines.stroke()
|
|
for x in [9.0, 15.0] {
|
|
UIBezierPath(ovalIn: CGRect(x: x - 1.4, y: 11.85, width: 2.8, height: 2.8)).fill()
|
|
}
|
|
}.withRenderingMode(.alwaysTemplate)
|
|
}
|
|
|
|
private static let glyphs: [String: UInt32] = [
|
|
"arrow.down": 0xea16, // arrow-down
|
|
"xmark": 0xeb55, // x
|
|
"xmark.circle.fill": 0xea6a, // circle-x
|
|
"chevron.backward": 0xea60, // chevron-left
|
|
"chevron.left": 0xea60, // chevron-left
|
|
"chevron.right": 0xea61, // chevron-right
|
|
"camera": 0xea54, // camera
|
|
"photo.on.rectangle": 0xfd4a, // images — matches BuzzIcons.images
|
|
"photo.on.rectangle.angled": 0xfd4a, // images — matches BuzzIcons.images
|
|
"paintpalette": 0xeb01, // palette
|
|
"drop": 0xea97, // droplet
|
|
"drop.fill": 0xea97, // droplet
|
|
"face.smiling": 0xeaf7, // mood-smile
|
|
"person": 0xeb4d, // user
|
|
"person.crop.circle": 0xeb4d, // user
|
|
"arrow.left.arrow.right": 0xf1f4, // arrows-left-right
|
|
"person.badge.plus": 0xeb4b, // user-plus
|
|
"gearshape": 0xeb20, // settings
|
|
"person.2": 0xebf2, // users
|
|
"rectangle.stack": 0xeb0a, // photo
|
|
"crop": 0xea85, // crop
|
|
"arrow.triangle.2.circlepath.camera": 0xee22, // camera-rotate
|
|
"circle": 0xea6b, // circle
|
|
"circle.fill": 0xea6b, // circle
|
|
"sun.max": 0xeb30, // sun
|
|
"moon": 0xeaf8, // moon
|
|
"ellipsis": 0xea95, // dots
|
|
"arrowshape.turn.up.left": 0xeb77, // arrow-back-up
|
|
"circle.lefthalf.filled": 0xf4a3, // sun-moon
|
|
"magnifyingglass": 0xeb1c, // search
|
|
"tray": 0xeac4, // inbox
|
|
"sparkles": 0xf6d7, // sparkles
|
|
"clock": 0xea70, // clock
|
|
"leaf": 0xed4f, // leaf
|
|
"fork.knife": 0xeeff, // tools-kitchen-2
|
|
"figure.run": 0xec82, // run
|
|
"airplane": 0xeb6f, // plane
|
|
"lightbulb": 0xea51, // bulb
|
|
"heart": 0xeabe, // heart
|
|
"flag": 0xeaa6, // flag
|
|
"video": 0xed22, // video
|
|
"mic": 0xeaf0, // microphone
|
|
"doc": 0xeaa4, // file
|
|
"plus": 0xeb0b, // plus
|
|
"building.2.crop.circle": 0xebf6, // building-community
|
|
"workspace": 0xecd7, // cube-3d-sphere
|
|
"folder.badge.plus": 0xeaab, // folder-plus
|
|
"headphones": 0xeabd, // headphones
|
|
"line.3.horizontal.decrease": 0xeaa5, // filter
|
|
"square.and.pencil": 0xea98, // edit
|
|
"pencil": 0xea98, // edit
|
|
"doc.on.doc": 0xea7a, // copy
|
|
"trash": 0xeb41, // trash
|
|
"pin": 0xec9c, // pin
|
|
"pin.slash": 0xed5f, // pinned-off
|
|
"square.and.arrow.up": 0xeb21, // share
|
|
"link": 0xeade, // link
|
|
"checkmark": 0xea5e, // check
|
|
"checkmark.circle": 0xea67, // circle-check
|
|
"bell.slash": 0xece9, // bell-off
|
|
"number": 0xeabc, // hash — matches BuzzIcons.hash
|
|
"bubble.left": 0xeaef, // message
|
|
"bubble.left.and.bubble.right": 0xeb6c, // messages
|
|
"arrow.uturn.backward": 0xeb77, // arrow-back-up
|
|
]
|
|
}
|