修改暗黑模式功能UI

This commit is contained in:
2026-01-09 19:35:36 +08:00
parent 1b6724f043
commit 21fcbe3665
8 changed files with 1113 additions and 760 deletions

View File

@@ -4,15 +4,79 @@
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{ {
"filename" : "kb_del_icon@2x.png", "filename" : "kb_del_icon@2x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "2x" "scale" : "2x"
}, },
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "kb_del_icon@2x 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "切图 256@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{ {
"filename" : "kb_del_icon@3x.png", "filename" : "kb_del_icon@3x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "3x" "scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"filename" : "kb_del_icon@3x 1.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "切图 256@3x.png",
"idiom" : "universal",
"scale" : "3x"
} }
], ],
"info" : { "info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -6,6 +6,7 @@
// //
#import "KBFunctionTagCell.h" #import "KBFunctionTagCell.h"
#import "KBFunctionView.h"
#import "Masonry.h" #import "Masonry.h"
@interface KBFunctionTagCell () @interface KBFunctionTagCell ()
@@ -18,7 +19,7 @@
- (instancetype)initWithFrame:(CGRect)frame { - (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) { if (self = [super initWithFrame:frame]) {
self.contentView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.9]; self.contentView.backgroundColor = [KBFunctionView kb_cellBackgroundColor];
self.contentView.layer.cornerRadius = 12; self.contentView.layer.cornerRadius = 12;
self.contentView.layer.masksToBounds = YES; self.contentView.layer.masksToBounds = YES;
@@ -73,7 +74,6 @@
_emojiLabel.textAlignment = NSTextAlignmentCenter; _emojiLabel.textAlignment = NSTextAlignmentCenter;
_emojiLabel.font = [KBFont medium:20]; _emojiLabel.font = [KBFont medium:20];
_emojiLabel.adjustsFontSizeToFitWidth = YES; _emojiLabel.adjustsFontSizeToFitWidth = YES;
} }
return _emojiLabel; return _emojiLabel;
} }
@@ -82,7 +82,7 @@
if (!_titleLabelInternal) { if (!_titleLabelInternal) {
_titleLabelInternal = [[UILabel alloc] init]; _titleLabelInternal = [[UILabel alloc] init];
_titleLabelInternal.font = [KBFont medium:10]; _titleLabelInternal.font = [KBFont medium:10];
_titleLabelInternal.textColor = [UIColor colorWithHex:0x1B1F1A]; _titleLabelInternal.textColor = [KBFunctionView kb_cellTextColor];
// //
_titleLabelInternal.numberOfLines = 2; _titleLabelInternal.numberOfLines = 2;
_titleLabelInternal.lineBreakMode = NSLineBreakByTruncatingTail; _titleLabelInternal.lineBreakMode = NSLineBreakByTruncatingTail;
@@ -91,14 +91,19 @@
} }
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
static UIActivityIndicatorViewStyle KBSpinnerStyle(void) { return UIActivityIndicatorViewStyleMedium; } static UIActivityIndicatorViewStyle KBSpinnerStyle(void) {
return UIActivityIndicatorViewStyleMedium;
}
#else #else
static UIActivityIndicatorViewStyle KBSpinnerStyle(void) { return UIActivityIndicatorViewStyleGray; } static UIActivityIndicatorViewStyle KBSpinnerStyle(void) {
return UIActivityIndicatorViewStyleGray;
}
#endif #endif
- (UIActivityIndicatorView *)loadingView { - (UIActivityIndicatorView *)loadingView {
if (!_loadingView) { if (!_loadingView) {
_loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:KBSpinnerStyle()]; _loadingView = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:KBSpinnerStyle()];
_loadingView.hidesWhenStopped = YES; _loadingView.hidesWhenStopped = YES;
_loadingView.color = [UIColor grayColor]; _loadingView.color = [UIColor grayColor];
_loadingView.hidden = YES; _loadingView.hidden = YES;
@@ -108,7 +113,9 @@ static UIActivityIndicatorViewStyle KBSpinnerStyle(void) { return UIActivityIndi
#pragma mark - Expose #pragma mark - Expose
- (UILabel *)titleLabel { return self.titleLabelInternal; } - (UILabel *)titleLabel {
return self.titleLabelInternal;
}
- (void)setLoading:(BOOL)loading { - (void)setLoading:(BOOL)loading {
if (loading) { if (loading) {

View File

@@ -10,9 +10,12 @@
@protocol KBFunctionViewDelegate <NSObject> @protocol KBFunctionViewDelegate <NSObject>
@optional @optional
- (void)functionView:(KBFunctionView *_Nullable)functionView didTapToolActionAtIndex:(NSInteger)index; - (void)functionView:(KBFunctionView *_Nullable)functionView
- (void)functionView:(KBFunctionView *_Nullable)functionView didRightTapToolActionAtIndex:(NSInteger)index; didTapToolActionAtIndex:(NSInteger)index;
- (void)functionViewDidRequestSubscription:(KBFunctionView *_Nullable)functionView; - (void)functionView:(KBFunctionView *_Nullable)functionView
didRightTapToolActionAtIndex:(NSInteger)index;
- (void)functionViewDidRequestSubscription:
(KBFunctionView *_Nullable)functionView;
@end @end
@@ -23,9 +26,10 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic, weak) id<KBFunctionViewDelegate> delegate; @property(nonatomic, weak) id<KBFunctionViewDelegate> delegate;
@property(nonatomic, strong, readonly)
@property (nonatomic, strong, readonly) UICollectionView *collectionView; // 话术分类/标签列表 UICollectionView *collectionView; // 话术分类/标签列表
@property (nonatomic, strong, readonly) NSArray<NSString *> *items; // 简单数据源(演示用) @property(nonatomic, strong, readonly)
NSArray<NSString *> *items; // 简单数据源(演示用)
// 子视图暴露,便于外部接入事件 // 子视图暴露,便于外部接入事件
@property(nonatomic, strong, readonly) KBFunctionBarView *barView; @property(nonatomic, strong, readonly) KBFunctionBarView *barView;
@@ -39,6 +43,14 @@ NS_ASSUME_NONNULL_BEGIN
/// 应用当前皮肤(更新背景/强调色) /// 应用当前皮肤(更新背景/强调色)
- (void)kb_applyTheme; - (void)kb_applyTheme;
#pragma mark - Theme Colors (用于 Cell 获取暗黑模式颜色)
/// Cell 背景色:暗黑 #707070浅色 白色90%透明度
+ (UIColor *)kb_cellBackgroundColor;
/// Cell 文字颜色:暗黑 #FFFFFF浅色 #1B1F1A
+ (UIColor *)kb_cellTextColor;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -6,30 +6,32 @@
// //
#import "KBFunctionView.h" #import "KBFunctionView.h"
#import "KBResponderUtils.h" // UIInputViewController #import "KBAuthManager.h" //
#import "KBBackspaceLongPressHandler.h"
#import "KBBackspaceUndoManager.h"
#import "KBBizCode.h"
#import "KBFullAccessGuideView.h"
#import "KBFullAccessManager.h"
#import "KBFunctionBarView.h" #import "KBFunctionBarView.h"
#import "KBFunctionPasteView.h" #import "KBFunctionPasteView.h"
#import "KBFunctionTagCell.h" #import "KBFunctionTagCell.h"
#import "Masonry.h"
#import <MBProgressHUD.h>
#import "KBFullAccessGuideView.h"
#import "KBFullAccessManager.h"
#import "KBSkinManager.h"
#import "KBAuthManager.h" //
#import "KBULBridgeNotification.h" // Darwin UL
#import "KBHostAppLauncher.h"
#import "KBStreamTextView.h" //
#import "KBStreamOverlayView.h" //
#import "KBFunctionTagListView.h" #import "KBFunctionTagListView.h"
#import "WJXEventSource.h" #import "KBHostAppLauncher.h"
#import "KBTagItemModel.h"
#import <MJExtension/MJExtension.h>
#import "KBBizCode.h"
#import "KBBackspaceLongPressHandler.h"
#import "KBBackspaceUndoManager.h"
#import "KBInputBufferManager.h" #import "KBInputBufferManager.h"
#import "KBResponderUtils.h" // UIInputViewController
#import "KBSkinManager.h"
#import "KBStreamOverlayView.h" //
#import "KBStreamTextView.h" //
#import "KBTagItemModel.h"
#import "KBULBridgeNotification.h" // Darwin UL
#import "Masonry.h"
#import "WJXEventSource.h"
#import <MBProgressHUD.h>
#import <MJExtension/MJExtension.h>
@interface KBFunctionView () <KBFunctionBarViewDelegate, KBStreamOverlayViewDelegate, KBFunctionTagListViewDelegate> @interface KBFunctionView () <KBFunctionBarViewDelegate,
KBStreamOverlayViewDelegate,
KBFunctionTagListViewDelegate>
// UI // UI
@property(nonatomic, strong) KBFunctionBarView *barViewInternal; @property(nonatomic, strong) KBFunctionBarView *barViewInternal;
@property(nonatomic, strong) KBFunctionPasteView *pasteViewInternal; @property(nonatomic, strong) KBFunctionPasteView *pasteViewInternal;
@@ -45,8 +47,10 @@
// //
@property(nonatomic, strong, nullable) WJXEventSource *eventSource; @property(nonatomic, strong, nullable) WJXEventSource *eventSource;
@property (nonatomic, assign) BOOL streamHasOutput; // \t @property(nonatomic, assign)
@property (nonatomic, strong, nullable) NSNumber *loadingTagIndex; // loadingindex BOOL streamHasOutput; // \t
@property(nonatomic, strong, nullable)
NSNumber *loadingTagIndex; // loadingindex
@property(nonatomic, copy, nullable) NSString *loadingTagTitle; @property(nonatomic, copy, nullable) NSString *loadingTagTitle;
@property(nonatomic, assign) BOOL eventSourceDidReceiveDone; @property(nonatomic, assign) BOOL eventSourceDidReceiveDone;
@property(nonatomic, copy, nullable) NSString *eventSourceSplitPrefix; @property(nonatomic, copy, nullable) NSString *eventSourceSplitPrefix;
@@ -56,8 +60,10 @@
@property(nonatomic, strong) NSMutableArray<KBTagItemModel *> *modelArray; @property(nonatomic, strong) NSMutableArray<KBTagItemModel *> *modelArray;
// //
@property (nonatomic, strong) NSTimer *pasteboardTimer; // 线 @property(nonatomic, strong)
@property (nonatomic, assign) NSInteger lastHandledPBCount; // changeCount NSTimer *pasteboardTimer; // 线
@property(nonatomic, assign)
NSInteger lastHandledPBCount; // changeCount
// UL // UL
@property(nonatomic, assign) NSUInteger kb_ulSeq; // UL @property(nonatomic, assign) NSUInteger kb_ulSeq; // UL
@@ -71,25 +77,29 @@
if (self = [super initWithFrame:frame]) { if (self = [super initWithFrame:frame]) {
// 使 // 使
[self kb_applyTheme]; [self kb_applyTheme];
self.backspaceHandler = [[KBBackspaceLongPressHandler alloc] initWithContainerView:self]; self.backspaceHandler =
[[KBBackspaceLongPressHandler alloc] initWithContainerView:self];
[self setupUI]; [self setupUI];
// [self reloadDemoData]; // [self reloadDemoData];
[self kb_reloadTagsFromSharedDefaults]; [self kb_reloadTagsFromSharedDefaults];
// //
_lastHandledPBCount = [UIPasteboard generalPasteboard].changeCount; _lastHandledPBCount = [UIPasteboard generalPasteboard].changeCount;
// 访访 TCC/XPC // 访访
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kb_fullAccessChanged) name:KBFullAccessChangedNotification object:nil]; // TCC/XPC
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(kb_fullAccessChanged)
name:KBFullAccessChangedNotification
object:nil];
// App Darwin UL // App Darwin UL
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), CFNotificationCenterAddObserver(
(__bridge const void *)(self), CFNotificationCenterGetDarwinNotifyCenter(),
KBULDarwinCallback, (__bridge const void *)(self), KBULDarwinCallback,
(__bridge CFStringRef)KBDarwinULHandled, (__bridge CFStringRef)KBDarwinULHandled, NULL,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately); CFNotificationSuspensionBehaviorDeliverImmediately);
} }
return self; return self;
@@ -99,11 +109,13 @@
/// App Group NSUserDefaults JSON model + /// App Group NSUserDefaults JSON model +
- (void)kb_reloadTagsFromSharedDefaults { - (void)kb_reloadTagsFromSharedDefaults {
NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:AppGroup]; NSUserDefaults *sharedDefaults =
[[NSUserDefaults alloc] initWithSuiteName:AppGroup];
NSDictionary *jsonDict = [sharedDefaults objectForKey:AppGroup_MyKbJson]; NSDictionary *jsonDict = [sharedDefaults objectForKey:AppGroup_MyKbJson];
if (jsonDict != nil) { if (jsonDict != nil) {
id dataObj = jsonDict[@"data"]; id dataObj = jsonDict[@"data"];
NSArray<KBTagItemModel *> *modelList = [KBTagItemModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj]; NSArray<KBTagItemModel *> *modelList =
[KBTagItemModel mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
if (modelList.count > 0) { if (modelList.count > 0) {
self.modelArray = [NSMutableArray array]; self.modelArray = [NSMutableArray array];
[self.modelArray addObjectsFromArray:modelList]; [self.modelArray addObjectsFromArray:modelList];
@@ -115,21 +127,118 @@
} }
} }
#pragma mark - Theme #pragma mark - Theme
///
- (BOOL)kb_isDarkMode {
if (@available(iOS 13.0, *)) {
return self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
}
return NO;
}
#pragma mark - Theme Colors
/// #323232 #D0D3DA
+ (UIColor *)kb_backgroundColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
UITraitCollection *_Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor colorWithHex:0x2B2B2B];
} else {
return [UIColor colorWithHex:0xD0D3DA];
}
}];
}
return [UIColor colorWithHex:0xD0D3DA];
}
/// Cell #707070 90%
+ (UIColor *)kb_cellBackgroundColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
UITraitCollection *_Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor colorWithHex:0x707070];
} else {
return [UIColor colorWithWhite:1 alpha:0.9];
}
}];
}
return [UIColor colorWithWhite:1 alpha:0.9];
}
/// Cell #FFFFFF #1B1F1A
+ (UIColor *)kb_cellTextColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
UITraitCollection *_Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor whiteColor];
} else {
return [UIColor colorWithHex:0x1B1F1A];
}
}];
}
return [UIColor colorWithHex:0x1B1F1A];
}
/// Clear
+ (UIColor *)kb_clearButtonTextColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
UITraitCollection *_Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor whiteColor];
} else {
return [UIColor blackColor];
}
}];
}
return [UIColor blackColor];
}
/// #707070 #B9BDC8
+ (UIColor *)kb_deleteButtonBackgroundColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull(
UITraitCollection *_Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor colorWithHex:0x707070];
} else {
return [UIColor colorWithHex:0xB9BDC8];
}
}];
}
return [UIColor colorWithHex:0xB9BDC8];
}
- (void)kb_applyTheme { - (void)kb_applyTheme {
// KBSkinManager *mgr = [KBSkinManager shared]; // 使
// UIColor *accent = mgr.current.accentColor ?: [UIColor colorWithRed:0.77 green:0.93 blue:0.82 alpha:1.0]; self.backgroundColor = [KBFunctionView kb_backgroundColor];
// BOOL hasImg = ([mgr currentBackgroundImage] != nil);
self.backgroundColor = [UIColor colorWithHex:0xD0D3DA]; //
self.clearButtonInternal.backgroundColor =
[KBFunctionView kb_deleteButtonBackgroundColor];
[self.clearButtonInternal
setTitleColor:[KBFunctionView kb_clearButtonTextColor]
forState:UIControlStateNormal];
self.deleteButtonInternal.backgroundColor =
[KBFunctionView kb_deleteButtonBackgroundColor];
// TagListView cell
[self.tagListView.collectionView reloadData];
} }
- (void)dealloc { - (void)dealloc {
[self stopPasteboardMonitor]; [self stopPasteboardMonitor];
[self kb_stopNetworkStreaming]; [self kb_stopNetworkStreaming];
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), (__bridge CFStringRef)KBDarwinULHandled, NULL); CFNotificationCenterRemoveObserver(
CFNotificationCenterGetDarwinNotifyCenter(),
(__bridge const void *)(self), (__bridge CFStringRef)KBDarwinULHandled,
NULL);
} }
#pragma mark - UI #pragma mark - UI
@@ -196,7 +305,9 @@
make.height.mas_equalTo(smallH); make.height.mas_equalTo(smallH);
}]; }];
// Paste // Paste
[self.pasteViewInternal.pasBtn addTarget:self action:@selector(onTapPaste) forControlEvents:UIControlEventTouchUpInside]; [self.pasteViewInternal.pasBtn addTarget:self
action:@selector(onTapPaste)
forControlEvents:UIControlEventTouchUpInside];
// 3. Tag List View // 3. Tag List View
[self addSubview:self.tagListView]; [self addSubview:self.tagListView];
@@ -229,7 +340,9 @@
- (void)kb_showStreamTextViewIfNeededWithTitle:(NSString *)title { - (void)kb_showStreamTextViewIfNeededWithTitle:(NSString *)title {
// //
if (self.streamOverlay.superview) { return; } if (self.streamOverlay.superview) {
return;
}
// 使 // 使
self.tagListView.hidden = YES; self.tagListView.hidden = YES;
@@ -277,16 +390,20 @@
[self kb_onTapStreamDelete]; [self kb_onTapStreamDelete];
} }
#pragma mark - Network Streaming (WJXEventSource) #pragma mark - Network Streaming (WJXEventSource)
- (void)kb_startNetworkStreamingWithSeed:(NSString *)seedTitle { - (void)kb_startNetworkStreamingWithSeed:(NSString *)seedTitle {
[self kb_stopNetworkStreaming]; [self kb_stopNetworkStreaming];
if (![[KBFullAccessManager shared] hasFullAccess]) { return; } if (![[KBFullAccessManager shared] hasFullAccess]) {
return;
}
NSString *apiUrl = [NSString stringWithFormat:@"%@%@", KB_BASE_URL, API_AI_TALK]; NSString *apiUrl =
[NSString stringWithFormat:@"%@%@", KB_BASE_URL, API_AI_TALK];
NSURL *url = [NSURL URLWithString:apiUrl]; NSURL *url = [NSURL URLWithString:apiUrl];
if (!url) { return; } if (!url) {
return;
}
NSInteger characterId = 0; NSInteger characterId = 0;
if (self.loadingTagIndex != nil) { if (self.loadingTagIndex != nil) {
@@ -297,21 +414,26 @@
} }
} }
NSInteger resolvedCharacterId = (characterId > 0) ? characterId : 75; NSInteger resolvedCharacterId = (characterId > 0) ? characterId : 75;
NSString *message = seedTitle.length > 0 ? seedTitle : @"aliqua non cupidatat"; NSString *message =
// message = [NSString stringWithFormat:@"%@%d",message,arc4random() % 10000]; seedTitle.length > 0 ? seedTitle : @"aliqua non cupidatat";
NSDictionary *payload = @{ // message = [NSString stringWithFormat:@"%@%d",message,arc4random() %
@"characterId": @(resolvedCharacterId), // 10000];
@"message": message NSDictionary *payload =
}; @{@"characterId" : @(resolvedCharacterId), @"message" : message};
NSLog(@"[KBFunction] request payload: %@", payload); NSLog(@"[KBFunction] request payload: %@", payload);
NSError *bodyError = nil; NSError *bodyError = nil;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&bodyError]; NSData *bodyData = [NSJSONSerialization dataWithJSONObject:payload
options:0
error:&bodyError];
if (bodyError || bodyData.length == 0) { if (bodyError || bodyData.length == 0) {
NSLog(@"[KBFunction] build body failed: %@", bodyError); NSLog(@"[KBFunction] build body failed: %@", bodyError);
return; return;
} }
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60]; NSMutableURLRequest *request =
[NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:60];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSString *token = KBAuthManager.shared.current.accessToken ?: @""; NSString *token = KBAuthManager.shared.current.accessToken ?: @"";
@@ -327,14 +449,24 @@
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
WJXEventSource *source = [[WJXEventSource alloc] initWithRquest:request]; WJXEventSource *source = [[WJXEventSource alloc] initWithRquest:request];
source.ignoreRetryAction = YES; source.ignoreRetryAction = YES;
[source addListener:^(WJXEvent * _Nonnull event) { [source
__strong typeof(weakSelf) self = weakSelf; if (!self) return; addListener:^(WJXEvent *_Nonnull event) {
__strong typeof(weakSelf) self = weakSelf;
if (!self)
return;
[self kb_handleEventSourceMessage:event]; [self kb_handleEventSourceMessage:event];
} forEvent:WJXEventNameMessage queue:NSOperationQueue.mainQueue]; }
[source addListener:^(WJXEvent * _Nonnull event) { forEvent:WJXEventNameMessage
__strong typeof(weakSelf) self = weakSelf; if (!self) return; queue:NSOperationQueue.mainQueue];
[source
addListener:^(WJXEvent *_Nonnull event) {
__strong typeof(weakSelf) self = weakSelf;
if (!self)
return;
[self kb_handleEventSourceError:event.error]; [self kb_handleEventSourceError:event.error];
} forEvent:WJXEventNameError queue:NSOperationQueue.mainQueue]; }
forEvent:WJXEventNameError
queue:NSOperationQueue.mainQueue];
self.eventSource = source; self.eventSource = source;
[self.eventSource open]; [self.eventSource open];
} }
@@ -348,16 +480,28 @@
} }
- (void)kb_handleEventSourceMessage:(WJXEvent *)event { - (void)kb_handleEventSourceMessage:(WJXEvent *)event {
if (event.data.length == 0) { return; } if (event.data.length == 0) {
return;
}
NSLog(@"[KBStream] SSE raw payload: %@", event.data); NSLog(@"[KBStream] SSE raw payload: %@", event.data);
NSData *jsonData = [event.data dataUsingEncoding:NSUTF8StringEncoding]; NSData *jsonData = [event.data dataUsingEncoding:NSUTF8StringEncoding];
if (!jsonData) { return; } if (!jsonData) {
return;
}
NSError *error = nil; NSError *error = nil;
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:jsonData
if (error || ![payload isKindOfClass:[NSDictionary class]]) { return; } options:0
if ([self kb_handleBizErrorIfNeeded:payload]) { return; } error:&error];
if (error || ![payload isKindOfClass:[NSDictionary class]]) {
return;
}
if ([self kb_handleBizErrorIfNeeded:payload]) {
return;
}
NSString *type = payload[@"type"]; NSString *type = payload[@"type"];
if (![type isKindOfClass:[NSString class]]) { return; } if (![type isKindOfClass:[NSString class]]) {
return;
}
if ([type isEqualToString:@"llm_chunk"]) { if ([type isEqualToString:@"llm_chunk"]) {
NSString *chunk = [self kb_normalizedLLMChunkString:payload[@"data"]]; NSString *chunk = [self kb_normalizedLLMChunkString:payload[@"data"]];
@@ -381,7 +525,9 @@
} }
- (void)kb_handleEventSourceError:(NSError *_Nullable)error { - (void)kb_handleEventSourceError:(NSError *_Nullable)error {
if (self.eventSourceDidReceiveDone) { return; } if (self.eventSourceDidReceiveDone) {
return;
}
[self kb_finishEventSourceWithError:error]; [self kb_finishEventSourceWithError:error];
} }
@@ -414,7 +560,8 @@
if (msg.length == 0) { if (msg.length == 0) {
msg = KBLocalized(@"拉取失败"); msg = KBLocalized(@"拉取失败");
} }
NSError *bizError = [NSError errorWithDomain:@"KBStreamBizError" NSError *bizError =
[NSError errorWithDomain:@"KBStreamBizError"
code:code code:code
userInfo:@{NSLocalizedDescriptionKey : msg}]; userInfo:@{NSLocalizedDescriptionKey : msg}];
[self kb_finishEventSourceWithError:bizError]; [self kb_finishEventSourceWithError:bizError];
@@ -425,7 +572,8 @@
} }
- (void)kb_requestSubscriptionGuide { - (void)kb_requestSubscriptionGuide {
if ([self.delegate respondsToSelector:@selector(functionViewDidRequestSubscription:)]) { if ([self.delegate
respondsToSelector:@selector(functionViewDidRequestSubscription:)]) {
[self.delegate functionViewDidRequestSubscription:self]; [self.delegate functionViewDidRequestSubscription:self];
} }
} }
@@ -433,7 +581,9 @@
#pragma mark - Event Parsing #pragma mark - Event Parsing
- (NSString *)kb_normalizedLLMChunkString:(id)dataValue { - (NSString *)kb_normalizedLLMChunkString:(id)dataValue {
if (![dataValue isKindOfClass:[NSString class]]) { return @""; } if (![dataValue isKindOfClass:[NSString class]]) {
return @"";
}
NSString *text = (NSString *)dataValue; NSString *text = (NSString *)dataValue;
// 1. <SPLIT> "<SP" + "LIT>" // 1. <SPLIT> "<SP" + "LIT>"
@@ -441,7 +591,9 @@
text = [self.eventSourceSplitPrefix stringByAppendingString:text ?: @""]; text = [self.eventSourceSplitPrefix stringByAppendingString:text ?: @""];
self.eventSourceSplitPrefix = nil; self.eventSourceSplitPrefix = nil;
} }
if (text.length == 0) { return @""; } if (text.length == 0) {
return @"";
}
// 2. // 2.
while (text.length > 0) { while (text.length > 0) {
@@ -452,7 +604,9 @@
} }
break; break;
} }
if (text.length == 0) { return @""; } if (text.length == 0) {
return @"";
}
// 3. "<SPLIT" // 3. "<SPLIT"
NSString *suffix = [self kb_pendingSplitSuffixForString:text]; NSString *suffix = [self kb_pendingSplitSuffixForString:text];
@@ -460,24 +614,29 @@
self.eventSourceSplitPrefix = suffix; self.eventSourceSplitPrefix = suffix;
text = [text substringToIndex:text.length - suffix.length]; text = [text substringToIndex:text.length - suffix.length];
} }
if (text.length == 0) { return @""; } if (text.length == 0) {
return @"";
}
// 4. <SPLIT> \t // 4. <SPLIT> \t
text = [text stringByReplacingOccurrencesOfString:@"<SPLIT>" withString:@"\t"]; text = [text stringByReplacingOccurrencesOfString:@"<SPLIT>"
withString:@"\t"];
// /t UI // /t UI
return text; return text;
} }
- (NSString *)kb_formattedSearchResultString:(id)dataValue { - (NSString *)kb_formattedSearchResultString:(id)dataValue {
// data // data
if (![dataValue isKindOfClass:[NSArray class]]) { return @""; } if (![dataValue isKindOfClass:[NSArray class]]) {
return @"";
}
NSArray *list = (NSArray *)dataValue; NSArray *list = (NSArray *)dataValue;
NSMutableArray<NSString *> *segments = [NSMutableArray array]; NSMutableArray<NSString *> *segments = [NSMutableArray array];
[list enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [list enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx,
BOOL *_Nonnull stop) {
NSString *payload = nil; NSString *payload = nil;
if ([obj isKindOfClass:[NSDictionary class]]) { if ([obj isKindOfClass:[NSDictionary class]]) {
@@ -490,19 +649,24 @@
payload = (NSString *)obj; payload = (NSString *)obj;
} }
payload = [payload stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; payload = [payload
stringByTrimmingCharactersInSet:[NSCharacterSet
whitespaceAndNewlineCharacterSet]];
if (payload.length > 0) { if (payload.length > 0) {
// payload // payload
[segments addObject:payload]; [segments addObject:payload];
} }
}]; }];
if (segments.count == 0) { return @""; } if (segments.count == 0) {
return @"";
}
// \t KBStreamTextView \t label // \t KBStreamTextView \t label
NSMutableString *result = [NSMutableString string]; NSMutableString *result = [NSMutableString string];
[segments enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { [segments enumerateObjectsUsingBlock:^(NSString *_Nonnull obj, NSUInteger idx,
BOOL *_Nonnull stop) {
// \t // \t
[result appendFormat:@"\t%@", obj]; [result appendFormat:@"\t%@", obj];
}]; }];
@@ -510,12 +674,15 @@
return result; return result;
} }
- (NSString *)kb_pendingSplitSuffixForString:(NSString *)text { - (NSString *)kb_pendingSplitSuffixForString:(NSString *)text {
static NSString *const token = @"<SPLIT>"; static NSString *const token = @"<SPLIT>";
if (text.length == 0) { return @""; } if (text.length == 0) {
return @"";
}
NSUInteger tokenLen = token.length; NSUInteger tokenLen = token.length;
if (tokenLen <= 1) { return @""; } if (tokenLen <= 1) {
return @"";
}
NSUInteger maxLen = MIN(tokenLen - 1, text.length); NSUInteger maxLen = MIN(tokenLen - 1, text.length);
for (NSUInteger len = maxLen; len > 0; len--) { for (NSUInteger len = maxLen; len > 0; len--) {
NSString *suffix = [text substringFromIndex:text.length - len]; NSString *suffix = [text substringFromIndex:text.length - len];
@@ -533,16 +700,20 @@
/// - `<SPLIT>` `\t` /// - `<SPLIT>` `\t`
/// - UI /// - UI
- (void)kb_appendChunkToStreamView:(NSString *)chunk { - (void)kb_appendChunkToStreamView:(NSString *)chunk {
if (chunk.length == 0) return; if (chunk.length == 0)
return;
// overlay cell // overlay cell
if (!self.streamOverlay) { if (!self.streamOverlay) {
[self kb_showStreamTextViewIfNeededWithTitle:self.loadingTagTitle ?: @""]; [self kb_showStreamTextViewIfNeededWithTitle:self.loadingTagTitle ?: @""];
if (self.loadingTagIndex) { if (self.loadingTagIndex) {
[self.tagListView setLoading:NO atIndex:self.loadingTagIndex.integerValue]; [self.tagListView setLoading:NO
self.loadingTagIndex = nil; self.loadingTagTitle = nil; atIndex:self.loadingTagIndex.integerValue];
self.loadingTagIndex = nil;
self.loadingTagTitle = nil;
} }
} }
if (!self.streamOverlay) return; if (!self.streamOverlay)
return;
[self.streamOverlay appendChunk:chunk]; [self.streamOverlay appendChunk:chunk];
self.streamHasOutput = YES; self.streamHasOutput = YES;
} }
@@ -554,20 +725,24 @@
if (text.length > 0) { if (text.length > 0) {
NSString *displayText = text; NSString *displayText = text;
if (displayText.length > 30) { if (displayText.length > 30) {
displayText = [[displayText substringToIndex:30] stringByAppendingString:@"…"]; displayText =
[[displayText substringToIndex:30] stringByAppendingString:@"…"];
} }
[self.pasteView.pasBtn setImage:nil forState:UIControlStateNormal]; [self.pasteView.pasBtn setImage:nil forState:UIControlStateNormal];
[self.pasteView.pasBtn setTitle:displayText forState:UIControlStateNormal]; [self.pasteView.pasBtn setTitle:displayText forState:UIControlStateNormal];
} else { } else {
UIImage *img = [UIImage imageNamed:@"kb_zt_icon"]; UIImage *img = [UIImage imageNamed:@"kb_zt_icon"];
[self.pasteView.pasBtn setImage:img forState:UIControlStateNormal]; [self.pasteView.pasBtn setImage:img forState:UIControlStateNormal];
[self.pasteView.pasBtn setTitle:KBLocalized(@" Paste Ta's Words") forState:UIControlStateNormal]; [self.pasteView.pasBtn setTitle:KBLocalized(@" Paste Ta's Words")
forState:UIControlStateNormal];
} }
} }
#pragma mark - KBFunctionTagListViewDelegate #pragma mark - KBFunctionTagListViewDelegate
- (void)tagListView:(KBFunctionTagListView *)view didSelectIndex:(NSInteger)index title:(NSString *)title { - (void)tagListView:(KBFunctionTagListView *)view
didSelectIndex:(NSInteger)index
title:(NSString *)title {
// 1) 访 // 1) 访
if (![[KBFullAccessManager shared] hasFullAccess]) { if (![[KBFullAccessManager shared] hasFullAccess]) {
// 访 // 访
@@ -576,38 +751,46 @@
return; return;
} }
// 2) -> App App // 2) -> App App
//
if (!KBAuthManager.shared.isLoggedIn) { if (!KBAuthManager.shared.isLoggedIn) {
UIInputViewController *ivc = KBFindInputViewController(self); UIInputViewController *ivc = KBFindInputViewController(self);
NSString *schemeStr = [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; NSString *schemeStr =
[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME];
NSURL *scheme = [NSURL URLWithString:schemeStr]; NSURL *scheme = [NSURL URLWithString:schemeStr];
// UIApplication App // UIApplication App
BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:ivc.view]; BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:ivc.view];
return; return;
// if (!ivc) return; // if (!ivc) return;
// NSString *encodedTitle = [title stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]] ?: @""; // NSString *encodedTitle = [title
// NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=functionView&index=%ld&title=%@", KB_UL_LOGIN, (long)index, encodedTitle]]; // stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet
// if (!ul) return; // URLQueryAllowedCharacterSet]] ?: @""; NSURL *ul = [NSURL
// URLWithString:[NSString
// stringWithFormat:@"%@?src=functionView&index=%ld&title=%@",
// KB_UL_LOGIN, (long)index, encodedTitle]]; if (!ul) return;
// // UL ok // // UL ok
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 *
// [ivc.extensionContext openURL:ul completionHandler:^(__unused BOOL ok) {}]; // NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// [ivc.extensionContext openURL:ul completionHandler:^(__unused
// BOOL ok) {}];
// }); // });
// // 500ms App 退 Scheme宿 UIApplication // // 500ms App 退
// self.kb_ulHandledFlag = NO; // Scheme宿 UIApplication self.kb_ulHandledFlag = NO;
// NSUInteger token = ++self.kb_ulSeq; // NSUInteger token = ++self.kb_ulSeq;
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 *
// NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// if (token != self.kb_ulSeq) return; // // if (token != self.kb_ulSeq) return; //
// if (self.kb_ulHandledFlag) return; // App // if (self.kb_ulHandledFlag) return; // App
// NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=functionView&index=%ld&title=%@", KB_APP_SCHEME, (long)index, encodedTitle]]; // NSURL *scheme = [NSURL URLWithString:[NSString
// if (!scheme) return; // stringWithFormat:@"%@://login?src=functionView&index=%ld&title=%@",
// UIResponder *start = ivc.view ?: (UIResponder *)self; // KB_APP_SCHEME, (long)index, encodedTitle]]; if (!scheme)
// return; UIResponder *start = ivc.view ?: (UIResponder *)self;
// // // //
// [ivc dismissKeyboard]; // [ivc dismissKeyboard];
// BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:start]; // BOOL ok = [KBHostAppLauncher openHostAppURL:scheme
// if (!ok) { // fromResponder:start]; if (!ok) {
// [KBHUD showInfo:KBLocalized(@"请切换到主App完成登录")]; // [KBHUD showInfo:KBLocalized(@"请切换到主App完成登录")];
// }else{ // }else{
// //
@@ -615,7 +798,8 @@
// }); // });
// return; // return;
} }
BOOL hasPasteText = ![self.pasteView.pasBtn.currentTitle isEqualToString:KBLocalized(@" Paste Ta's Words")]; BOOL hasPasteText = ![self.pasteView.pasBtn.currentTitle
isEqualToString:KBLocalized(@" Paste Ta's Words")];
// BOOL hasPasteText = (self.pasteView.pasBtn.imageView.image == nil); // BOOL hasPasteText = (self.pasteView.pasBtn.imageView.image == nil);
if (!hasPasteText) { if (!hasPasteText) {
[KBHUD showInfo:KBLocalized(@"Please copy the text first")]; [KBHUD showInfo:KBLocalized(@"Please copy the text first")];
@@ -631,15 +815,22 @@
} }
// Darwin App UL // Darwin App UL
static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer,
CFStringRef name, const void *object,
CFDictionaryRef userInfo) {
KBFunctionView *self_ = (__bridge KBFunctionView *)observer; KBFunctionView *self_ = (__bridge KBFunctionView *)observer;
if (!self_) return; if (!self_)
dispatch_async(dispatch_get_main_queue(), ^{ self_.kb_ulHandledFlag = YES; }); return;
dispatch_async(dispatch_get_main_queue(), ^{
self_.kb_ulHandledFlag = YES;
});
} }
// UL App Scheme访 // UL App
// 访 KBStreamTextView // Scheme访 访
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { // KBStreamTextView
- (void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// KBFunctionTagListView id/name // KBFunctionTagListView id/name
// + 访访 // + 访访
if ([[KBFullAccessManager shared] hasFullAccess]) { if ([[KBFullAccessManager shared] hasFullAccess]) {
@@ -651,39 +842,61 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
[KBHUD showInfo:KBLocalized(@"处理中…")]; [KBHUD showInfo:KBLocalized(@"处理中…")];
UIInputViewController *ivc = KBFindInputViewController(self); UIInputViewController *ivc = KBFindInputViewController(self);
if (!ivc) return; if (!ivc)
return;
NSString *title = self.modelArray[indexPath.item].characterName; NSString *title = self.modelArray[indexPath.item].characterName;
NSString *encodedTitle = [title stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]] ?: @""; NSString *encodedTitle =
[title stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet URLQueryAllowedCharacterSet]]
?: @"";
NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=functionView&index=%ld&title=%@", KB_UL_LOGIN, (long)indexPath.item, encodedTitle]]; NSURL *ul = [NSURL
if (!ul) return; URLWithString:
[NSString stringWithFormat:@"%@?src=functionView&index=%ld&title=%@",
KB_UL_LOGIN, (long)indexPath.item,
encodedTitle]];
if (!ul)
return;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
// extensionContext UL // extensionContext UL
[ivc.extensionContext openURL:ul completionHandler:^(BOOL ok) { [ivc.extensionContext
openURL:ul
completionHandler:^(BOOL ok) {
if (ok) { if (ok) {
return; return;
} }
// UL 宿 UIApplication + Scheme // UL 宿 UIApplication + Scheme
NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//login?src=functionView&index=%ld&title=%@", KB_APP_SCHEME, (long)indexPath.item, encodedTitle]]; NSURL *scheme = [NSURL
URLWithString:
[NSString
stringWithFormat:
@"%@@//login?src=functionView&index=%ld&title=%@",
KB_APP_SCHEME, (long)indexPath.item,
encodedTitle]];
UIResponder *start = ivc.view ?: (UIResponder *)self; UIResponder *start = ivc.view ?: (UIResponder *)self;
BOOL ok2 = [KBHostAppLauncher openHostAppURL:scheme fromResponder:start]; BOOL ok2 = [KBHostAppLauncher openHostAppURL:scheme
fromResponder:start];
if (!ok2) { if (!ok2) {
// 访宿 Manager // 访宿 Manager
//
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[[KBFullAccessManager shared] ensureFullAccessOrGuideInView:self]; [[KBFullAccessManager shared]
ensureFullAccessOrGuideInView:self];
}); });
} }
}]; }];
}); });
} }
#pragma mark - Button Actions #pragma mark - Button Actions
- (void)onTapPaste { - (void)onTapPaste {
[[KBMaiPointReporter sharedReporter] reportClickWithEventName:@"click_keyboard_function_paste_btn" [[KBMaiPointReporter sharedReporter]
reportClickWithEventName:@"click_keyboard_function_paste_btn"
pageId:@"keyboard_function_panel" pageId:@"keyboard_function_panel"
elementId:@"paste_btn" elementId:@"paste_btn"
extra:nil extra:nil
@@ -691,8 +904,9 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
// //
// - iOS16+ App // - iOS16+ App
// - iOS15 // - iOS15
// viewDidLoad //
// + 访访 // viewDidLoad +
// 访访
if (![[KBFullAccessManager shared] hasFullAccess]) { if (![[KBFullAccessManager shared] hasFullAccess]) {
// 访 // 访
[[KBFullAccessManager shared] ensureFullAccessOrGuideInView:self]; [[KBFullAccessManager shared] ensureFullAccessOrGuideInView:self];
@@ -731,20 +945,31 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
// //
return; return;
// 访宿/ // 访宿/
if (![[KBFullAccessManager shared] hasFullAccess]) return; if (![[KBFullAccessManager shared] hasFullAccess])
if (self.pasteboardTimer) return; return;
KBWeakSelf if (self.pasteboardTimer)
self.pasteboardTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:YES block:^(NSTimer * _Nonnull timer) { return;
__strong typeof(weakSelf) self = weakSelf; if (!self) return; KBWeakSelf self.pasteboardTimer = [NSTimer
UIPasteboard *pb = [UIPasteboard generalPasteboard]; scheduledTimerWithTimeInterval:0.5
repeats:YES
block:^(NSTimer *_Nonnull timer) {
__strong typeof(weakSelf) self = weakSelf;
if (!self)
return;
UIPasteboard *pb =
[UIPasteboard generalPasteboard];
NSInteger cc = pb.changeCount; NSInteger cc = pb.changeCount;
if (cc <= self.lastHandledPBCount) return; // if (cc <= self.lastHandledPBCount)
self.lastHandledPBCount = cc; // return; //
self.lastHandledPBCount =
cc; //
// iOS16+ // iOS16+
NSString *text = pb.string; NSString *text = pb.string;
// -> / -> + // -> / ->
[self kb_updatePasteButtonWithDisplayText:text]; // +
[self
kb_updatePasteButtonWithDisplayText:text];
}]; }];
} }
@@ -777,12 +1002,15 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
} }
- (void)kb_fullAccessChanged { - (void)kb_fullAccessChanged {
dispatch_async(dispatch_get_main_queue(), ^{ [self kb_refreshPasteboardMonitor]; }); dispatch_async(dispatch_get_main_queue(), ^{
[self kb_refreshPasteboardMonitor];
});
} }
- (void)onTapDelete { - (void)onTapDelete {
NSLog(@"点击:删除"); NSLog(@"点击:删除");
[[KBMaiPointReporter sharedReporter] reportClickWithEventName:@"click_keyboard_function_delete_btn" [[KBMaiPointReporter sharedReporter]
reportClickWithEventName:@"click_keyboard_function_delete_btn"
pageId:@"keyboard_function_panel" pageId:@"keyboard_function_panel"
elementId:@"delete_btn" elementId:@"delete_btn"
extra:nil extra:nil
@@ -790,14 +1018,18 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
UIInputViewController *ivc = KBFindInputViewController(self); UIInputViewController *ivc = KBFindInputViewController(self);
id<UITextDocumentProxy> proxy = ivc.textDocumentProxy; id<UITextDocumentProxy> proxy = ivc.textDocumentProxy;
[[KBInputBufferManager shared] refreshFromProxyIfPossible:proxy]; [[KBInputBufferManager shared] refreshFromProxyIfPossible:proxy];
[[KBInputBufferManager shared] prepareSnapshotForDeleteWithContextBefore:proxy.documentContextBeforeInput [[KBInputBufferManager shared]
after:proxy.documentContextAfterInput]; prepareSnapshotForDeleteWithContextBefore:proxy.documentContextBeforeInput
[[KBBackspaceUndoManager shared] captureAndDeleteBackwardFromProxy:proxy count:1]; after:proxy
.documentContextAfterInput];
[[KBBackspaceUndoManager shared] captureAndDeleteBackwardFromProxy:proxy
count:1];
[[KBInputBufferManager shared] applyHoldDeleteCount:1]; [[KBInputBufferManager shared] applyHoldDeleteCount:1];
} }
- (void)onTapClear { - (void)onTapClear {
NSLog(@"点击:清空"); NSLog(@"点击:清空");
[[KBMaiPointReporter sharedReporter] reportClickWithEventName:@"click_keyboard_function_clear_btn" [[KBMaiPointReporter sharedReporter]
reportClickWithEventName:@"click_keyboard_function_clear_btn"
pageId:@"keyboard_function_panel" pageId:@"keyboard_function_panel"
elementId:@"clear_btn" elementId:@"clear_btn"
extra:nil extra:nil
@@ -807,7 +1039,8 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
- (void)onTapSend { - (void)onTapSend {
NSLog(@"点击:发送"); NSLog(@"点击:发送");
[[KBMaiPointReporter sharedReporter] reportClickWithEventName:@"click_keyboard_function_send_btn" [[KBMaiPointReporter sharedReporter]
reportClickWithEventName:@"click_keyboard_function_send_btn"
pageId:@"keyboard_function_panel" pageId:@"keyboard_function_panel"
elementId:@"send_btn" elementId:@"send_btn"
extra:nil extra:nil
@@ -832,16 +1065,20 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
#pragma mark - KBFunctionBarViewDelegate #pragma mark - KBFunctionBarViewDelegate
- (void)functionBarView:(KBFunctionBarView *)bar didTapLeftAtIndex:(NSInteger)index { - (void)functionBarView:(KBFunctionBarView *)bar
didTapLeftAtIndex:(NSInteger)index {
// //
if ([self.delegate respondsToSelector:@selector(functionView:didTapToolActionAtIndex:)]) { if ([self.delegate respondsToSelector:@selector(functionView:
didTapToolActionAtIndex:)]) {
[self.delegate functionView:self didTapToolActionAtIndex:index]; [self.delegate functionView:self didTapToolActionAtIndex:index];
} }
} }
- (void)functionBarView:(KBFunctionBarView *)bar didTapRightAtIndex:(NSInteger)index { - (void)functionBarView:(KBFunctionBarView *)bar
didTapRightAtIndex:(NSInteger)index {
// / // /
if ([self.delegate respondsToSelector:@selector(functionView:didRightTapToolActionAtIndex:)]) { if ([self.delegate respondsToSelector:@selector(functionView:
didRightTapToolActionAtIndex:)]) {
[self.delegate functionView:self didRightTapToolActionAtIndex:index]; [self.delegate functionView:self didRightTapToolActionAtIndex:index];
} }
} }
@@ -869,7 +1106,8 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
return _rightButtonContainer; return _rightButtonContainer;
} }
- (UIButton *)buildRightButtonWithTitle:(NSString *)title color:(UIColor *)color { - (UIButton *)buildRightButtonWithTitle:(NSString *)title
color:(UIColor *)color {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.backgroundColor = color; btn.backgroundColor = color;
btn.layer.cornerRadius = 8.0; btn.layer.cornerRadius = 8.0;
@@ -882,8 +1120,15 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
- (UIButton *)pasteButtonInternal { - (UIButton *)pasteButtonInternal {
if (!_pasteButtonInternal) { if (!_pasteButtonInternal) {
_pasteButtonInternal = [self buildRightButtonWithTitle:KBLocalized(@"Paste") color:[UIColor colorWithRed:0.13 green:0.73 blue:0.60 alpha:1.0]]; _pasteButtonInternal =
[_pasteButtonInternal addTarget:self action:@selector(onTapPaste) forControlEvents:UIControlEventTouchUpInside]; [self buildRightButtonWithTitle:KBLocalized(@"Paste")
color:[UIColor colorWithRed:0.13
green:0.73
blue:0.60
alpha:1.0]];
[_pasteButtonInternal addTarget:self
action:@selector(onTapPaste)
forControlEvents:UIControlEventTouchUpInside];
} }
return _pasteButtonInternal; return _pasteButtonInternal;
} }
@@ -894,10 +1139,14 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
_deleteButtonInternal.backgroundColor = [UIColor colorWithHex:0xB9BDC8]; _deleteButtonInternal.backgroundColor = [UIColor colorWithHex:0xB9BDC8];
_deleteButtonInternal.layer.cornerRadius = 8.0; _deleteButtonInternal.layer.cornerRadius = 8.0;
_deleteButtonInternal.layer.masksToBounds = YES; _deleteButtonInternal.layer.masksToBounds = YES;
[_deleteButtonInternal setImage:[UIImage imageNamed:@"kb_del_icon"] forState:UIControlStateNormal]; [_deleteButtonInternal setImage:[UIImage imageNamed:@"kb_del_icon"]
forState:UIControlStateNormal];
[_deleteButtonInternal addTarget:self action:@selector(onTapDelete) forControlEvents:UIControlEventTouchUpInside]; [_deleteButtonInternal addTarget:self
[self.backspaceHandler bindDeleteButton:_deleteButtonInternal showClearLabel:NO]; action:@selector(onTapDelete)
forControlEvents:UIControlEventTouchUpInside];
[self.backspaceHandler bindDeleteButton:_deleteButtonInternal
showClearLabel:NO];
} }
return _deleteButtonInternal; return _deleteButtonInternal;
} }
@@ -909,32 +1158,53 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, C
_clearButtonInternal.layer.cornerRadius = 8.0; _clearButtonInternal.layer.cornerRadius = 8.0;
_clearButtonInternal.layer.masksToBounds = YES; _clearButtonInternal.layer.masksToBounds = YES;
_clearButtonInternal.titleLabel.font = [KBFont medium:13]; _clearButtonInternal.titleLabel.font = [KBFont medium:13];
[_clearButtonInternal setTitle:KBLocalized(@"Clear") forState:UIControlStateNormal]; [_clearButtonInternal setTitle:KBLocalized(@"Clear")
[_clearButtonInternal setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; forState:UIControlStateNormal];
[_clearButtonInternal addTarget:self action:@selector(onTapClear) forControlEvents:UIControlEventTouchUpInside]; [_clearButtonInternal setTitleColor:[UIColor blackColor]
forState:UIControlStateNormal];
[_clearButtonInternal addTarget:self
action:@selector(onTapClear)
forControlEvents:UIControlEventTouchUpInside];
} }
return _clearButtonInternal; return _clearButtonInternal;
} }
- (UIButton *)sendButtonInternal { - (UIButton *)sendButtonInternal {
if (!_sendButtonInternal) { if (!_sendButtonInternal) {
_sendButtonInternal = [self buildRightButtonWithTitle:KBLocalized(@"Send") color:[UIColor colorWithHex:0x02BEAC]]; _sendButtonInternal =
[_sendButtonInternal addTarget:self action:@selector(onTapSend) forControlEvents:UIControlEventTouchUpInside]; [self buildRightButtonWithTitle:KBLocalized(@"Send")
color:[UIColor colorWithHex:0x02BEAC]];
[_sendButtonInternal addTarget:self
action:@selector(onTapSend)
forControlEvents:UIControlEventTouchUpInside];
} }
return _sendButtonInternal; return _sendButtonInternal;
} }
#pragma mark - Expose #pragma mark - Expose
- (UICollectionView *)collectionView { return self.tagListView.collectionView; } - (UICollectionView *)collectionView {
return self.tagListView.collectionView;
}
//- (NSArray<NSString *> *)items { return self.itemsInternal; } //- (NSArray<NSString *> *)items { return self.itemsInternal; }
- (KBFunctionBarView *)barView { return self.barViewInternal; } - (KBFunctionBarView *)barView {
- (KBFunctionPasteView *)pasteView { return self.pasteViewInternal; } return self.barViewInternal;
- (UIButton *)pasteButton { return self.pasteButtonInternal; } }
- (UIButton *)deleteButton { return self.deleteButtonInternal; } - (KBFunctionPasteView *)pasteView {
- (UIButton *)clearButton { return self.clearButtonInternal; } return self.pasteViewInternal;
- (UIButton *)sendButton { return self.sendButtonInternal; } }
- (UIButton *)pasteButton {
return self.pasteButtonInternal;
}
- (UIButton *)deleteButton {
return self.deleteButtonInternal;
}
- (UIButton *)clearButton {
return self.clearButtonInternal;
}
- (UIButton *)sendButton {
return self.sendButtonInternal;
}
#pragma mark - Find Owner Controller #pragma mark - Find Owner Controller