mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-24 04:27:27 +08:00
WIP: 嵌入式框架优化调整
This commit is contained in:
@@ -33,7 +33,7 @@ class AFFiNEViewController: CAPBridgeViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AFFiNEViewController: IntelligentsButtonDelegate {
|
extension AFFiNEViewController: IntelligentsButtonDelegate, IntelligentsFocusApertureViewDelegate {
|
||||||
func onIntelligentsButtonTapped(_ button: IntelligentsButton) {
|
func onIntelligentsButtonTapped(_ button: IntelligentsButton) {
|
||||||
guard let webView else {
|
guard let webView else {
|
||||||
assertionFailure() // ? wdym ?
|
assertionFailure() // ? wdym ?
|
||||||
@@ -56,37 +56,55 @@ extension AFFiNEViewController: IntelligentsButtonDelegate {
|
|||||||
print("[?] \(self) script error: \(error.localizedDescription)")
|
print("[?] \(self) script error: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let .success(content) = result,
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
let res = content as? String
|
if case let .success(content) = result,
|
||||||
{
|
let res = content as? String
|
||||||
print("[*] \(self) received document with \(res.count) characters")
|
{
|
||||||
DispatchQueue.main.async {
|
print("[*] \(self) received document with \(res.count) characters")
|
||||||
self.openIntelligentsSheet(withContext: res)
|
DispatchQueue.main.async {
|
||||||
}
|
self.openIntelligentsSheet(withContext: res)
|
||||||
} else {
|
}
|
||||||
DispatchQueue.main.async {
|
} else {
|
||||||
self.openSimpleChat()
|
DispatchQueue.main.async {
|
||||||
|
self.openSimpleChat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openIntelligentsSheet(withContext context: String) {
|
func openIntelligentsSheet(withContext context: String) {
|
||||||
guard let view = webView else {
|
guard let view = webView?.subviews.first else {
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
assert(view is UIScrollView)
|
||||||
_ = context
|
_ = context
|
||||||
let focus = IntelligentsFocusApertureView()
|
let focus = IntelligentsFocusApertureView()
|
||||||
focus.prepareAnimationWith(
|
focus.prepareAnimationWith(
|
||||||
capturingTargetContentView: view,
|
capturingTargetContentView: view,
|
||||||
coveringRootViewController: self
|
coveringRootViewController: self
|
||||||
)
|
)
|
||||||
|
focus.delegate = self
|
||||||
focus.executeAnimationKickIn()
|
focus.executeAnimationKickIn()
|
||||||
|
dismissIntelligentsButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
func openSimpleChat() {
|
func openSimpleChat() {
|
||||||
let targetController = IntelligentsChatController()
|
let targetController = IntelligentsChatController()
|
||||||
presentIntoCurrentContext(withTargetController: targetController)
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -19,6 +19,15 @@ extension UIView {
|
|||||||
return nil
|
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
|
#if DEBUG
|
||||||
func debugFrame() {
|
func debugFrame() {
|
||||||
layer.borderWidth = 1
|
layer.borderWidth = 1
|
||||||
|
|||||||
+76
@@ -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?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -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)
|
||||||
|
}
|
||||||
+93
-2
@@ -9,11 +9,102 @@ import UIKit
|
|||||||
|
|
||||||
extension IntelligentsFocusApertureView {
|
extension IntelligentsFocusApertureView {
|
||||||
class ControlButtonsPanel: UIView {
|
class ControlButtonsPanel: UIView {
|
||||||
|
let headerLabel = UILabel()
|
||||||
|
let headerIcon = UIImageView()
|
||||||
|
|
||||||
|
let translateButton = DarkActionButton()
|
||||||
|
let summaryButton = DarkActionButton()
|
||||||
|
let chatWithAIButton = DarkActionButton()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(frame: .zero)
|
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)
|
@available(*, unavailable)
|
||||||
|
|||||||
+23
-12
@@ -25,34 +25,44 @@ public class IntelligentsFocusApertureView: UIView {
|
|||||||
var contentBeginConstraints: [NSLayoutConstraint] = []
|
var contentBeginConstraints: [NSLayoutConstraint] = []
|
||||||
var contentFinalConstraints: [NSLayoutConstraint] = []
|
var contentFinalConstraints: [NSLayoutConstraint] = []
|
||||||
|
|
||||||
|
public weak var delegate: (any IntelligentsFocusApertureViewDelegate)?
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
super.init(frame: .zero)
|
super.init(frame: .zero)
|
||||||
|
|
||||||
let tap = UITapGestureRecognizer(
|
|
||||||
target: self,
|
|
||||||
action: #selector(dismissFocus)
|
|
||||||
)
|
|
||||||
|
|
||||||
backgroundView.backgroundColor = .black
|
backgroundView.backgroundColor = .black
|
||||||
backgroundView.isUserInteractionEnabled = true
|
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.layer.masksToBounds = true
|
||||||
snapshotView.contentMode = .scaleAspectFill
|
snapshotView.contentMode = .scaleAspectFill
|
||||||
snapshotView.isUserInteractionEnabled = true
|
snapshotView.isUserInteractionEnabled = true
|
||||||
snapshotView.addGestureRecognizer(tap)
|
snapshotView.addGestureRecognizer(UITapGestureRecognizer(
|
||||||
|
target: self,
|
||||||
|
action: #selector(dismissFocus)
|
||||||
|
))
|
||||||
|
|
||||||
addSubview(backgroundView)
|
addSubview(backgroundView)
|
||||||
addSubview(controlButtonsPanel)
|
addSubview(controlButtonsPanel)
|
||||||
addSubview(snapshotView)
|
addSubview(snapshotView)
|
||||||
bringSubviewToFront(snapshotView)
|
bringSubviewToFront(snapshotView)
|
||||||
|
|
||||||
var views: [UIView] = [self]
|
controlButtonsPanel.translateButton.action = { [weak self] in
|
||||||
while let view = views.first {
|
self?.delegate?.focusApertureRequestAction(actionType: .translateTo)
|
||||||
views.removeFirst()
|
|
||||||
view.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.subviews.forEach { views.append($0) }
|
|
||||||
}
|
}
|
||||||
|
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)
|
@available(*, unavailable)
|
||||||
@@ -112,6 +122,7 @@ public class IntelligentsFocusApertureView: UIView {
|
|||||||
isUserInteractionEnabled = false
|
isUserInteractionEnabled = false
|
||||||
executeAnimationDismiss {
|
executeAnimationDismiss {
|
||||||
self.removeFromSuperview()
|
self.removeFromSuperview()
|
||||||
|
self.delegate?.focusApertureRequestAction(actionType: .dismiss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
@@ -9,5 +9,14 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Chat with AI" = "Chat with AI";
|
"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. */
|
/* No comment provided by engineer. */
|
||||||
"Summarize this article for me..." = "Summarize this article for me...";
|
"Summarize this article for me..." = "Summarize this article for me...";
|
||||||
|
|||||||
+9
@@ -9,5 +9,14 @@
|
|||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Chat with AI" = "与 AI 聊天";
|
"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. */
|
/* No comment provided by engineer. */
|
||||||
"Summarize this article for me..." = "请为我总结这份文档...";
|
"Summarize this article for me..." = "请为我总结这份文档...";
|
||||||
|
|||||||
Reference in New Issue
Block a user