Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# 更新日志

## [3.0.1] - 2026-09-29

### 修复

- 补充简体中文语言声明,使 Edge 商店正确识别扩展语言并提供中文介绍页。
- 打包流程纳入语言文件,检查缺失的语言消息引用。

## [3.0.0] - 2026-09-29

### 新增
Expand Down Expand Up @@ -29,3 +36,4 @@
更早版本尚未整理为独立发行记录,历史改动见 [Git 提交记录](https://github.com/aredddd/GlassNote/commits/main/)。

[3.0.0]: https://github.com/aredddd/GlassNote/releases/tag/v3.0.0
[3.0.1]: https://github.com/aredddd/GlassNote/releases/tag/v3.0.1
2 changes: 1 addition & 1 deletion PRIVACY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GlassNote 隐私说明

更新日期:2026 年 9 月 29 日。适用于 GlassNote 3.0.0。
更新日期:2026 年 9 月 29 日。适用于 GlassNote 3.0.1。

GlassNote 是网页标注与笔记扩展。笔记保存在你使用的浏览器中;扩展没有账号系统、开发者服务器、广告或使用分析服务,不向开发者或第三方上传网页内容和笔记。

Expand Down
8 changes: 8 additions & 0 deletions _locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extensionName": {
"message": "GlassNote"
},
"extensionDescription": {
"message": "轻盈的网页高亮与笔记工具。自动找回原文位置,本地保存,随时整理和导出。"
}
}
4 changes: 2 additions & 2 deletions docs/EDGE_STORE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Edge 商店发布材料

对应版本:3.0.0。此文件是提交材料,不代表已经提交审核或已经上架。
对应版本:3.0.1。此文件是提交材料,不代表已经提交审核或已经上架。

## 商店字段

Expand Down Expand Up @@ -57,7 +57,7 @@ GlassNote 用于在网页中标注文字、记录笔记,并在以后打开同

## 资源规格与官方入口

安装包位于 dist/GlassNote-v3.0.0.zip;商店图标可用 assets/icon-128.png。截图使用扩展实际运行界面,尺寸为 1280 × 800,不放入用户的私人笔记或第三方宣传素材。
安装包位于 dist/GlassNote-v3.0.1.zip;商店图标可用 assets/icon-128.png。截图使用扩展实际运行界面,尺寸为 1280 × 800,不放入用户的私人笔记或第三方宣传素材。

