WIP: 嵌入式框架优化调整

This commit is contained in:
砍砍
2024-11-21 21:59:13 +08:00
parent 9d0de52609
commit d252de0c5a
9 changed files with 301 additions and 26 deletions

View File

@@ -33,7 +33,7 @@ class AFFiNEViewController: CAPBridgeViewController {
}
}
extension AFFiNEViewController: IntelligentsButtonDelegate {
extension AFFiNEViewController: IntelligentsButtonDelegate, IntelligentsFocusApertureViewDelegate {
func onIntelligentsButtonTapped(_ button: IntelligentsButton) {
guard let webView else {
assertionFailure() // ? wdym ?
@@ -56,37 +56,55 @@ extension AFFiNEViewController: IntelligentsButtonDelegate {
print("[?] \(self) script error: \(error.localizedDescription)")
}
if case let .success(content) = result,
let res = content as? String
{
print("[*] \(self) received document with \(res.count) characters")
DispatchQueue.main.async {
self.openIntelligentsSheet(withContext: res)
}
} else {
DispatchQueue.main.async {
self.openSimpleChat()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if case let .success(content) = result,
let res = content as? String
{
print("[*] \(self) received document with \(res.count) characters")
DispatchQueue.main.async {
self.openIntelligentsSheet(withContext: res)
}
} else {
DispatchQueue.main.async {
self.openSimpleChat()
}
}
}
}
}
func openIntelligentsSheet(withContext context: String) {
guard let view = webView else {
guard let view = webView?.subviews.first else {
assertionFailure()
return
}
assert(view is UIScrollView)
_ = context
let focus = IntelligentsFocusApertureView()
focus.prepareAnimationWith(
capturingTargetContentView: view,
coveringRootViewController: self
)
focus.delegate = self
focus.executeAnimationKickIn()
dismissIntelligentsButton()
}
func openSimpleChat() {
let targetController = IntelligentsChatController()
presentIntoCurrentContext(withTargetController: targetController)
}
func focusApertureRequestAction(actionType: IntelligentsFocusApertureViewActionType) {
switch actionType {
case .translateTo:
fatalError("not implemented")
case .summary:
fatalError("not implemented")
case .chatWithAI:
fatalError("not implemented")
case .dismiss:
presentIntelligentsButton()
}
}
}

View File

@@ -0,0 +1,33 @@
//
// Ext+UIFont.swift
// Intelligents
//
// Created by on 2024/11/21.
//
import UIKit
extension UIFont {
static func preferredFont(for style: TextStyle, weight: Weight, italic: Bool = false) -> UIFont {
// Get the style's default pointSize
let traits = UITraitCollection(preferredContentSizeCategory: .large)
let desc = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style, compatibleWith: traits)
// Get the font at the default size and preferred weight
var font = UIFont.systemFont(ofSize: desc.pointSize, weight: weight)
if italic == true {
font = font.with([.traitItalic])
}
// Setup the font to be auto-scalable
let metrics = UIFontMetrics(forTextStyle: style)
return metrics.scaledFont(for: font)
}
private func with(_ traits: UIFontDescriptor.SymbolicTraits...) -> UIFont {
guard let descriptor = fontDescriptor.withSymbolicTraits(UIFontDescriptor.SymbolicTraits(traits).union(fontDescriptor.symbolicTraits)) else {
return self
}
return UIFont(descriptor: descriptor, size: 0)
}
}

View File

@@ -19,6 +19,15 @@ extension UIView {
return nil
}
func removeEveryAutoResizingMasks() {
var views: [UIView] = [self]
while let view = views.first {
views.removeFirst()
view.translatesAutoresizingMaskIntoConstraints = false
view.subviews.forEach { views.append($0) }
}
}
#if DEBUG
func debugFrame() {
layer.borderWidth = 1

View File

@@ -0,0 +1,76 @@
//
// IntelligentsFocusApertureView+ActionButton.swift
// Intelligents
//
// Created by on 2024/11/21.
//
import UIKit
extension IntelligentsFocusApertureView.ControlButtonsPanel {
class DarkActionButton: UIView {
var iconSystemName: String {
set { iconView.image = UIImage(systemName: newValue) }
get { fatalError() }
}
var title: String {
set { titleLabel.text = newValue }
get { titleLabel.text ?? "" }
}
let titleLabel = UILabel()
let iconView = UIImageView()
var action: (() -> Void)? = nil
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .white.withAlphaComponent(0.25)
layer.cornerRadius = 12
let layoutGuide = UILayoutGuide()
addLayoutGuide(layoutGuide)
titleLabel.textAlignment = .center
titleLabel.font = .systemFont(ofSize: UIFont.labelFontSize, weight: .semibold)
titleLabel.textColor = .white
addSubview(titleLabel)
iconView.contentMode = .scaleAspectFit
iconView.tintColor = .white
addSubview(iconView)
[
layoutGuide.centerXAnchor.constraint(equalTo: centerXAnchor),
layoutGuide.centerYAnchor.constraint(equalTo: centerYAnchor),
iconView.topAnchor.constraint(greaterThanOrEqualTo: layoutGuide.topAnchor),
iconView.leadingAnchor.constraint(equalTo: layoutGuide.leadingAnchor),
iconView.bottomAnchor.constraint(lessThanOrEqualTo: layoutGuide.bottomAnchor),
iconView.centerYAnchor.constraint(equalTo: layoutGuide.centerYAnchor),
titleLabel.topAnchor.constraint(greaterThanOrEqualTo: layoutGuide.topAnchor),
titleLabel.trailingAnchor.constraint(equalTo: layoutGuide.trailingAnchor),
titleLabel.bottomAnchor.constraint(lessThanOrEqualTo: layoutGuide.bottomAnchor),
titleLabel.centerYAnchor.constraint(equalTo: layoutGuide.centerYAnchor),
titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 8),
].forEach { $0.isActive = true }
isUserInteractionEnabled = true
addGestureRecognizer(UITapGestureRecognizer(
target: self,
action: #selector(onTapped)
))
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError()
}
@objc func onTapped() {
action?()
}
}
}

View File

@@ -0,0 +1,19 @@
//
// IntelligentsFocusApertureView+Delegate.swift
// Intelligents
//
// Created by on 2024/11/21.
//
import Foundation
public enum IntelligentsFocusApertureViewActionType: String {
case translateTo
case summary
case chatWithAI
case dismiss
}
public protocol IntelligentsFocusApertureViewDelegate: AnyObject {
func focusApertureRequestAction(actionType: IntelligentsFocusApertureViewActionType)
}

View File

@@ -9,11 +9,102 @@ import UIKit
extension IntelligentsFocusApertureView {
class ControlButtonsPanel: UIView {
let headerLabel = UILabel()
let headerIcon = UIImageView()
let translateButton = DarkActionButton()
let summaryButton = DarkActionButton()
let chatWithAIButton = DarkActionButton()
init() {
super.init(frame: .zero)
backgroundColor = .red
defer { removeEveryAutoResizingMasks() }
heightAnchor.constraint(equalToConstant: 256).isActive = true
let contentSpacing: CGFloat = 16
let buttonGroupHeight: CGFloat = 55
let headerGroup = UIView()
addSubview(headerGroup)
[
headerGroup.topAnchor.constraint(equalTo: topAnchor),
headerGroup.leadingAnchor.constraint(equalTo: leadingAnchor),
headerGroup.trailingAnchor.constraint(equalTo: trailingAnchor),
].forEach { $0.isActive = true }
headerLabel.text = NSLocalizedString("AFFiNE AI", comment: "") // TODO: FREE TRAIL???
// title 3 with bold
headerLabel.font = .preferredFont(for: .title3, weight: .bold)
headerLabel.textColor = .white
headerLabel.textAlignment = .left
headerIcon.image = .init(named: "spark", in: .module, with: nil)
headerIcon.contentMode = .scaleAspectFit
headerIcon.tintColor = Constant.affineTintColor
headerGroup.addSubview(headerLabel)
headerGroup.addSubview(headerIcon)
[
headerLabel.topAnchor.constraint(equalTo: headerGroup.topAnchor),
headerLabel.leadingAnchor.constraint(equalTo: headerGroup.leadingAnchor),
headerLabel.bottomAnchor.constraint(equalTo: headerGroup.bottomAnchor),
headerIcon.topAnchor.constraint(equalTo: headerGroup.topAnchor),
headerIcon.trailingAnchor.constraint(equalTo: headerGroup.trailingAnchor),
headerIcon.bottomAnchor.constraint(equalTo: headerGroup.bottomAnchor),
headerIcon.widthAnchor.constraint(equalToConstant: 32),
headerIcon.trailingAnchor.constraint(equalTo: headerGroup.trailingAnchor),
headerIcon.leadingAnchor.constraint(equalTo: headerLabel.trailingAnchor, constant: contentSpacing),
].forEach { $0.isActive = true }
let firstButtonSectionGroup = UIView()
addSubview(firstButtonSectionGroup)
[
firstButtonSectionGroup.topAnchor.constraint(equalTo: headerGroup.bottomAnchor, constant: contentSpacing),
firstButtonSectionGroup.leadingAnchor.constraint(equalTo: leadingAnchor),
firstButtonSectionGroup.trailingAnchor.constraint(equalTo: trailingAnchor),
firstButtonSectionGroup.heightAnchor.constraint(equalToConstant: buttonGroupHeight),
].forEach { $0.isActive = true }
translateButton.title = NSLocalizedString("Translate", comment: "")
translateButton.iconSystemName = "textformat"
summaryButton.title = NSLocalizedString("Summary", comment: "")
summaryButton.iconSystemName = "doc.text"
firstButtonSectionGroup.addSubview(translateButton)
firstButtonSectionGroup.addSubview(summaryButton)
[
translateButton.topAnchor.constraint(equalTo: firstButtonSectionGroup.topAnchor),
translateButton.leadingAnchor.constraint(equalTo: firstButtonSectionGroup.leadingAnchor),
translateButton.bottomAnchor.constraint(equalTo: firstButtonSectionGroup.bottomAnchor),
summaryButton.topAnchor.constraint(equalTo: firstButtonSectionGroup.topAnchor),
summaryButton.trailingAnchor.constraint(equalTo: firstButtonSectionGroup.trailingAnchor),
summaryButton.bottomAnchor.constraint(equalTo: firstButtonSectionGroup.bottomAnchor),
translateButton.widthAnchor.constraint(equalTo: summaryButton.widthAnchor),
translateButton.trailingAnchor.constraint(equalTo: summaryButton.leadingAnchor, constant: -contentSpacing),
].forEach { $0.isActive = true }
let secondButtonSectionGroup = UIView()
addSubview(secondButtonSectionGroup)
[
secondButtonSectionGroup.topAnchor.constraint(equalTo: firstButtonSectionGroup.bottomAnchor, constant: contentSpacing),
secondButtonSectionGroup.leadingAnchor.constraint(equalTo: leadingAnchor),
secondButtonSectionGroup.trailingAnchor.constraint(equalTo: trailingAnchor),
secondButtonSectionGroup.heightAnchor.constraint(equalToConstant: buttonGroupHeight),
].forEach { $0.isActive = true }
secondButtonSectionGroup.addSubview(chatWithAIButton)
chatWithAIButton.title = NSLocalizedString("Chat with AI", comment: "")
chatWithAIButton.iconSystemName = "paperplane"
[
chatWithAIButton.topAnchor.constraint(equalTo: secondButtonSectionGroup.topAnchor),
chatWithAIButton.leadingAnchor.constraint(equalTo: secondButtonSectionGroup.leadingAnchor),
chatWithAIButton.bottomAnchor.constraint(equalTo: secondButtonSectionGroup.bottomAnchor),
chatWithAIButton.trailingAnchor.constraint(equalTo: secondButtonSectionGroup.trailingAnchor),
].forEach { $0.isActive = true }
[
secondButtonSectionGroup.bottomAnchor.constraint(equalTo: bottomAnchor),
].forEach { $0.isActive = true }
}
@available(*, unavailable)

View File

@@ -25,34 +25,44 @@ public class IntelligentsFocusApertureView: UIView {
var contentBeginConstraints: [NSLayoutConstraint] = []
var contentFinalConstraints: [NSLayoutConstraint] = []
public weak var delegate: (any IntelligentsFocusApertureViewDelegate)?
public init() {
super.init(frame: .zero)
let tap = UITapGestureRecognizer(
target: self,
action: #selector(dismissFocus)
)
backgroundView.backgroundColor = .black
backgroundView.isUserInteractionEnabled = true
backgroundView.addGestureRecognizer(tap)
backgroundView.addGestureRecognizer(UITapGestureRecognizer(
target: self,
action: #selector(dismissFocus)
))
snapshotView.setContentHuggingPriority(.defaultLow, for: .vertical)
snapshotView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
snapshotView.layer.contentsGravity = .top
snapshotView.layer.masksToBounds = true
snapshotView.contentMode = .scaleAspectFill
snapshotView.isUserInteractionEnabled = true
snapshotView.addGestureRecognizer(tap)
snapshotView.addGestureRecognizer(UITapGestureRecognizer(
target: self,
action: #selector(dismissFocus)
))
addSubview(backgroundView)
addSubview(controlButtonsPanel)
addSubview(snapshotView)
bringSubviewToFront(snapshotView)
var views: [UIView] = [self]
while let view = views.first {
views.removeFirst()
view.translatesAutoresizingMaskIntoConstraints = false
view.subviews.forEach { views.append($0) }
controlButtonsPanel.translateButton.action = { [weak self] in
self?.delegate?.focusApertureRequestAction(actionType: .translateTo)
}
controlButtonsPanel.summaryButton.action = { [weak self] in
self?.delegate?.focusApertureRequestAction(actionType: .summary)
}
controlButtonsPanel.chatWithAIButton.action = { [weak self] in
self?.delegate?.focusApertureRequestAction(actionType: .chatWithAI)
}
removeEveryAutoResizingMasks()
}
@available(*, unavailable)
@@ -112,6 +122,7 @@ public class IntelligentsFocusApertureView: UIView {
isUserInteractionEnabled = false
executeAnimationDismiss {
self.removeFromSuperview()
self.delegate?.focusApertureRequestAction(actionType: .dismiss)
}
}
}

View File

@@ -9,5 +9,14 @@
/* No comment provided by engineer. */
"Chat with AI" = "Chat with AI";
/* No comment provided by engineer. */
"AFFiNE AI" = "AFFiNE AI";
/* No comment provided by engineer. */
"Translate" = "Translate";
/* No comment provided by engineer. */
"Summary" = "Summary";
/* No comment provided by engineer. */
"Summarize this article for me..." = "Summarize this article for me...";

View File

@@ -9,5 +9,14 @@
/* No comment provided by engineer. */
"Chat with AI" = "与 AI 聊天";
/* No comment provided by engineer. */
"AFFiNE AI" = "AFFiNE 人工智能";
/* No comment provided by engineer. */
"Translate" = "翻译";
/* No comment provided by engineer. */
"Summary" = "总结";
/* No comment provided by engineer. */
"Summarize this article for me..." = "请为我总结这份文档...";