Compare commits

...
2 Commits
Author SHA1 Message Date
mid aca004c513 补强分享元数据与版本一致性检查 2026-08-09 15:41:13 +08:00
mid 5638c56558 发布 0.1.0 页面介绍与分享兼容更新 2026-08-09 15:23:01 +08:00
11 changed files with 70 additions and 14 deletions
+2 -1
View File
@@ -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
+15
View File
@@ -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
+4 -2
View File
@@ -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
+6 -4
View File
@@ -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
```
+1 -1
View File
@@ -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
+2 -1
View File
@@ -4,7 +4,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#f5f6f7" />
<meta name="description" content="浏览器本地运行的个人 Web 工具箱" />
<meta name="description" content="一个匿名访问、自托管的浏览器本地工具箱" />
<meta property="og:description" content="一个匿名访问、自托管的浏览器本地工具箱" />
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32 48x48" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" sizes="any" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "personal-web-tools",
"version": "0.0.6",
"version": "0.1.0",
"private": true,
"type": "module",
"engines": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Web Tools",
"short_name": "Web Tools",
"description": "浏览器本地运行的个人 Web 工具箱",
"description": "一个匿名访问、自托管的浏览器本地工具箱",
"start_url": "/",
"scope": "/",
"display": "standalone",
+15 -1
View File
@@ -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('<link rel="manifest" href="/site.webmanifest" />')
})
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(/<meta\s+(?:name|property)="((?:og|twitter):[^"]+)"/gi)]
.map((match) => match[1].toLowerCase())
expect(html).toContain('<meta name="description" content="一个匿名访问、自托管的浏览器本地工具箱" />')
expect(html).toContain('<meta property="og:description" content="一个匿名访问、自托管的浏览器本地工具箱" />')
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')
})
+21
View File
@@ -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'),