- [Edge 开发者注册](https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/create-dev-account)
- [Edge 扩展发布说明](https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/publish-extension)
Expand Down
7 changes: 4 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"manifest_version": 3,
"name": "GlassNote",
"version": "3.0.0",
"name": "__MSG_extensionName__",
"version": "3.0.1",
"default_locale": "zh_CN",
"minimum_chrome_version": "105",
"description": "轻盈的网页高亮与笔记工具。自动找回原文位置,本地保存,随时整理和导出。",
"description": "__MSG_extensionDescription__",
"permissions": ["activeTab", "storage", "scripting"],
"background": { "service_worker": "src/background/background.js" },
"options_page": "src/library/library.html",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "glassnote",
"version": "3.0.0",
"version": "3.0.1",
"private": true,
"description": "轻盈、可靠的本地网页笔记工具",
"scripts": {
"test": "node --test tests/*.test.cjs",
"test:e2e": "node --test tests/extension.e2e.cjs",
"test:live": "node tests/live-site.cjs",
"check": "node scripts/check.cjs",
"format": "prettier --write src styles scripts tests .github docs manifest.json package.json package-lock.json \"*.md\"",
"format:check": "prettier --check src styles scripts tests .github docs manifest.json package.json package-lock.json \"*.md\"",
"format": "prettier --write src styles _locales scripts tests .github docs manifest.json package.json package-lock.json \"*.md\"",
"format:check": "prettier --check src styles _locales scripts tests .github docs manifest.json package.json package-lock.json \"*.md\"",
"package": "node scripts/package.cjs",
"package:verify": "node scripts/verify-package.cjs"
},
Expand Down
26 changes: 25 additions & 1 deletion scripts/check.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ function walk(directory) {
}
walk(path.join(root, 'src'));
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));
const localeReferences = [...JSON.stringify(manifest).matchAll(/__MSG_([A-Za-z0-9_]+)__/g)].map(
(match) => match[1],
);
const localeRoot = path.join(root, '_locales');
if ((localeReferences.length || fs.existsSync(localeRoot)) && !manifest.default_locale)
throw new Error('使用语言包时必须声明 default_locale');
if (manifest.default_locale) {
if (!/^[A-Za-z0-9_]+$/.test(manifest.default_locale))
throw new Error('default_locale 必须是合法的语言目录名');
const messagesPath = path.join(localeRoot, manifest.default_locale, 'messages.json');
if (!fs.existsSync(messagesPath)) throw new Error('缺少默认语言包:' + messagesPath);
const messages = JSON.parse(fs.readFileSync(messagesPath, 'utf8'));
if (!messages || typeof messages !== 'object' || Array.isArray(messages))
throw new Error('语言包必须是消息名称到消息内容的对象');
const normalizedMessages = new Map();
for (const [key, value] of Object.entries(messages)) {
if (!value || typeof value.message !== 'string' || !value.message.trim())
throw new Error('语言包消息内容不能为空:' + key);
normalizedMessages.set(key.toLowerCase(), value.message);
}
for (const key of localeReferences)
if (!normalizedMessages.has(key.toLowerCase()))
throw new Error('默认语言包缺少 manifest 引用的消息:' + key);
}
const files = [
manifest.background.service_worker,
manifest.action.default_popup,
Expand All @@ -31,4 +55,4 @@ for (const [size, file] of Object.entries(manifest.icons)) {
)
throw new Error('扩展图标必须是正确尺寸的 PNG:' + file);
}
console.log('JavaScript 语法和扩展入口检查通过。');
console.log('JavaScript 语法、扩展入口和语言包检查通过。');
1 change: 1 addition & 0 deletions scripts/package-lib.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const REQUIRED_FILES = [
'SECURITY.md',
];
const TREES = {
_locales: /\.json$/,
src: /\.(js|html|css)$/,
styles: /\.css$/,
assets: /\.(png|svg|ico|webp)$/,
Expand Down
14 changes: 14 additions & 0 deletions tests/extension.e2e.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ after(async () => {
fs.rmSync(profile, { recursive: true, force: true });
});

test('真实扩展:默认简体中文语言资源可解析', async () => {
const localized = await worker.evaluate(() => ({
name: chrome.runtime.getManifest().name,
description: chrome.runtime.getManifest().description,
message: chrome.i18n.getMessage('extensionDescription'),
}));
assert.equal(localized.name, 'GlassNote');
assert.equal(
localized.description,
'轻盈的网页高亮与笔记工具。自动找回原文位置,本地保存,随时整理和导出。',
);
assert.equal(localized.message, localized.description);
});

test('真实扩展:跨节点标注不破坏链接,隐藏保留原文,刷新和关页重开恢复', async () => {
await openArticle();
const beforeHTML = await page.locator('#linked').innerHTML();
Expand Down
56 changes: 54 additions & 2 deletions tests/package.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const { createHash } = require('node:crypto');
const { spawnSync } = require('node:child_process');
const {
packageExtension,
releaseVersion,
Expand All @@ -18,7 +19,23 @@ function fixture(t) {
fs.mkdirSync(path.dirname(path.join(root, name)), { recursive: true });
fs.writeFileSync(path.join(root, name), value);
};
write('manifest.json', JSON.stringify({ manifest_version: 3, version: '3.2.1' }));
write(
'manifest.json',
JSON.stringify({
manifest_version: 3,
version: '3.2.1',
name: '__MSG_extensionName__',
description: '__MSG_extensionDescription__',
default_locale: 'zh_CN',
}),
);
write(
'_locales/zh_CN/messages.json',
JSON.stringify({
extensionName: { message: 'GlassNote' },
extensionDescription: { message: '轻盈的网页高亮与笔记工具。' },
}),
);
write('package.json', JSON.stringify({ version: '3.2.1' }));
write(
'package-lock.json',
Expand Down Expand Up @@ -56,17 +73,23 @@ test('发布包按版本命名,附许可证和隐私声明,排除依赖、
assert.equal(path.basename(result.archive), 'GlassNote-v3.2.1.zip');
const archive = fs.readFileSync(result.archive);
const entries = readArchive(archive);
assert.equal(entries.size, 11);
assert.equal(entries.size, 12);
for (const name of [
'LICENSE',
'PRIVACY.md',
'THIRD_PARTY_NOTICES.md',
'CONTRIBUTING.md',
'SECURITY.md',
'_locales/zh_CN/messages.json',
'docs/安装说明.md',
])
assert.ok(entries.has(name), name);
assert.equal(entries.get('docs/安装说明.md').toString(), '中文文档应当正确保存在 ZIP 中。\n');
const manifest = JSON.parse(entries.get('manifest.json').toString());
const messages = JSON.parse(entries.get('_locales/zh_CN/messages.json').toString());
assert.equal(manifest.default_locale, 'zh_CN');
assert.equal(messages.extensionName.message, 'GlassNote');
assert.equal(messages.extensionDescription.message, '轻盈的网页高亮与笔记工具。');
assert.ok(
[...entries.keys()].every((name) => !/node_modules|work\/|tests\/|\.env|debug/.test(name)),
);
Expand Down Expand Up @@ -125,3 +148,32 @@ test('发布白名单内的符号链接不能将外部文件带入安装包', (t
fs.symlinkSync(path.join(root, 'LICENSE'), path.join(root, 'src/linked.js'));
assert.throws(() => packageExtension(root, output), /符号链接/);
});

test('扩展检查拒绝缺失默认语言声明、语言包或 manifest 引用的消息', (t) => {
const { root, write } = fixture(t);
write('scripts/check.cjs', fs.readFileSync(path.join(__dirname, '../scripts/check.cjs')));
const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8'));
Object.assign(manifest, {
background: { service_worker: 'src/background/background.js' },
action: { default_popup: 'src/background/background.js' },
options_page: 'src/background/background.js',
icons: {},
content_scripts: [],
});
const saveManifest = () => write('manifest.json', JSON.stringify(manifest));
const check = () =>
spawnSync(process.execPath, [path.join(root, 'scripts/check.cjs')], { encoding: 'utf8' });
saveManifest();
assert.equal(check().status, 0);
delete manifest.default_locale;
saveManifest();
assert.match(check().stderr, /必须声明 default_locale/);
manifest.default_locale = 'zh_CN';
manifest.name = '__MSG_missingName__';
saveManifest();
assert.match(check().stderr, /缺少 manifest 引用的消息:missingName/);
write('_locales/zh_CN/messages.json', JSON.stringify({ extensionName: { message: '' } }));
assert.match(check().stderr, /消息内容不能为空/);
fs.rmSync(path.join(root, '_locales/zh_CN/messages.json'));
assert.match(check().stderr, /缺少默认语言包/);
});