Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View File

@@ -0,0 +1,183 @@
/**
* Gemini API 工具函数
*/
import { ImageInput, ProcessedContent } from './types';
/**
* 将文件转换为 base64 格式
*/
export async function fileToBase64(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const result = reader.result as string;
resolve(result);
};
reader.onerror = () => reject(new Error('文件读取失败'));
reader.readAsDataURL(file);
});
}
/**
* 准备图片数据,转换为 API 所需格式
*/
export async function prepareImageData(image: ImageInput): Promise<string> {
if (image.file) {
return await fileToBase64(image.file);
} else if (image.base64) {
// 确保 base64 数据包含正确的前缀
if (image.base64.startsWith('data:')) {
return image.base64;
} else {
return `data:image/png;base64,${image.base64}`;
}
} else if (image.url) {
// 对于 URL直接返回API 可能支持 URL 格式)
return image.url;
} else {
throw new Error('无效的图片输入:必须提供 file、base64 或 url');
}
}
/**
* 将 base64 数据转换为 Blob URL
*/
export function base64ToBlobUrl(base64Data: string, mimeType: string = 'image/png'): string {
const byteCharacters = atob(base64Data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: mimeType });
return URL.createObjectURL(blob);
}
/**
* 处理混合内容文字、base64图片、URL图片、视频链接
*/
export function processMixedContent(content: string): ProcessedContent {
// 查找 base64 图片
const base64Pattern = /data:image\/[^;]+;base64,([A-Za-z0-9+/=]+)/g;
const base64Matches = Array.from(content.matchAll(base64Pattern));
// 优先查找 Markdown 格式的图片 ![alt](url)
// 这能正确提取带查询参数的完整 URL
const markdownImagePattern = /!\[[^\]]*\]\((https?:\/\/[^)]+)\)/gi;
const markdownImageMatches = Array.from(content.matchAll(markdownImagePattern));
// 查找图片 URL 链接(支持带查询参数的 URL
// Original: /https?:\/\/[^\s<>"'\]]+\.(png|jpg|jpeg|gif|webp)/gi
const imageUrlPattern = /https?:\/\/[^\s<>"'\])]+\.(png|jpg|jpeg|gif|webp)(\?[^\s<>"'\])]*)?/gi;
const imageUrlMatches = Array.from(content.matchAll(imageUrlPattern));
// 查找视频 URL 链接包括markdown格式
const videoUrlPatterns = [
// 匹配markdown链接中的视频URL[▶️ 在线观看](url) 或 [⏬ 下载视频](url)
/\[(?:▶️\s*在线观看|⏬\s*下载视频|.*?观看.*?|.*?下载.*?)\]\(([^)]+\.(?:mp4|avi|mov|wmv|flv|webm|mkv)(?:\?[^)]*)?)\)/gi,
// 直接的视频URL
/https?:\/\/[^\s<>"'\]]+\.(?:mp4|avi|mov|wmv|flv|webm|mkv)(?:\?[^\s<>"'\]]*)?/gi,
// 特定域名的视频链接如filesystem.site
/https?:\/\/filesystem\.site\/[^\s<>"'\]]+/gi
];
let textContent = content;
const images: ProcessedContent['images'] = [];
const videos: Array<{ type: 'url'; data: string; index: number }> = [];
let imageIndex = 1;
let videoIndex = 1;
// 处理 base64 图片
for (const match of base64Matches) {
const fullMatch = match[0];
const base64Data = match[1];
images.push({
type: 'base64',
data: base64Data,
index: imageIndex,
});
textContent = textContent.replace(fullMatch, `[图片 ${imageIndex}]`);
imageIndex++;
}
// 用于避免重复处理同一 URL
const processedUrls = new Set<string>();
// 优先处理 Markdown 格式的图片 ![alt](url) - 能正确提取带查询参数的完整 URL
for (const match of markdownImageMatches) {
const fullMatch = match[0]; // ![alt](url)
const url = match[1]; // 括号内的完整 URL
if (!processedUrls.has(url)) {
processedUrls.add(url);
images.push({
type: 'url',
data: url,
index: imageIndex,
});
textContent = textContent.replace(fullMatch, `[图片 ${imageIndex}]`);
imageIndex++;
}
}
// 处理普通图片 URL排除已处理的
for (const match of imageUrlMatches) {
const url = match[0];
if (!processedUrls.has(url)) {
processedUrls.add(url);
images.push({
type: 'url',
data: url,
index: imageIndex,
});
textContent = textContent.replace(url, `[图片 ${imageIndex}]`);
imageIndex++;
}
}
// 处理视频 URL按优先级顺序
for (const pattern of videoUrlPatterns) {
const matches = Array.from(content.matchAll(pattern));
for (const match of matches) {
let videoUrl: string;
if (match.length > 1 && match[1]) {
// markdown链接格式提取括号内的URL
videoUrl = match[1];
} else {
// 直接的URL
videoUrl = match[0];
}
// 清理URL末尾可能的标点符号
videoUrl = videoUrl.replace(/[.,;!?]*$/, '');
// 检查是否已经添加过这个视频URL
const alreadyExists = videos.some(v => v.data === videoUrl);
if (!alreadyExists) {
videos.push({
type: 'url',
data: videoUrl,
index: videoIndex,
});
// 替换原文中的内容
textContent = textContent.replace(match[0], `[视频 ${videoIndex}]`);
videoIndex++;
}
}
}
return {
textContent,
images,
videos: videos.length > 0 ? videos : undefined,
originalContent: content,
};
}