ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Operit ToolPkg 包 Logo 支持实现指南:manifest 声明、运行时缓存读取与市场展示全链路解析

Operit ToolPkg 包 Logo 支持实现指南:manifest 声明、运行时缓存读取与市场展示全链路解析 AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载ToolPkg 是 Operit 的插件/工具包归档格式本指南围绕仓库文档 toolpkg_logo_support_20260820 中「Manifest And Runtime」分册1_manifest-and-runtime.md展开系统讲解 ToolPkg 包级 Logo 从 manifest 声明、解析校验、运行时缓存读取到插件列表/市场卡片渲染与发布流程的完整实现。读完本文你将掌握如何为自己的 ToolPkg 归档声明可选 Logo、理解其底层校验规则与数据流并了解 Operit 客户端如何在不升级 manifest 版本、不改归档结构的前提下完成包 Logo 的本地渲染与市场展示。一、背景ToolPkg 图标现状与能力复用在引入 Logo 支持之前Operit 中 ToolPkg 容器的图标呈现存在明显空缺包管理器Package Manager渲染的是通用 Apps 图标市场列表与市场详情头部渲染的是由标题派生的首字母色块avatar而模型 Provider 的 Logo 加载器ProviderLogoLoader已经具备 AndroidSVG 解析与 Bitmap 缩放能力但它被绑定在 APK assets 目录与 provider 标识上无法直接服务于包级 Logo见 2-rendering-and-ui.md 的 Existing 描述。同时ToolPkg 的运行时缓存已经提取了归档中的每一个条目runtime cache already extracts every archive entry这意味着只要在 manifest 中声明一个 Logo 资源键运行时无需引入任何新的归档格式或解压策略就能直接从缓存中读取该资源字节。因此该特性状态已标记为 [DONE]的核心思路是为 ToolPkg 添加一个可选的包 Logo 资源它随归档一起分发安装后在本地由包缓存渲染市场响应可额外提供可选的logoUrl用于展示但客户端不上传、不托管 Logo 文件。二、Manifest 契约logo字段与资源声明2.1 契约定义logo是 manifest 顶层的一个可选字段其值是一个资源键resource key必须指向resources数组中已声明的某个条目。官方契约示例摘自 index.md{ logo: plugin_logo, resources: [ { key: plugin_logo, path: resources/logo.svg, mime: image/svgxml } ] }支持的静态图片格式为SVG、PNG、JPEG 和 WebP。这与仓库的 ToolPkg 格式指南docs/TOOLPKP_FORMAT_GUIDE.md一致logo| string | 否 | 包 Logo 对应的resources[].key支持 SVG、PNG、JPEG 和 WebP在归档目录结构中Logo 文件一般放在resources/下如resources/logo.svg见 TOOLPKG_FORMAT_GUIDE.md 的目录树注释「可选包 Logo」。2.2 在完整 manifest 中的位置以格式指南的完整示例为参考logo与resources的配合关系如下节选{ schema_version: 1, toolpkg_id: com.operit.windows_bundle, version: 0.2.0, api_version: 1.0.0, main: main.js, logo: package_logo, resources: [ { key: package_logo, path: resources/logo.svg, mime: image/svgxml } ] }其中schema_version仍保持1不需要任何版本升级详见下文兼容性小节。三、解析与校验源码级实现3.1 manifest 数据模型在 ToolPkgParser.kt 中ToolPkgManifest通过SerialName(logo)接收任意 JSON 元素并对外暴露字符串访问器SerialName(logo) val logoElement: JsonElement? null, // ... val logo: String? get() (logoElement as? JsonPrimitive)?.takeIf { it.isString }?.content也就是说logo必须是 JSON 字符串资源键非字符串或缺失时返回null走「无 Logo」路径。3.2 资源解析与三条件校验在解析资源列表之后解析器调用resolveLogoResource(manifest.logo, resources)ToolPkgParser.kt其实现L1823-L1849依次执行以下校验private fun resolveLogoResource( logoResourceKey: String?, resources: ListToolPkgResourceRuntime ): ToolPkgResourceRuntime? { val key logoResourceKey?.trim().orEmpty() if (key.isBlank()) return null // 1. 未声明 → 返回 null val resource resources.firstOrNull { it.key.equals(key, ignoreCase true) } ?: throw IllegalArgumentException( manifest.logo must reference an existing resource key: $key ) // 2. 键必须命中 resources大小写不敏感 if (isDirectoryResourceMime(resource.mime)) { throw IllegalArgumentException( manifest.logo must reference a file resource: $key ) // 3a. 必须是文件资源不能是目录资源 val extension resource.path.substringAfterLast(., ).lowercase() val mime resource.mime.trim().lowercase() if (extension !in TOOLPKG_LOGO_EXTENSIONS mime !in TOOLPKG_LOGO_MIME_TYPES) { throw IllegalArgumentException( manifest.logo must reference an SVG, PNG, JPEG or WebP resource: $key ) // 3b. 扩展名或 MIME 必须受支持 } return resource } private val TOOLPKG_LOGO_EXTENSIONS setOf(svg, png, jpg, jpeg, webp) private val TOOLPKG_LOGO_MIME_TYPES setOf(image/svgxml, image/png, image/jpeg, image/webp)可归纳为三条规则校验点规则违反时的行为键存在性logo值必须在resources[].key中存在忽略大小写抛IllegalArgumentException包加载失败资源类型必须是文件资源MIME 不能是目录类型如inode/directory、application/x-directory等抛异常包加载失败格式支持扩展名 ∈{svg, png, jpg, jpeg, webp}或 MIME ∈{image/svgxml, image/png, image/jpeg, image/webp}抛异常包加载失败注意这里的校验是「扩展名或MIME 命中其一即可」同时扩展名与 MIME 不一致的场景也可以接受只要至少一端命中支持集合判定依据以解析器常量为准。解析成功后Logo 资源被存入运行时容器详情ToolPkgContainerRuntime.logoResource: ToolPkgResourceRuntime?ToolPkgParser.kt#L190并在构建容器时写入L1466。ToolPkgResourceRuntime携带key / path / mime三个字段完整描述了这个 Logo 资源。四、运行时数据流从容器详情到缓存字节读取4.1 公开容器详情携带 Logo 元数据PackageManager的公开容器详情数据类PackageManager.kt新增了两个字段val logoResourceKey: String? null, val logoMimeType: String? null,并定义了 Logo 字节的返回类型data class ToolPkgLogoBytes( val resourceKey: String, val mimeType: String, val fileName: String, val bytes: ByteArray )4.2 包管理器读取方法包管理器对外暴露readToolPkgLogoBytes(packageName)PackageManager.kt#L1457-L1469这是「读取缓存 Logo 字节」的唯一入口fun readToolPkgLogoBytes(packageName: String): ToolPkgLogoBytes? { ensureInitialized() val normalizedPackageName normalizePackageName(packageName) val runtime toolPkgContainers[normalizedPackageName] ?: return null val logoResource runtime.logoResource ?: return null val bytes readToolPkgResourceBytes(runtime, logoResource.path) ?: return null return ToolPkgLogoBytes( resourceKey logoResource.key, mimeType logoResource.mime, fileName logoResource.path.substringAfterLast(/), bytes bytes ) }关键点返回值null有两种情形包不存在或该包未声明 Logo——UI 层据此回退到通用图标/首字母色块字节直接来自已解压的运行时资源缓存readToolPkgResourceBytes无需重新解压归档fileName取资源路径最后一段供渲染器根据扩展名辅助判定格式。五、渲染与 UI复用 Provider Logo 的通用渲染器5.1 通用字节/流渲染器原 Provider Logo 实现ProviderLogoLoader.kt中的LogoBitmapLoader被重构为通用 byte/stream 渲染器同时服务 provider 与 ToolPkg 两种来源。其核心逻辑L76-L164SVG 路径使用 AndroidSVGSVG.getFromInputStream解析按目标尺寸等比缩放在正方形画布上居中绘制renderSvgToBitmap位图路径BitmapFactory.decodeStream解码后等比缩放并居中scaleBitmapToBitmap位图扩展名集合为png/jpg/jpeg/webpMIME 集合为image/png/image/jpeg/image/webp渲染前根据 MIME 或文件扩展名判定格式无法识别的返回null。与之配套的 Compose 入口rememberLogoPainter(logoKey, bytes, mimeType, fileName, size)L191-L222通过produceStateDispatchers.IO异步解码返回Painter?null表示无 Logo。5.2 Provider 专用逻辑保持不变Provider 特有的素材目录查找model_logos/{providerTypeId}/与深色模式染色providerLogoColorFilter()深色表面将黑色品牌素材染亮、浅色模式保留原色继续留在 Provider API 中ProviderLogoLoader.kt 与 L224-L233。5.3 插件 Logo 以原色渲染与 Provider Logo 不同ToolPkg 插件 Logo 在包管理器、市场列表、市场详情头部保留原始颜色渲染不套用 Provider 的染色逻辑未声明 Logo 的包与条目继续使用当前通用图标/首字母色块。各 UI 落点界面实现位置说明包管理器插件列表PackageManagerScreen.kt#L836-L838通过packageManager.readToolPkgLogoBytes(packageName)传入loadPluginLogo回调插件页 TabPluginTabContent.kt#L141rememberLogoPainter渲染插件 Logo包详情对话框PackageDetailsDialog.kt#L103-L111IO 线程读取字节后rememberLogoPainter例如包详情对话框中的读取与渲染withContext(Dispatchers.IO) { packageManager.readToolPkgLogoBytes(packageName) } // ... rememberLogoPainter(logoKey ..., bytes ..., mimeType ..., fileName ..., size ...)六、市场发布只读不传、本地预览与远程logoUrl6.1 发布屏只读取已声明的 Logo发布流程遵循「发布 ToolPkg 时仅使用归档中已有的manifest.logo资源」的原则TOOLPKG_FORMAT_GUIDE.md#L233。发布屏通过ToolPkgArtifactMinifier.readToolPkgLogoAsset(sourceFile)ToolPkgArtifactMinifier.kt#L20-L58从选定的归档文件中直接读取 Logo复用ToolPkgArchiveParser.readToolPkgManifestPreview解析 manifest校验logo键存在于resources中、且不是目录资源归一化资源相对路径后从 ZIP 中读取字节强制大小上限require(bytes.size PUBLISH_LOGO_MAX_BYTES)即 Logo 不得超过 512 KiBPUBLISH_LOGO_MAX_BYTES 512 * 1024定义于 ArtifactMarketModels.kt#L12返回ToolPkgLogoAsset(fileName, contentType, bytes)。发布屏ArtifactPublishScreen.kt在produceState中异步读取包 LogoL405-L419在发布卡片L870-L911与最终确认对话框L1277-L1281中展示预览并在发布预览对话框MarketPublishPreview.kt中以 48.dp列表卡片与 76.dp详情头部两种尺寸渲染L73-L90。关键约束客户端不在 publish / update / new-version 请求中上传、托管或发送任何 Logo 数据。市场侧对已安装包与包预览的渲染一律取自本地归档资源。6.2 市场条目的可选logoUrl市场条目响应模型MarketStatsApiService.kt#L312与市场浏览模型MarketBrowseList.kt#L70均包含可选字段logoUrl: String?由服务端返回后经MarketBrowseEntryMappersMarketBrowseEntryMappers.kt#L46映射到 UI 模型。远程 Logo 的加载由 RemoteLogoLoader.kt 完成安全与资源控制措施明确仅接受httpsURL非法/非 https 直接返回nullL51-L52单张 Logo 上限MAX_LOGO_BYTES 512 * 1024流式读取并实时校验L31、L76-L101内存 LRU 缓存上限 4 MBMAX_CACHE_BYTES 4 * 1024 * 1024OkHttp 连接超时 15 s、读取超时 20 s、跟随重定向L32-L48下载后的字节同样交给LogoBitmapLoader按 MIME/文件名渲染。市场详情头部UnifiedMarketDetailScreen.kt#L532-L569采用优先级策略本地包 LogologoPainter优先其次才是远程logoUrlval remoteLogoPainter rememberRemoteLogoPainter( logoUrl logoUrl.takeIf { logoPainter null }, size 76.dp ) val resolvedLogoPainter logoPainter ?: remoteLogoPainter两者皆为空时回退到标题首字母fallbackAvatarText色块。市场列表卡片同样遵循「本地优先、远程兜底」的取值逻辑MarketBrowseList.kt#L425-L442。七、兼容性与边界无logo的旧归档完全不受影响logo缺失时解析器返回null所有界面继续渲染通用 Apps 图标或标题首字母色块manifest 无需版本升级schema_version保持1api_version保持既有取值新旧归档在同一客户端内共存校验失败即拒绝加载若logo引用了不存在的资源键、目录资源或不支持的格式包加载会直接抛错避免出现「声明了但渲染不出」的中间态发布与市场解耦manifest.logo是归档内资源本地渲染logoUrl是市场可选展示字段远程渲染两者独立存在、互不替代。八、相关源码与文档索引特性总览与契约docs/TODO/toolpkg_logo_support_20260820/index.md分册文档1_manifest-and-runtime.md、2-rendering-and-ui.md、3-market-publish.md格式指南含logo字段与完整 manifest 示例docs/TOOLPKG_FORMAT_GUIDE.mdmanifest 模型与 Logo 解析校验ToolPkgParser.kt公开容器详情与缓存读取PackageManager.kt通用 Logo 渲染器ProviderLogoLoader.kt远程 Logo 加载器RemoteLogoLoader.kt发布侧 Logo 提取与大小限制ToolPkgArtifactMinifier.kt、ArtifactMarketModels.kt综上ToolPkg Logo 支持在不改归档格式、不升级 manifest 版本的前提下通过「manifest 可选声明 运行时资源缓存 通用 SVG/位图渲染器 市场可选logoUrl」四层设计为包管理器、插件列表、市场列表与详情页提供了完整一致的品牌化图标体验同时严格约束了发布与网络侧的 Logo 数据传输边界。赞分享AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆【免费下载链接】OperitThe most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent项目地址https://gitcode.com/gh_mirrors/op/Operit点击查看免费下载相关推荐Operit ToolPkg 市场 API 版本贯通从 manifest api_version 到发布链路与市场展示的实现指南Operit ToolPkg 市场 API 版本贯通从 manifest api_version 到发布链路与市场展示的实现指南 本文以 Operit 开源仓AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Operit ToolPkg Logo 支持指南为插件包声明与渲染可选包 Logo 的完整实现方案Operit ToolPkg Logo 支持指南为插件包声明与渲染可选包 Logo 的完整实现方案 本文以 Operit 开源仓库中 docs/TODO/toAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Operit ToolPkg 市场发布中的 Logo 支持manifest.logo 本地资源与 logoUrl 远端展示的分工Operit ToolPkg 市场发布中的 Logo 支持manifest.logo 本地资源与 logoUrl 远端展示的分工 本篇技术指南围绕 OperiAI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化上一篇探索Dogescript生态系统插件、工具与第三方库推荐下一篇Akagi麻将助手从新手到高手的完整实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表