NPP 元数据
本文档详细介绍 NPP 插件系统中每个 MetaData 项的说明、格式以及需要注意的事项,以帮助开发者能够正确配置插件。
1. 必填元数据
1.1 @id - 插件唯一标识符
说明
@id 是插件的唯一标识符,用于系统内部识别和管理插件,是插件存储、更新和卸载的关键标识。
格式
- 类型: 字符串
- 格式: 13位时间戳 + UUIDv4 nitaiPage 通过以下两步完成验证:
// 时间戳
D{13} > 1749401460000
// UUIDv4
/^([0-9]{13})_[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/这两步只对普通插件生效。 @type 为 coreNpp 或 translate 的插件整段跳过校验,@id 直接写可读的名字即可 —— 官方插件里就是 themeColor、customStyle、advancedSettings 这种。
注意事项
- ID 必须全局唯一,不能与其他插件冲突
- 一旦设置,不建议更改 (会影响安装、更新、卸载和存储)
- 建议仅使用小写字母,避免大小写混淆
1.2 @version - 插件版本
说明
@version 用于定义插件的版本号,用于版本管理、更新检查和依赖关系验证。
格式
- 类型: 字符串
- 格式: MAJOR.MINOR.PATCH (纯数字)
示例
// ==Npplication==
// @version 1.0.0
// @version 2.1.3
// ==/Npplication==注意事项
- MAJOR: 主版本号,不兼容的API修改
- MINOR: 次版本号,向下兼容的功能性新增
- PATCH: 修订号,向下兼容的问题修正
- 纯数字: 禁止使用连字符和标识符(如-beta.1)
1.3 @time - 加载时机
注意
- 此项对于
translate插件为非必填项,NitaiPage会强制将translate插件的加载时机设置为body - 对于非
translate插件,此项为必填项
说明
@time 用于指定插件的加载时机,控制插件在页面加载过程中的执行时机。
格式
- 类型: 字符串
- 可选:
head或body(默认)
示例
// ==Npplication==
// @time head
// @time body
// ==/Npplication==说明
head: 在页面 <head>部分加载,适用于需要尽早执行的插件 body: 在页面 <body>部分加载,适用于大多数插件
注意事项
- 插件应尽量使用
body加载 head适用于需要修改页面初始状态的插件- 需要考虑插件对页面加载性能的影响,选择错误的加载时机可能导致功能异常
1.4 @translates - 语言代码
重要!
- 仅针对
@type为translate的插件必填,对于非translate插件为可选元数据 - 文档此部分描述仅适用
@type为translate的插件 - 若您要为插件关联翻译项请点击此处查看适用于非
translate的格式要求
说明
@translates 用于声明此项翻译面向的语言。
格式
- 类型: 字符串
- 格式: 单个语言代码
示例
// ==Npplication==
// @translates zh-CN
// @translates en-US
// ==/Npplication==注意事项
- 仅支持识别单个语言代码
2. 可选元数据
2.1 @description - 插件描述
说明
@description 用于提供插件的详细描述信息,帮助用户了解插件的特点。
格式
- 类型: 字符串
- 长度: 无限制,建议10-500个字符
示例
// ==Npplication==
// @description 一个简单实用的计算器插件
// @description Weather Widget - 显示实时天气信息的小工具
// ==/Npplication==注意事项
- 描述应简洁明了,突出插件核心功能
- 虽不必要,但强烈建议填写
2.2 @author - 插件作者
说明
@author 用于标识插件的作者。
格式
- 类型: 字符串
- 长度: 无限制,建议5-20个字符
示例
// ==Npplication==
// @author 张三
// @author John Doe
// @author NitaiDev Team
// @author example@email.com
// ==/Npplication==注意事项
- 支持包含邮箱地址等联系方式
- 不建议使用他人的作者标识
- 建议使用一致的作者标识
- 虽不必要,但强烈建议填写
2.3 @name - 名称
说明
@name 用于定义插件的显示名称,这个名称将展示给用户。
格式
- 类型: 字符串
- 长度: 建议不要超出 16 字符
- 字符集: 支持中文、英文、数字和符号
示例
// ==Npplication==
// @name 我的插件
// @name My_Npp
// ==/Npplication==注意事项
- 名称应简短
- 尽量避免使用特殊字符和表情符号
- 不建议与其他插件名称重复
- 虽不必要,但强烈建议填写
2.4 @icon - 插件图标
说明
@icon 用于指定插件的图标URL,用于在用户界面中显示插件图标。
格式
- 类型: 字符串 (URL)
- 协议: http 或 https
- 格式: 图片 URL 或 Base64
示例
// ==Npplication==
// @icon https://example.com/icon.png
// @icon https://cdn.jsdelivr.net/gh/user/repo/icon.svg
// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==
// ==/Npplication==注意事项
- 建议使用正方形图标 (如 48x48 或 96x96)
- 支持 PNG、JPG、SVG 等常见格式
- 图标 URL 应稳定可靠,避免失效
2.5 @dependencies - 依赖项
说明
@dependencies 用于声明插件所依赖的其他插件,在插件安装时需要满足对应条件才可安装。
格式
- 类型: 字符串
- 格式:
['插件 URL':'版本号'],多个依赖用逗号分隔
示例
// ==Npplication==
// @dependencies [`https://example.com/plugin.js`] // 单个依赖项,使用 Latest 版本
// @dependencies [`https://example.com/plugin.js`:`1.0.0`] // 单个依赖项,指定版本
// @dependencies [`https://example.com/plugin1.js`:`1.2.0`, `https://example.com/plugin2.js`] // 多个依赖项,使用逗号分隔
// ==/Npplication==注意事项
- 谨慎指定依赖版本,确保兼容性
- 考虑依赖项的加载顺序 (可能需要通知用户手动更改加载顺序)
2.6 @type - 插件类型
说明
@type 用于指定插件的类型。
- normal: 普通插件,默认类型,系统普通功能插件
- coreNpp: 核心插件,系统核心功能插件,不允许卸载和新安装,只能更新
- translate: 翻译插件,用于翻译插件界面
格式要求
- 类型: 字符串
- 可选值:
coreNpp,translate - 默认值:
normal
示例
// ==Npplication==
// @type nomal
// @type coreNpp
// @type translate
// ==/Npplication==2.7 @setting - 是否注册设置界面
说明
@setting 用于告诉 nitaiPage 插件是否需要设置界面。
格式要求
- 类型: 布尔值
- 可选:
true或false(默认)
示例
// ==Npplication==
// @setting true
// @setting false
// ==/Npplication==具体设置及调用方法,请参考NPP 设置页面内容添加指南
使用的组件可参考插件设置项组件
2.8 @screen - 屏幕截图
说明
@screen 用于向用户提供插件预览。
格式要求
- 类型: 字符串
示例
// ==Npplication==
// @screen "https://example.com/image.png" // 单个图片链接地址
// @screen ["url1.png", "url2.png"] // 多个图片链接地址 (数组)
// @screen "url1.png,url2.png" //或直接使用逗号分隔多个图片
// ==/Npplication==注意事项
商店也可以使用此 Key 设置截图 (参考商店数据格式), 在插件详细页读取截图时会优先读取插件元数据中的截图设置, 而不是商店的插件清单
2.9 @forced - 是否开启强制更新
说明
@forced 用于强制更新插件,仅显示更新完成的短消息。
格式要求
- 类型: 布尔值
- 可选:
true或false(默认)
示例
// ==Npplication==
// @forced true
// @forced false
// ==/Npplication==2.10 @associations - 关联插件
说明
@associations 用于声明插件与其他插件的关联关系。
格式
- 类型: 字符串
- 格式:
['插件 URL':'版本号'],多个依赖用逗号分隔
示例
// ==Npplication==
// @associations [`https://example.com/plugin.js`] // 单个关联项,使用 Latest 版本
// @associations [`https://example.com/plugin.js`:`1.0.0`] // 单个关联项,指定版本
// @associations [`https://example.com/plugin1.js`:`1.2.0`, `https://example.com/plugin2.js`] // 多个关联项,使用逗号分隔
// ==/Npplication==注意事项
- 谨慎指定关联版本,确保兼容性
- 考虑关联项的加载顺序 (可能需要通知用户手动更改加载顺序)
2.11 @translates - 关联翻译
重要!
- 文档此部分描述仅适用
@type为非translate的插件 - 若设置的为
translate,请点击此处查看适用于translate的格式要求
说明
@translates 用于声明插件与翻译的关联关系。
格式
- 类型: 字符串
- 格式:
['插件 URL':'版本号'],多个翻译用逗号分隔
示例
// ==Npplication==
// @translates [`https://example.com/plugin.js`] // 单个翻译项,使用 Latest 版本
// @translates [`https://example.com/plugin.js`:`1.0.0`] // 单个翻译项,指定版本
// @translates [`https://example.com/plugin1.js`:`1.2.0`, `https://example.com/plugin2.js`] // 多个翻译项,使用逗号分隔
// ==/Npplication==注意事项
- 谨慎指定翻译版本,建议使用
Latest - 考虑翻译项的加载顺序 (可能需要通知用户手动更改加载顺序)
3. Metadata 验证和处理机制
3.1 Metadata 处理流程
必需字段验证
// 验证必需字段
async function extractMetadata(url) {
try {
// 提取元数据块
const urlMetadata = scriptText.match(
/\/\/\s*==Npplication==\s*\n([\s\S]*?)\n\/\/\s*==\/Npplication==/
);
if (!urlMetadata || !urlMetadata[1]) {
console.error('未找到元数据');
return;
}
// 解析元数据:逐行读 `// @key value`
const metadataLines = urlMetadata[1].split('\n');
const metadata = {};
for (const line of metadataLines) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith('// @')) {
const [key, ...valueParts] = trimmedLine.replace('// @', '').trim().split(' ');
const value = valueParts.join(' ').trim();
if (key && value) {
metadata[key] = value;
}
}
}
// 必填:名字 / id / 版本
if (!metadata.name || !metadata.id || !metadata.version) {
console.error('缺少必要元数据字段');
return;
}
// 翻译插件必须带 translates
if (metadata.type === 'translate' && !metadata.translates) {
console.error('翻译插件缺少必要的 translates 字段');
return;
}
// 非翻译插件必须带 time
if (metadata.type !== 'translate' && !metadata.time) {
console.error('缺少必要的 time 字段');
return;
}
}
}格式验证
// 验证 id 格式
if (metadata.type !== 'coreNpp' && metadata.type !== 'translate') {
// UUID v4 格式
const idPattern = /^([0-9]{13})_[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
const match = metadata.id.match(idPattern);
if (!match) {
console.error('错误的 ID 格式');
return;
}
// 验证 id 有效性
const timestamp = parseInt(match[1]);
if (timestamp < 1749401460000) {
console.error('ID 无效');
return;
}
}
// 验证 加载时机(翻译插件强制为 body)
if (metadata.type !== 'translate') {
if (!metadata.time || !['head', 'body'].includes(metadata.time.toLowerCase())) {
metadata.time = 'body'; // 默认
}
}其它部分
const isTranslatePlugin = metadata.type === 'translate';
return {
name: metadata.name,
id: metadata.id,
version: metadata.version,
updateUrl: url,
description: metadata.description || '@npplication:no-description',
author: metadata.author || '@npplication:no-author',
type: metadata.type || '',
time: isTranslatePlugin ? 'body' : metadata.time.toLowerCase(),
icon: metadata.icon || 'https://nitai-images.pages.dev/nitaiPage/defeatNpp.svg',
screen: metadata.screen || '',
forceUpdate: metadata.forced || 'false',
setting: metadata.setting || 'false',
dependencies: isTranslatePlugin ? '' : (metadata.dependencies || ''),
associations: isTranslatePlugin ? '' : (metadata.associations || ''),
translates: metadata.translates || ''
};3.2 Metadata 存储和管理
存储结构
解析结果写进 nitaiPageDB 的 nitaiPage ,key 为 npp_plugins,value 是插件列表的数组:
{
id: 'npp_plugins',
data: [
{
author: "Nitai",
dependencies: "",
description: "主题扩展插件",
forceUpdate: "false",
icon: "https://nitai-images.pages.dev/nitaiPage/themeColor.svg",
id: "themeColor",
ignoreUpdatePrompt: false,
installTime: 1754415490324,
name: "主题色",
screen: "[`https://nitai-images.pages.dev/nitaiPage/store/themeColor_screen.webp`]",
setting: "true",
time: "head",
type: "coreNpp",
updateUrl: "https://nppdb.nitai.cc/themeColor.js",
version: "0.2.1"
}
]
}列表里只存元数据,插件文件内容存在 nppstore 中
读写
// 保存 / 更新单个插件元数据,保留安装时间戳
export async function savePluginMetadata(metadata) {
const plugins = await getPluginsList();
const existingIndex = plugins.findIndex(p => p.id === metadata.id);
const existing = existingIndex > -1 ? plugins[existingIndex] : null;
const installTime = existing?.installTime || Date.now();
if (existing) {
plugins[existingIndex] = { ...metadata, installTime, ignoreUpdatePrompt: false };
} else {
plugins.push({ ...metadata, installTime, ignoreUpdatePrompt: false });
}
return savePluginsList(plugins);
}
// 读取插件列表(首次会从 localStorage 的存量数据迁移)
export async function getPluginsList() {
const record = await dbGet('nitaiPageDB', 'nitaiPage', 'npp_plugins');
if (record && Array.isArray(record.data)) return record.data;
const legacy = JSON.parse(localStorage.getItem('npp_plugins') || '[]');
if (legacy.length) dbPut('nitaiPageDB', 'nitaiPage', { id: 'npp_plugins', data: legacy });
return legacy;
}