diff --git a/AGENTS.md b/AGENTS.md index 3e7f1ea..7e9610f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ This file is mandatory for AI and coding agents working in this repository. Read ## Product identity and non-goals -- Web Tools is a self-hosted Vue 3 + TypeScript browser-local tools application with a zero-dependency Node.js static/configuration server. +- Web Tools is an anonymous-access, self-hosted Vue 3 + TypeScript browser-local tools application with a zero-dependency Node.js static/configuration server. - The default production port is `9000`. Port `8081` is only the local Vite development proxy target. - The application is anonymous. Do not add login, accounts, favorites, sponsorship, analytics, telemetry, advertising, CDN assets, or a database. - Tool inputs, keys, files, images, and results must remain inside the current browser tab. Do not send them to the Node server, a third-party API, or any remote service. @@ -64,6 +64,7 @@ This file is mandatory for AI and coding agents working in this repository. Read - Do not add a visible link to `/setting`, explanatory marketing copy, login UI, favorites, or sponsorship UI. - Preserve the application icon contract under `public/`: `favicon.svg`, a 16/32/48-frame `favicon.ico`, `apple-touch-icon.png` at 180x180, `icon-192.png`, `icon-512.png`, `icon-maskable-512.png`, and `site.webmanifest`. Keep the icon visually aligned with the sidebar brand and keep the maskable artwork inside its safe zone. - The manifest is metadata/install-ready only. Do not add a Service Worker, offline cache, install prompt, splash-screen bundle, Safari mask icon, legacy `browserconfig.xml`, redundant Apple sizes, or separate 16/32 PNG favicons unless requirements explicitly change. +- Preserve the share metadata contract: the single user-facing description is exactly `一个匿名访问、自托管的浏览器本地工具箱`, represented by the standard HTML description and the identical `og:description` compatibility field. Do not add a custom share image, other Open Graph or platform-specific metadata, platform crawlers, route-specific previews, tool lists, or user data. Sharing clients may fall back to the document title and existing favicon assets, but their final preview and cache behavior remain platform-controlled. - For UI work, verify a wide desktop, an intermediate width around 801-1040 px, and mobile portrait around 390 x 844 in a real browser. Check direct routes, refresh, navigation, search, `/setting`, both themes, console errors, and network requests. ## Adding a tool diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f9f88e..f0c2fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ 本文档记录面向使用者和维护者的重要变更。版本号以 `package.json` 为准。 +## 0.1.0 - 2026-08-09 + +### Added + +- 增加统一页面介绍,不列出具体工具或生成路由专属内容。 + +### Documentation + +- 明确标准 HTML description 和同文的 `og:description` 分享兼容性契约,不设置自定义分享图、其他 Open Graph、平台抓取器、平台专用字段或包含用户数据的动态内容。 + +### Verification + +- 87 条 Vitest 测试通过,页面元数据测试覆盖标准 description、唯一允许的 `og:description`、Manifest 描述、favicon 回退入口和无自定义分享图约束,并增加发布版本跨文件一致性检查。 +- Vue/TypeScript 类型检查和生产构建通过。 + ## 0.0.6 - 2026-08-08 ### Added diff --git a/README.md b/README.md index 8b4a616..edbb283 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [简体中文](README.zh-CN.md) -Web Tools is a self-hosted collection of browser-local utilities built with Vue 3, TypeScript, Vite, and a small zero-dependency Node.js server. It supports multiple simultaneous users: text, keys, files, images, and calculation results remain inside each browser tab. The server only persists the navigation order shared by all visitors. +Web Tools is an anonymously accessible, self-hosted collection of browser-local utilities built with Vue 3, TypeScript, Vite, and a small zero-dependency Node.js server. It supports multiple simultaneous users: text, keys, files, images, and calculation results remain inside each browser tab. The server only persists the navigation order shared by all visitors. The authoritative application version is the `version` field in `package.json`. The default production address is `http://localhost:9000`. @@ -112,13 +112,15 @@ server/ static server and navigation configuration API shared/tool-catalog.json authoritative tool metadata shared/default-navigation.json first-run navigation order tests/ unit and server integration tests -public/ favicon, Apple touch icon, and Web App Manifest assets +public/ browser icons and Web App Manifest ``` New tools must keep their computation independent, register the same stable ID in the catalog, default navigation, and lazy-load registry, reuse the shared UI, define resource limits where appropriate, and include normal, invalid, boundary, and known-vector tests. Routes are generated from the tool registry; ordinary tools do not add routes manually. The browser icon set follows the sidebar brand and includes SVG, multi-frame ICO, Apple touch, 192/512, and maskable assets. The manifest supplies metadata only: the project deliberately has no Service Worker, offline cache, install prompt, or splash-screen bundle, so a deployment update cannot be hidden by application-managed stale caches. +The page exposes one product description through both the standard HTML description and an identical `og:description` compatibility field. It has no custom share image, other Open Graph metadata, platform crawler, or platform-specific fields. Sharing clients may fall back to the document title and existing favicons, but each platform controls its final preview and cache refresh behavior. + For the complete extension procedure and AI handoff rules, read [AGENTS.md](AGENTS.md) and [README.zh-CN.md](README.zh-CN.md) before editing. Inspect the Git worktree first and preserve existing changes. ## Documentation diff --git a/README.zh-CN.md b/README.zh-CN.md index 34a7a4e..37ba4fb 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,7 +4,7 @@ 一个匿名访问、自托管的浏览器本地工具箱。支持多人同时使用;除全局导航顺序外,文本、密钥、文件、图片和计算结果均只存在于各自浏览器中,不会发送到服务端。 -当前应用版本:`0.0.6`。 +当前应用版本:`0.1.0`。 ## 内置工具 @@ -35,7 +35,7 @@ shared/tool-catalog.json 工具与分类的权威元数据目录 shared/default-navigation.json 新部署的默认分类和工具顺序 tests/ Vitest 单元测试 dist/ npm run build 生成的生产前端 -public/ 浏览器标签页、Apple 主屏幕和 Web App Manifest 图标资源 +public/ 浏览器图标和 Web App Manifest Dockerfile 多阶段容器构建 docker-compose.yml 默认部署配置 THIRD_PARTY_NOTICES.md 第三方依赖许可说明 @@ -59,6 +59,8 @@ CHANGELOG.md 版本变更、修复和验证记录 `site.webmanifest` 只提供应用名称、颜色和图标元数据,便于浏览器添加到主屏幕。项目不注册 Service Worker,不启用离线缓存、安装提示或启动画面,避免部署更新后继续加载过期构建。 +页面只提供统一介绍“一个匿名访问、自托管的浏览器本地工具箱”,同时写入标准 HTML description 和同文的 `og:description`,以兼容不同分享客户端;不设置自定义分享图片、其他 Open Graph 或平台专用字段,也不增加平台抓取器。分享平台可以回退使用页面标题和现有 favicon,但最终展示及缓存刷新时机由平台决定。 + ## 核心功能契约 以下行为属于已经发布的兼容性约束。修改时必须同步实现、页面提示、自动化测试和本文档,不能只调整界面文案。 @@ -243,10 +245,10 @@ docker compose restart web-tools ## 离线镜像 -若交付目录包含 `personal-web-tools-0.0.6-amd64.tar`,可直接导入: +若交付目录包含 `personal-web-tools-0.1.0-amd64.tar`,可直接导入: ```bash -docker load -i personal-web-tools-0.0.6-amd64.tar +docker load -i personal-web-tools-0.1.0-amd64.tar docker compose up -d --no-build ``` diff --git a/docker-compose.yml b/docker-compose.yml index 4417e5b..6f4acfe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: web-tools: - image: personal-web-tools:0.0.6 + image: personal-web-tools:0.1.0 build: context: . dockerfile: Dockerfile diff --git a/index.html b/index.html index 0a67709..ddb854b 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,8 @@ - + + diff --git a/package-lock.json b/package-lock.json index 35a74db..27e330b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "personal-web-tools", - "version": "0.0.6", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "personal-web-tools", - "version": "0.0.6", + "version": "0.1.0", "dependencies": { "@lucide/vue": "^1.29.0", "@stablelib/aes": "^2.0.1", diff --git a/package.json b/package.json index 3d26fae..952ecc8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "personal-web-tools", - "version": "0.0.6", + "version": "0.1.0", "private": true, "type": "module", "engines": { diff --git a/public/site.webmanifest b/public/site.webmanifest index 65eb3f0..4ac4d36 100644 --- a/public/site.webmanifest +++ b/public/site.webmanifest @@ -1,7 +1,7 @@ { "name": "Web Tools", "short_name": "Web Tools", - "description": "浏览器本地运行的个人 Web 工具箱", + "description": "一个匿名访问、自托管的浏览器本地工具箱", "start_url": "/", "scope": "/", "display": "standalone", diff --git a/tests/appIcons.test.mjs b/tests/appIcons.test.mjs index 9b42d57..88f2670 100644 --- a/tests/appIcons.test.mjs +++ b/tests/appIcons.test.mjs @@ -67,7 +67,7 @@ function decodeRgbaPng(buffer) { return { width, height, pixels } } -describe('application icon assets', () => { +describe('application branding assets', () => { it('keeps the vector favicon self-contained', async () => { const svg = await readFile(path.join(publicRoot, 'favicon.svg'), 'utf8') @@ -128,6 +128,7 @@ describe('application icon assets', () => { expect(manifest).toMatchObject({ name: 'Web Tools', short_name: 'Web Tools', + description: '一个匿名访问、自托管的浏览器本地工具箱', start_url: '/', scope: '/', display: 'standalone', @@ -151,6 +152,19 @@ describe('application icon assets', () => { expect(html).toContain('') }) + it('declares one product description for standard and Open Graph consumers without a custom image', async () => { + const html = (await readProjectFile('index.html')).toString('utf8') + const socialMetadata = [...html.matchAll(/ match[1].toLowerCase()) + + expect(html).toContain('') + expect(html).toContain('') + expect(html.match(/一个匿名访问、自托管的浏览器本地工具箱/g)).toHaveLength(2) + expect(socialMetadata).toEqual(['og:description']) + expect(html).not.toContain('share-preview.png') + await expect(access(path.join(publicRoot, 'share-preview.png'))).rejects.toMatchObject({ code: 'ENOENT' }) + }) + it('includes public assets in container builds', async () => { expect((await readProjectFile('Dockerfile')).toString('utf8')).toContain('COPY public ./public') }) diff --git a/tests/deploymentConfig.test.mjs b/tests/deploymentConfig.test.mjs index 933611c..264bcde 100644 --- a/tests/deploymentConfig.test.mjs +++ b/tests/deploymentConfig.test.mjs @@ -11,6 +11,27 @@ async function readProjectFile(relativePath) { } describe('deployment configuration', () => { + it('keeps the release version synchronized across project and deployment metadata', async () => { + const [packageText, lockText, compose, readme, changelog] = await Promise.all([ + readProjectFile('package.json'), + readProjectFile('package-lock.json'), + readProjectFile('docker-compose.yml'), + readProjectFile('README.zh-CN.md'), + readProjectFile('CHANGELOG.md'), + ]) + const packageJson = JSON.parse(packageText) + const packageLock = JSON.parse(lockText) + const version = packageJson.version + + expect(version).toMatch(/^\d+\.\d+\.\d+$/) + expect(packageLock.version).toBe(version) + expect(packageLock.packages[''].version).toBe(version) + expect(compose.match(/^\s*image:\s*personal-web-tools:(\S+)$/m)?.[1]).toBe(version) + expect(readme).toContain(`当前应用版本:\`${version}\`。`) + expect(readme).toContain(`personal-web-tools-${version}-amd64.tar`) + expect(changelog.match(/^## (\d+\.\d+\.\d+) - /m)?.[1]).toBe(version) + }) + it('keeps the default production port synchronized', async () => { const [dockerfile, compose, environment, script, readme] = await Promise.all([ readProjectFile('Dockerfile'),