Skip to content

NPP 元数据 ​

本文档详细介绍 NPP 插件系统中每个 MetaData 项的说明、格式以及需要注意的事项,以帮助开发者能够正确配置插件。

1. 必填元数据 ​

1.1 @id - 插件唯一标识符 ​

说明 ​

@id 是插件的唯一标识符,用于系统内部识别和管理插件,是插件存储、更新和卸载的关键标识。

格式 ​

  • 类型: 字符串
  • 格式: 13位时间戳 + UUIDv4 nitaiPage 通过以下两步完成验证:
javascript
// 时间戳
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 (纯数字)

示例 ​

javascript
// ==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 (默认)

示例 ​

javascript
// ==Npplication==
// @time head
// @time body
// ==/Npplication==

说明 ​

head: 在页面 <head>部分加载,适用于需要尽早执行的插件 body: 在页面 <body>部分加载,适用于大多数插件

注意事项 ​

  • 插件应尽量使用 body 加载
  • head 适用于需要修改页面初始状态的插件
  • 需要考虑插件对页面加载性能的影响,选择错误的加载时机可能导致功能异常

1.4 @translates - 语言代码 ​

重要! ​

  • 仅针对@type为translate的插件必填,对于非translate插件为可选元数据
  • 文档此部分描述仅适用@type为translate的插件
  • 若您要为插件关联翻译项请点击此处查看适用于非translate的格式要求

说明 ​

@translates 用于声明此项翻译面向的语言。

格式 ​

  • 类型: 字符串
  • 格式: 单个语言代码

示例 ​

javascript
// ==Npplication==
// @translates zh-CN
// @translates en-US
// ==/Npplication==

注意事项 ​

  • 仅支持识别单个语言代码

2. 可选元数据 ​

2.1 @description - 插件描述 ​

说明 ​

@description 用于提供插件的详细描述信息,帮助用户了解插件的特点。

格式 ​

  • 类型: 字符串
  • 长度: 无限制,建议10-500个字符

示例 ​

javascript
// ==Npplication==
// @description 一个简单实用的计算器插件
// @description Weather Widget - 显示实时天气信息的小工具
// ==/Npplication==

注意事项 ​

  • 描述应简洁明了,突出插件核心功能
  • 虽不必要,但强烈建议填写

2.2 @author - 插件作者 ​

说明 ​

@author 用于标识插件的作者。

格式 ​

  • 类型: 字符串
  • 长度: 无限制,建议5-20个字符

示例 ​

javascript
// ==Npplication==
// @author 张三
// @author John Doe
// @author NitaiDev Team
// @author example@email.com
// ==/Npplication==

注意事项 ​

  • 支持包含邮箱地址等联系方式
  • 不建议使用他人的作者标识
  • 建议使用一致的作者标识
  • 虽不必要,但强烈建议填写

2.3 @name - 名称 ​

说明 ​

@name 用于定义插件的显示名称,这个名称将展示给用户。

格式 ​

  • 类型: 字符串
  • 长度: 建议不要超出 16 字符
  • 字符集: 支持中文、英文、数字和符号

示例 ​

javascript
// ==Npplication==
// @name 我的插件
// @name My_Npp
// ==/Npplication==

注意事项 ​

  • 名称应简短
  • 尽量避免使用特殊字符和表情符号
  • 不建议与其他插件名称重复
  • 虽不必要,但强烈建议填写

2.4 @icon - 插件图标 ​

说明 ​

@icon 用于指定插件的图标URL,用于在用户界面中显示插件图标。

格式 ​

  • 类型: 字符串 (URL)
  • 协议: http 或 https
  • 格式: 图片 URL 或 Base64

示例 ​

javascript
// ==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':'版本号'],多个依赖用逗号分隔

示例 ​

javascript
// ==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

示例 ​

javascript
// ==Npplication==
// @type nomal
// @type coreNpp
// @type translate
// ==/Npplication==

2.7 @setting - 是否注册设置界面 ​

说明 ​

@setting 用于告诉 nitaiPage 插件是否需要设置界面。

格式要求 ​

  • 类型: 布尔值
  • 可选: true 或 false (默认)

示例 ​

javascript
// ==Npplication==
// @setting true
// @setting false
// ==/Npplication==

具体设置及调用方法,请参考NPP 设置页面内容添加指南

使用的组件可参考插件设置项组件

2.8 @screen - 屏幕截图 ​

说明 ​

@screen 用于向用户提供插件预览。

格式要求 ​

  • 类型: 字符串

示例 ​

javascript
// ==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 (默认)

示例 ​

javascript
// ==Npplication==
// @forced true
// @forced false
// ==/Npplication==

2.10 @associations - 关联插件 ​

说明 ​

@associations 用于声明插件与其他插件的关联关系。

格式 ​

  • 类型: 字符串
  • 格式: ['插件 URL':'版本号'],多个依赖用逗号分隔

示例 ​

javascript
// ==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':'版本号'],多个翻译用逗号分隔

示例 ​

javascript
// ==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 处理流程 ​

必需字段验证 ​

javascript
// 验证必需字段
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;
        }
    }
}

格式验证 ​

javascript
// 验证 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'; // 默认
    }
}

其它部分 ​

javascript
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 是插件列表的数组:

javascript
{
    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 中

读写 ​

javascript
// 保存 / 更新单个插件元数据,保留安装时间戳
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;
}

Released under the Apache-2.0 License.