This commit is contained in:
2025-12-17 17:01:49 +08:00
parent dde8716262
commit 1e04e7c39a
21 changed files with 180 additions and 18 deletions

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "home_ai_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "home_ai_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "home_chat_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "home_chat_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "home_emotion_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "home_emotion_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "home_keyboard_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "home_keyboard_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "keybord_bg_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "keybord_bg_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -299,6 +299,10 @@ static void KBSkinInstallNotificationCallback(CFNotificationCenterRef center,
} }
} }
- (void)functionViewDidRequestSubscription:(KBFunctionView *)functionView {
[self showSubscriptionPanel];
}
#pragma mark - KBKeyboardSubscriptionViewDelegate #pragma mark - KBKeyboardSubscriptionViewDelegate
- (void)subscriptionViewDidTapClose:(KBKeyboardSubscriptionView *)view { - (void)subscriptionViewDidTapClose:(KBKeyboardSubscriptionView *)view {

View File

@@ -185,9 +185,12 @@ didReceiveResponse:(NSURLResponse *)response
didCompleteWithError:(nullable NSError *)error; didCompleteWithError:(nullable NSError *)error;
{ {
if (_closedByUser) { if (_closedByUser) {
_buffer = [NSMutableData data];
return; return;
} }
[self _dispatchPlainBufferIfNeeded];
WJXEvent *event = [[WJXEvent alloc] initWithReadyState:WJXEventStateClosed]; WJXEvent *event = [[WJXEvent alloc] initWithReadyState:WJXEventStateClosed];
if (nil == (event.error = error)) { if (nil == (event.error = error)) {
event.error = [NSError errorWithDomain:@"WJXEventSource" code:event.readyState userInfo:@{ event.error = [NSError errorWithDomain:@"WJXEventSource" code:event.readyState userInfo:@{
@@ -239,13 +242,16 @@ didCompleteWithError:(nullable NSError *)error;
WJXEvent *event = [[WJXEvent alloc] initWithReadyState:WJXEventStateOpen]; WJXEvent *event = [[WJXEvent alloc] initWithReadyState:WJXEventStateOpen];
NSString *eventString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSString *eventString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (eventString.length == 0) { return; }
NSArray *lines = [eventString componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet]; NSArray *lines = [eventString componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet];
BOOL hasDataLine = NO;
for (NSString *line in lines) { for (NSString *line in lines) {
if ([line hasPrefix:@"id:"]) { if ([line hasPrefix:@"id:"]) {
event.eventId = [[line substringFromIndex:3] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; event.eventId = [[line substringFromIndex:3] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
} else if ([line hasPrefix:@"event:"]) { } else if ([line hasPrefix:@"event:"]) {
event.event = [[line substringFromIndex:6] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; event.event = [[line substringFromIndex:6] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
} else if ([line hasPrefix:@"data:"]) { } else if ([line hasPrefix:@"data:"]) {
hasDataLine = YES;
NSString *data = [[line substringFromIndex:5] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; NSString *data = [[line substringFromIndex:5] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
event.data = event.data ? [event.data stringByAppendingFormat:@"\n%@", data] : data; event.data = event.data ? [event.data stringByAppendingFormat:@"\n%@", data] : data;
} else if ([line hasPrefix:@"retry:"]) { } else if ([line hasPrefix:@"retry:"]) {
@@ -254,6 +260,13 @@ didCompleteWithError:(nullable NSError *)error;
} }
} }
if (!hasDataLine) {
NSString *trimmed = [eventString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (trimmed.length > 0) {
event.data = trimmed;
}
}
if (event.eventId) { if (event.eventId) {
self.lastEventId = event.eventId; self.lastEventId = event.eventId;
} }
@@ -272,6 +285,15 @@ didCompleteWithError:(nullable NSError *)error;
}]; }];
} }
- (void)_dispatchPlainBufferIfNeeded
{
if (_buffer.length == 0) { return; }
NSData *data = [_buffer copy];
[_buffer setLength:0];
if (data.length == 0) { return; }
[self _parseEventData:data];
}
#pragma mark - #pragma mark -
#pragma mark Setters #pragma mark Setters

View File

@@ -12,6 +12,7 @@
@optional @optional
- (void)functionView:(KBFunctionView *_Nullable)functionView didTapToolActionAtIndex:(NSInteger)index; - (void)functionView:(KBFunctionView *_Nullable)functionView didTapToolActionAtIndex:(NSInteger)index;
- (void)functionView:(KBFunctionView *_Nullable)functionView didRightTapToolActionAtIndex:(NSInteger)index; - (void)functionView:(KBFunctionView *_Nullable)functionView didRightTapToolActionAtIndex:(NSInteger)index;
- (void)functionViewDidRequestSubscription:(KBFunctionView *_Nullable)functionView;
@end @end

View File

@@ -24,6 +24,7 @@
#import "WJXEventSource.h" #import "WJXEventSource.h"
#import "KBTagItemModel.h" #import "KBTagItemModel.h"
#import <MJExtension/MJExtension.h> #import <MJExtension/MJExtension.h>
#import "KBBizCode.h"
@interface KBFunctionView () <KBFunctionBarViewDelegate, KBStreamOverlayViewDelegate, KBFunctionTagListViewDelegate> @interface KBFunctionView () <KBFunctionBarViewDelegate, KBStreamOverlayViewDelegate, KBFunctionTagListViewDelegate>
// UI // UI
@@ -347,6 +348,7 @@
NSError *error = nil; NSError *error = nil;
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
if (error || ![payload isKindOfClass:[NSDictionary class]]) { return; } 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; }
@@ -395,6 +397,32 @@
self.eventSourceDidReceiveDone = NO; self.eventSourceDidReceiveDone = NO;
} }
- (BOOL)kb_handleBizErrorIfNeeded:(NSDictionary *)payload {
NSInteger code = KBBizCodeFromJSONObject(payload);
if (code == NSNotFound || code == KBBizCodeSuccess) {
return NO;
}
BOOL needSubscriptionGuide = (code == KBBizCodeQuotaExhausted);
NSString *msg = KBBizMessageFromJSONObject(payload);
if (msg.length == 0) {
msg = KBLocalized(@"拉取失败");
}
NSError *bizError = [NSError errorWithDomain:@"KBStreamBizError"
code:code
userInfo:@{NSLocalizedDescriptionKey: msg}];
[self kb_finishEventSourceWithError:bizError];
if (needSubscriptionGuide) {
[self kb_requestSubscriptionGuide];
}
return YES;
}
- (void)kb_requestSubscriptionGuide {
if ([self.delegate respondsToSelector:@selector(functionViewDidRequestSubscription:)]) {
[self.delegate functionViewDidRequestSubscription:self];
}
}
#pragma mark - Event Parsing #pragma mark - Event Parsing
- (NSString *)kb_normalizedLLMChunkString:(id)dataValue { - (NSString *)kb_normalizedLLMChunkString:(id)dataValue {

View File

@@ -17,7 +17,7 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
@end @end
@interface KBKeyboardSubscriptionView () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout> @interface KBKeyboardSubscriptionView () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) UIView *cardView; @property (nonatomic, strong) UIImageView *cardView;
@property (nonatomic, strong) UIButton *closeButton; @property (nonatomic, strong) UIButton *closeButton;
@property (nonatomic, strong) UILabel *titleLabel; @property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIScrollView *featureScrollView; @property (nonatomic, strong) UIScrollView *featureScrollView;
@@ -35,7 +35,6 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
@property (nonatomic, assign, getter=isLoading) BOOL loading; @property (nonatomic, assign, getter=isLoading) BOOL loading;
@property (nonatomic, strong) CADisplayLink *featureDisplayLink; @property (nonatomic, strong) CADisplayLink *featureDisplayLink;
@property (nonatomic, assign) CGFloat featureLoopWidth; @property (nonatomic, assign) CGFloat featureLoopWidth;
@property (nonatomic, strong) CAGradientLayer *cardGradientLayer;
@end @end
@implementation KBKeyboardSubscriptionView @implementation KBKeyboardSubscriptionView
@@ -56,7 +55,6 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
- (void)layoutSubviews { - (void)layoutSubviews {
[super layoutSubviews]; [super layoutSubviews];
self.cardGradientLayer.frame = self.cardView.bounds;
self.featureLoopWidth = self.featureScrollView.contentSize.width * 0.5f; self.featureLoopWidth = self.featureScrollView.contentSize.width * 0.5f;
[self startFeatureTickerIfNeeded]; [self startFeatureTickerIfNeeded];
} }
@@ -98,10 +96,10 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
- (void)setupCardView { - (void)setupCardView {
[self addSubview:self.cardView]; [self addSubview:self.cardView];
[self.cardView mas_makeConstraints:^(MASConstraintMaker *make) { [self.cardView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.mas_left).offset(6); make.left.equalTo(self.mas_left).offset(0);
make.right.equalTo(self.mas_right).offset(-6); make.right.equalTo(self.mas_right).offset(0);
make.top.equalTo(self.mas_top).offset(4); make.top.equalTo(self.mas_top).offset(0);
make.bottom.equalTo(self.mas_bottom).offset(-4); make.bottom.equalTo(self.mas_bottom).offset(0);
}]; }];
[self.cardView addSubview:self.closeButton]; [self.cardView addSubview:self.closeButton];
@@ -422,17 +420,13 @@ static NSString * const kKBKeyboardSubscriptionCellId = @"kKBKeyboardSubscriptio
#pragma mark - Lazy #pragma mark - Lazy
- (UIView *)cardView { - (UIImageView *)cardView {
if (!_cardView) { if (!_cardView) {
_cardView = [[UIView alloc] init]; _cardView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"keybord_bg_icon"]];
_cardView.layer.cornerRadius = 20; // _cardView.layer.cornerRadius = 20;
_cardView.layer.masksToBounds = YES; // _cardView.layer.masksToBounds = YES;
_cardGradientLayer = [CAGradientLayer layer]; _cardView.contentMode = UIViewContentModeScaleAspectFill;
_cardGradientLayer.colors = @[ (id)[UIColor colorWithRed:0.80 green:0.96 blue:0.91 alpha:1].CGColor, _cardView.clipsToBounds = true;
(id)[UIColor colorWithRed:0.72 green:0.89 blue:0.98 alpha:1].CGColor ];
_cardGradientLayer.startPoint = CGPointMake(0, 0);
_cardGradientLayer.endPoint = CGPointMake(1, 1);
[_cardView.layer insertSublayer:_cardGradientLayer atIndex:0];
} }
return _cardView; return _cardView;
} }

View File

@@ -76,6 +76,9 @@ typedef NS_ENUM(NSInteger, KBBizCode) {
/// 特定收据无效 /// 特定收据无效
KBBizCodeReceiptError = 50016, KBBizCodeReceiptError = 50016,
/// 免费次数耗尽,需要订阅
KBBizCodeQuotaExhausted = 50022,
}; };
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN