添加皮肤

This commit is contained in:
2025-11-19 15:39:47 +08:00
parent 7518a29d2f
commit cc55bb107a
86 changed files with 17599 additions and 3467 deletions

View File

@@ -54,93 +54,109 @@
// - key_icons
// * KB_SKIN_ICON_USE_REMOTE==0 value Assets "kb_space_melon"
// * KB_SKIN_ICON_USE_REMOTE==1 value Zip "key_space" Skins/<skinId>/icons/key_space.png
// self.skins = @[
// @{
// @"id": @"melon",
// @"name": KBLocalized(@"蜜瓜主题"),
// @"background_image": @"https://picsum.photos/id/1018/800/450.jpg",
// @"background": @"#F5FFE8",
// @"key_bg": @"#FFFFFF",
// @"key_text": @"#4A4A4A",
// @"key_highlight": @"#D9F4C4",
// @"accent": @"#A4D68A",
// @"hidden_keys": @[
// @"letter_q",@"letter_w",@"letter_e",@"letter_r",@"letter_t",
// @"letter_y",@"letter_u",@"letter_i",@"letter_o",@"letter_p",
// @"letter_a",@"letter_s",@"letter_d",@"letter_f",@"letter_g",
// @"letter_h",@"letter_j",@"letter_k",@"letter_l",
// @"letter_z",@"letter_x",@"letter_c",@"letter_v",
// @"letter_b",@"letter_n",@"letter_m",
// @"space"
// ],
// // Assets
// // Zip Zip "key_space"
// @"key_icons": @{
// //
// // *_lower / *_upper
// @"letter_q_lower": @"key_q",
// @"letter_q_upper": @"key_q",
// @"letter_w_lower": @"key_w",
// @"letter_w_upper": @"key_w",
// @"letter_e_lower": @"key_e",
// @"letter_e_upper": @"key_e",
// @"letter_r_lower": @"key_r",
// @"letter_r_upper": @"key_r",
// @"letter_t_lower": @"key_t",
// @"letter_t_upper": @"key_t",
// @"letter_y_lower": @"key_y",
// @"letter_y_upper": @"key_y",
// @"letter_u_lower": @"key_u",
// @"letter_u_upper": @"key_u",
// @"letter_i_lower": @"key_i",
// @"letter_i_upper": @"key_i",
// @"letter_o_lower": @"key_o",
// @"letter_o_upper": @"key_o",
// @"letter_p_lower": @"key_p",
// @"letter_p_upper": @"key_p",
//
// @"letter_a_lower": @"key_a",
// @"letter_a_upper": @"key_a",
// @"letter_s_lower": @"key_s",
// @"letter_s_upper": @"key_s",
// @"letter_d_lower": @"key_d",
// @"letter_d_upper": @"key_d",
// @"letter_f_lower": @"key_f",
// @"letter_f_upper": @"key_f",
// @"letter_g_lower": @"key_g",
// @"letter_g_upper": @"key_g",
// @"letter_h_lower": @"key_h",
// @"letter_h_upper": @"key_h",
// @"letter_j_lower": @"key_j",
// @"letter_j_upper": @"key_j",
// @"letter_k_lower": @"key_k",
// @"letter_k_upper": @"key_k",
// @"letter_l_lower": @"key_l",
// @"letter_l_upper": @"key_l",
//
// @"letter_z_lower": @"key_z",
// @"letter_z_upper": @"key_z",
// @"letter_x_lower": @"key_x",
// @"letter_x_upper": @"key_x",
// @"letter_c_lower": @"key_c",
// @"letter_c_upper": @"key_c",
// @"letter_v_lower": @"key_v",
// @"letter_v_upper": @"key_v",
// @"letter_b_lower": @"key_b",
// @"letter_b_upper": @"key_b",
// @"letter_n_lower": @"key_n",
// @"letter_n_upper": @"key_n",
// @"letter_m_lower": @"key_m",
// @"letter_m_upper": @"key_m",
//
// //
// @"space": @"key_space", //
// @"backspace": @"key_del", //
// @"shift": @"key_up", // Shift
// @"mode_123": @"key_123", // "123"
// @"ai": @"key_ai", // AI
// @"return": @"key_send" // /
// }
// }
// ];
self.skins = @[
@{
@"id": @"melon",
@"name": KBLocalized(@"蜜瓜主题"),
@"background_image": @"https://picsum.photos/id/1018/800/450.jpg",
@"id": @"local001",
@"name": @"本地001皮肤",
// zip_url bundle:// +
@"zip_url": @"bundle://001.zip",
// 便
@"background": @"#F5FFE8",
@"key_bg": @"#FFFFFF",
@"key_text": @"#4A4A4A",
@"key_highlight": @"#D9F4C4",
@"accent": @"#A4D68A",
@"hidden_keys": @[
@"letter_q",@"letter_w",@"letter_e",@"letter_r",@"letter_t",
@"letter_y",@"letter_u",@"letter_i",@"letter_o",@"letter_p",
@"letter_a",@"letter_s",@"letter_d",@"letter_f",@"letter_g",
@"letter_h",@"letter_j",@"letter_k",@"letter_l",
@"letter_z",@"letter_x",@"letter_c",@"letter_v",
@"letter_b",@"letter_n",@"letter_m",
@"space"
],
// Assets
// Zip Zip "key_space"
@"key_icons": @{
//
// *_lower / *_upper
@"letter_q_lower": @"key_q",
@"letter_q_upper": @"key_q",
@"letter_w_lower": @"key_w",
@"letter_w_upper": @"key_w",
@"letter_e_lower": @"key_e",
@"letter_e_upper": @"key_e",
@"letter_r_lower": @"key_r",
@"letter_r_upper": @"key_r",
@"letter_t_lower": @"key_t",
@"letter_t_upper": @"key_t",
@"letter_y_lower": @"key_y",
@"letter_y_upper": @"key_y",
@"letter_u_lower": @"key_u",
@"letter_u_upper": @"key_u",
@"letter_i_lower": @"key_i",
@"letter_i_upper": @"key_i",
@"letter_o_lower": @"key_o",
@"letter_o_upper": @"key_o",
@"letter_p_lower": @"key_p",
@"letter_p_upper": @"key_p",
@"letter_a_lower": @"key_a",
@"letter_a_upper": @"key_a",
@"letter_s_lower": @"key_s",
@"letter_s_upper": @"key_s",
@"letter_d_lower": @"key_d",
@"letter_d_upper": @"key_d",
@"letter_f_lower": @"key_f",
@"letter_f_upper": @"key_f",
@"letter_g_lower": @"key_g",
@"letter_g_upper": @"key_g",
@"letter_h_lower": @"key_h",
@"letter_h_upper": @"key_h",
@"letter_j_lower": @"key_j",
@"letter_j_upper": @"key_j",
@"letter_k_lower": @"key_k",
@"letter_k_upper": @"key_k",
@"letter_l_lower": @"key_l",
@"letter_l_upper": @"key_l",
@"letter_z_lower": @"key_z",
@"letter_z_upper": @"key_z",
@"letter_x_lower": @"key_x",
@"letter_x_upper": @"key_x",
@"letter_c_lower": @"key_c",
@"letter_c_upper": @"key_c",
@"letter_v_lower": @"key_v",
@"letter_v_upper": @"key_v",
@"letter_b_lower": @"key_b",
@"letter_b_upper": @"key_b",
@"letter_n_lower": @"key_n",
@"letter_n_upper": @"key_n",
@"letter_m_lower": @"key_m",
@"letter_m_upper": @"key_m",
//
@"space": @"key_space", //
@"backspace": @"key_del", //
@"shift": @"key_up", // Shift
@"mode_123": @"key_123", // "123"
@"ai": @"key_ai", // AI
@"return": @"key_send" // /
}
@"accent": @"#A4D68A"
// key_icons
}
];

View File

@@ -339,16 +339,18 @@
#if __has_include(<SSZipArchive/SSZipArchive.h>)
// zip_url Zip
//
// 1) 线 URLhttp/https KBNetworkManager
// 2) zip_url "bundle://" "bundle://001.zip"
if (!hasCachedAssets && zipURL.length > 0) {
dispatch_group_enter(group);
[[KBNetworkManager shared] GET:zipURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) {
NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil);
if (error || data.length == 0) {
void (^handleZipData)(NSData *) = ^(NSData *data) {
if (data.length == 0) {
zipOK = NO;
dispatch_group_leave(group);
return;
}
// Zip
[[NSFileManager defaultManager] createDirectoryAtPath:skinRoot
withIntermediateDirectories:YES
@@ -370,9 +372,81 @@
[fm removeItemAtPath:zipPath error:nil];
if (!ok || unzipError) {
zipOK = NO;
dispatch_group_leave(group);
return;
}
//
// Skins/<skinId>/icons Skins/<skinId>/<>/icons
// icons background.png
BOOL isDir2 = NO;
NSArray *iconsContent = [fm contentsOfDirectoryAtPath:iconsDir error:NULL];
BOOL iconsValid = ([fm fileExistsAtPath:iconsDir isDirectory:&isDir2] && isDir2 && iconsContent.count > 0);
if (!iconsValid) {
NSArray<NSString *> *subItems = [fm contentsOfDirectoryAtPath:skinRoot error:NULL];
for (NSString *name in subItems) {
if ([name isEqualToString:@"icons"] || [name isEqualToString:@"__MACOSX"]) continue;
NSString *nestedRoot = [skinRoot stringByAppendingPathComponent:name];
BOOL isDirNested = NO;
if (![fm fileExistsAtPath:nestedRoot isDirectory:&isDirNested] || !isDirNested) continue;
NSString *nestedIcons = [nestedRoot stringByAppendingPathComponent:@"icons"];
BOOL isDirNestedIcons = NO;
if ([fm fileExistsAtPath:nestedIcons isDirectory:&isDirNestedIcons] && isDirNestedIcons) {
NSArray *nestedFiles = [fm contentsOfDirectoryAtPath:nestedIcons error:NULL];
if (nestedFiles.count > 0) {
// icons
[fm createDirectoryAtPath:iconsDir
withIntermediateDirectories:YES
attributes:nil
error:NULL];
// icons
for (NSString *fn in nestedFiles) {
NSString *from = [nestedIcons stringByAppendingPathComponent:fn];
NSString *to = [iconsDir stringByAppendingPathComponent:fn];
[fm removeItemAtPath:to error:nil];
[fm moveItemAtPath:from toPath:to error:nil];
}
}
}
// background.png skinRoot
NSString *nestedBg = [nestedRoot stringByAppendingPathComponent:@"background.png"];
if ([fm fileExistsAtPath:nestedBg]) {
[fm removeItemAtPath:bgPath error:nil];
[fm moveItemAtPath:nestedBg toPath:bgPath error:nil];
}
}
}
dispatch_group_leave(group);
}];
};
// bundle zip_url "bundle://001.zip"
if ([zipURL hasPrefix:@"bundle://"]) {
NSString *name = [zipURL substringFromIndex:@"bundle://".length];
NSString *fileName = name ?: @"";
NSString *ext = fileName.pathExtension;
NSString *base = fileName;
if (ext.length == 0) {
ext = @"zip";
} else {
base = [fileName stringByDeletingPathExtension];
}
NSString *path = [[NSBundle mainBundle] pathForResource:base ofType:ext];
NSData *data = (path.length > 0) ? [NSData dataWithContentsOfFile:path] : nil;
handleZipData(data);
} else {
//
[[KBNetworkManager shared] GET:zipURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) {
NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil);
if (error || data.length == 0) {
zipOK = NO;
dispatch_group_leave(group);
return;
}
handleZipData(data);
}];
}
}
#else
zipOK = NO;

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB