# WIDECK V5 卡片包制作教程与设计规范

更新时间：2026-08-10  
适用版本：仅 WIDECK V5；V1～V4 包不再读取、恢复或导入

这份文档同时写给开发者、测试人员和生成代码的 AI。目标是让读者知道：一个 `.wideck` 包里可以放什么、怎样设计小组件与全屏界面、如何适配浅色与深色主题，以及怎样打包和验证。

如果你让 AI 制作卡片，建议把本文完整提供给它，不要只截取 `manifest.json`。

## 1. 先理解 WIDECK

一个 WIDECK 包是一个可离线运行的小型网页应用，使用 HTML、CSS、JavaScript 和包内媒体资源制作。它有两种显示状态：

- `widget`：桌面网格里的小组件，适合快速查看和少量操作。
- `fullscreen`：点击宿主提供的展开按钮后进入完整视窗，适合游戏、图表、编辑器和详细内容。

宿主网站负责卡片外壳、圆角、边框、阴影、拖动、删除、展开、退出全屏和网格排列。包只负责卡片内部的内容，不要再造一层外壳。

### 一种结构，两种信任级别

官方包和第三方包使用相同的 V5 目录、清单、完整性文件、尺寸和设计规则，区别只在权限：

- 第三方包保存在当前浏览器中，在隔离沙箱内运行，`permissions` 必须为 `[]`。
- 官方包随网站发布，并由官方目录中的固定路径、SHA-256 和 `grants` 共同授权。
- 发布者签名只能证明包的来源，没有官方权限，也不会解除第三方沙箱限制。
- AI 可以不使用密钥直接生成和打包普通第三方卡片。

## 2. 包内可以有什么

### 2.1 固定目录

源目录必须至少包含：

```text
my-card/
├── manifest.json
├── app/
│   ├── index.html
│   ├── style.css
│   └── main.js
└── assets/
    └── preview.svg
```

可以在 `assets/` 中继续加入资源并使用子目录：

```text
assets/
├── preview.svg              # 固定预览入口，必须存在
├── images/cover.webp        # 可选图片
├── audio/background.mp3     # 可选音频
└── video/intro.mp4          # 可选视频
```

当前可识别的媒体类型：

- 图片：SVG、PNG、JPEG、WebP、GIF
- 音频：MP3、WAV、OGG、M4A、WebM
- 视频：MP4

打包工具会另外生成：

```text
integrity.json               # 必须，由工具生成
signature.json               # 可选，仅签名包包含
```

不要手写 `integrity.json`、`signature.json`，也不要在打包完成后直接修改压缩包。

### 2.2 包内不能依赖什么

第三方包必须离线自足，不能依赖 CDN、远程字体、外部接口或运行时 npm 安装。它默认不能访问：

- 网络请求、宿主页面数据和 Cookie
- 摄像头、麦克风、定位和剪贴板
- 支付、USB、蓝牙、串口等设备能力
- 稳定的 `localStorage` 或 IndexedDB；刷新后的持久化状态不应作为第三方 V5 的前提

可以使用 Canvas 2D、CSS 动画、键盘、鼠标、触控、计时器、包内图片、音频和视频。游戏、可视化、计算器、计时器、离线阅读器等都可以实现。

## 3. manifest.json 清单

第三方包推荐从下面的清单开始：

```json
{
  "format": "wideck.package",
  "version": 5,
  "trust": "local",
  "id": "howie-focus-card",
  "release": "1.0.0",
  "title": "专注计时",
  "description": "一个可以全屏使用的本地专注计时器",
  "accent": "blue",
  "dimensions": {
    "rows": 2,
    "columns": 2
  },
  "permissions": [],
  "entry": "app/index.html",
  "style": "app/style.css",
  "script": "app/main.js",
  "preview": "assets/preview.svg",
  "ui": {
    "modes": ["widget", "fullscreen"],
    "theme": "adaptive",
    "hostControls": "top-right"
  }
}
```

| 字段 | 要求 |
|---|---|
| `format` | 固定为 `wideck.package` |
| `version` | 固定为数字 `5` |
| `trust` | 普通包为 `local`；签名工具会改为 `publisher` |
| `id` | 3～64 位小写字母、数字和连字符；升级时保持不变 |
| `release` | 语义化版本，例如 `1.0.0`、`1.1.0` |
| `title` | 最长 48 个字符 |
| `description` | 最长 76 个字符 |
| `accent` | `blue`、`mint`、`purple`、`peach`、`graphite` |
| `dimensions.rows` | 第三方桌面端 1～3；官方 1～2 |
| `dimensions.columns` | 第三方桌面端 1～6；官方 1～2 |
| `permissions` | 第三方必须严格为 `[]` |
| 四个入口字段 | 必须使用示例中的固定路径 |
| `ui` | 必须声明两种模式、主题适配和右上宿主控制区 |

同一个项目升级时保持 `id` 不变并递增 `release`。相同文件的重复导入会被拒绝；包导入成功后，可以在桌面添加多个实例，没有固定实例数量上限。

> 尺寸以“行 × 列”的对象表达。例如 `rows: 2, columns: 3` 表示 2 行、3 列。不要把行列写反。

## 4. 小组件和全屏模式

宿主会在运行文档的 `<html>` 上设置模式属性：

```text
小组件：html 上没有 data-wideck-fullscreen
全屏：  html[data-wideck-fullscreen]
```

包不需要监听宿主消息，直接用 CSS 属性选择器切换即可：

```html
<main class="app">
  <section class="widget-view">小组件内容</section>
  <section class="fullscreen-view">全屏内容</section>
</main>
```

```css
.widget-view { display: grid; }
.fullscreen-view { display: none; }

html[data-wideck-fullscreen] .widget-view { display: none; }
html[data-wideck-fullscreen] .fullscreen-view { display: grid; }
```

不要只把小组件等比例放大。推荐这样分工：

- 小组件只表达一个核心目的，保留最重要状态和 1～3 个相关操作。
- 全屏重新组织信息，可以增加详情、设置、历史、帮助和复杂操作。
- 小组件文字简短、可扫读；长文、长表单和大量控制放到全屏。
- 全屏普通文本默认允许选择；不要给整个应用设置永久的 `user-select: none`。

### 尺寸规则

- 所有卡片最小 1×1。
- 官方桌面卡片最大 2×2。
- 第三方桌面卡片最大 3 行×6 列。
- 手机端最终最多显示为 2×2，超出部分由宿主裁定尺寸，而不是按比例拉长。
- 桌面端与手机端分别记录卡片位置。

### 右上角宿主控制区

展开和删除按钮由宿主绘制。小组件右上角至少留出约 72×48px 的安静区域：

- 不放按钮、标题、分数、状态文字或关键图形。
- 不自行制作展开、关闭、删除或拖动手柄。
- 全屏退出按钮也由宿主提供，内容需避开设备安全区。

全屏根界面应铺满视窗，不要再绘制一张带大圆角、外边框和外阴影的“卡片”。可以用以下方式处理安全区：

```css
.fullscreen-view {
  min-height: 100svh;
  padding:
    max(72px, env(safe-area-inset-top))
    max(20px, env(safe-area-inset-right))
    max(24px, env(safe-area-inset-bottom))
    max(20px, env(safe-area-inset-left));
}
```

## 5. 浅色与深色主题：正确实现方式

这是 WIDECK 主题适配最重要的规则。

宿主会把当前网站主题写到沙箱文档的根元素：

```text
html[data-theme="light"]
html[data-theme="dark"]
```

用户点击网站的黑白模式按钮时，宿主会实时更新这个属性。因此：

- 必须优先使用 `html[data-theme]`，这样才能跟随网站的手动切换。
- `prefers-color-scheme` 只能作为宿主属性缺失时的后备，不能作为唯一方案。
- 不要在卡片内再放一个独立的黑白模式按钮。

### 5.1 推荐的 CSS 变量结构

```css
:root,
html[data-theme="light"] {
  color-scheme: light;
  --surface: #ffffff;
  --surface-soft: #f5f5f7;
  --text: #1d1d1f;
  --text-secondary: #6e6e73;
  --line: rgba(60, 60, 67, 0.12);
  --accent: #0071e3;
}

html[data-theme="dark"] {
  color-scheme: dark;
  --surface: #1c1c1e;
  --surface-soft: #2c2c2e;
  --text: #f5f5f7;
  --text-secondary: #a1a1a6;
  --line: rgba(235, 235, 245, 0.14);
  --accent: #2997ff;
}

@media (prefers-color-scheme: dark) {
  html:not([data-theme]) {
    color-scheme: dark;
    --surface: #1c1c1e;
    --surface-soft: #2c2c2e;
    --text: #f5f5f7;
    --text-secondary: #a1a1a6;
    --line: rgba(235, 235, 245, 0.14);
    --accent: #2997ff;
  }
}

.app {
  min-height: 100%;
  color: var(--text);
  background: var(--surface);
}

.secondary { color: var(--text-secondary); }
.panel { border: 1px solid var(--line); background: var(--surface-soft); }
```

尽量让组件都引用变量，不要在每个选择器里重复写两套颜色。切换主题时 CSS 会自动更新，不需要刷新或重新创建卡片。

### 5.2 Canvas 或 JavaScript 绘图

Canvas 不会自动读取 CSS 颜色。需要在主题属性变化时重新取色并重绘：

```js
const root = document.documentElement;

function currentTheme() {
  return root.dataset.theme === "dark" ? "dark" : "light";
}

function draw() {
  const dark = currentTheme() === "dark";
  const background = dark ? "#1c1c1e" : "#ffffff";
  const foreground = dark ? "#f5f5f7" : "#1d1d1f";
  // 使用 background 和 foreground 重新绘制 Canvas。
}

new MutationObserver(draw).observe(root, {
  attributes: true,
  attributeFilter: ["data-theme"]
});

draw();
```

### 5.3 主题验收标准

- 两种主题下正文、辅助文字、按钮和分隔线都清楚可读。
- 深色模式不是简单反相；图片、阴影和强调色仍应自然。
- 不只靠颜色表达成功、失败或选中状态，同时使用文字、图标或形状。
- 切换主题时不闪白、不重置游戏或计时器状态。
- 浏览器或网站主题切换后，卡片与全屏界面同时更新。

## 6. 视觉与交互规范

WIDECK 参考 Apple Widgets 和 Layout 的信息层级、适配与可用性原则，但不代表 Apple 官方认证。

- 使用系统字体：`-apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif`。
- 小组件内容边距通常约 14～18px；全出血图片或画布可以例外。
- 正文和辅助文字原则上不小于 11px，辅助文字使用中性灰。
- 间距优先采用 4、8、12、16、24 的节奏。
- 内容内部圆角建议 10～16px；根节点不要重复宿主的圆角、边框和阴影。
- 以白色或近白内容面为主，深色使用接近黑色的内容面，避免大面积高饱和色。
- 动画通常控制在 160～260ms，避免持续闪烁、剧烈缩放和无意义背景动画。
- 使用 `prefers-reduced-motion` 为用户关闭非必要动画。
- 按钮应有清楚的可访问名称；图片需要有意义的 `alt`，纯装饰图使用空 `alt`。
- 触控目标建议至少 40×40px；键盘交互应有清楚的焦点样式。
- 窗口变化时保持信息顺序稳定，不依赖固定像素宽度。

```css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
```

## 7. HTML、CSS、JavaScript 和资源规则

### HTML

`app/index.html` 只写应用内容，不要包含完整文档外壳。宿主会负责创建 `doctype`、`html`、`head`、安全策略和脚本入口。

第三方 HTML 不能包含：

```text
script、iframe、object、embed、form、base、meta、link
```

脚本必须写在 `app/main.js`，样式必须写在 `app/style.css`。

### CSS

- 禁止远程 `@import`。
- 禁止 HTTP/HTTPS、协议相对地址和远程 `url()`。
- 不要覆盖宿主页面；CSS 只在自己的沙箱内生效。
- 根节点必须可以同时适应实际卡片尺寸和完整视窗。

### JavaScript

第三方脚本不得使用：

```text
fetch、XMLHttpRequest、WebSocket、EventSource、sendBeacon
```

不要尝试访问 `parent` 的 DOM 或假设拥有设备权限。宿主会自动处理主题、全屏标志、可见性暂停、心跳和尺寸报告。

### 包内资源引用

所有媒体资源通过 `wideck://` 引用：

```html
<img src="wideck://assets/images/cover.webp" alt="专辑封面">
<audio src="wideck://assets/audio/background.mp3" preload="metadata"></audio>
<video src="wideck://assets/video/intro.mp4" playsinline></video>
```

CSS 中也可以引用包内资源：

```css
.hero {
  background-image: url("wideck://assets/images/background.webp");
}
```

音频应由用户操作后播放，不要假设浏览器允许自动播放。页面不可见时，宿主可能暂停音视频和动画。

## 8. 五分钟制作一个最小卡片

下面的示例展示主题切换、双模式和简单交互。

### 第一步：创建文件树

```text
hello-wideck/
├── manifest.json
├── app/
│   ├── index.html
│   ├── style.css
│   └── main.js
└── assets/
    └── preview.svg
```

### 第二步：编写 app/index.html

```html
<main class="app">
  <section class="widget-view">
    <span class="eyebrow">今日问候</span>
    <strong data-count>0</strong>
    <button type="button" data-add>记录一次灵感</button>
  </section>

  <section class="fullscreen-view">
    <span class="eyebrow">今日问候</span>
    <h1>已经记录 <b data-count>0</b> 次灵感</h1>
    <p class="secondary">这是全屏模式，可放置更完整的说明与操作。</p>
    <button type="button" data-add>再记录一次</button>
  </section>
</main>
```

### 第三步：编写 app/style.css

```css
:root, html[data-theme="light"] {
  color-scheme: light;
  --surface: #fff;
  --text: #1d1d1f;
  --muted: #6e6e73;
  --accent: #0071e3;
}

html[data-theme="dark"] {
  color-scheme: dark;
  --surface: #1c1c1e;
  --text: #f5f5f7;
  --muted: #a1a1a6;
  --accent: #2997ff;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif;
}

.app {
  min-height: 100%;
  color: var(--text);
  background: var(--surface);
}

.widget-view,
.fullscreen-view {
  min-height: 100%;
  padding: 16px;
}

.widget-view {
  display: grid;
  align-content: space-between;
  gap: 12px;
  padding-right: 76px;
}

.widget-view strong { font-size: 48px; }
.fullscreen-view { display: none; }
.secondary, .eyebrow { color: var(--muted); }

button {
  min-height: 40px;
  border: 0;
  border-radius: 12px;
  color: #fff;
  background: var(--accent);
}

html[data-wideck-fullscreen] .widget-view { display: none; }
html[data-wideck-fullscreen] .fullscreen-view {
  display: grid;
  min-height: 100svh;
  align-content: center;
  gap: 18px;
  padding: max(72px, env(safe-area-inset-top)) 24px 32px;
}
```

### 第四步：编写 app/main.js

```js
let count = 0;

function render() {
  document.querySelectorAll("[data-count]").forEach((node) => {
    node.textContent = String(count);
  });
}

document.querySelectorAll("[data-add]").forEach((button) => {
  button.addEventListener("click", () => {
    count += 1;
    render();
  });
});

render();
```

### 第五步：制作 assets/preview.svg

预览图应只表达卡片内容，不绘制宿主按钮。保持文件小于 64KB：

```svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 200">
  <rect width="320" height="200" fill="#f5f5f7"/>
  <text x="24" y="48" font-family="sans-serif" font-size="16" fill="#6e6e73">今日问候</text>
  <text x="24" y="132" font-family="sans-serif" font-size="64" fill="#1d1d1f">0</text>
</svg>
```

最后添加第 3 节的 `manifest.json`，调整 `id`、标题、描述和尺寸即可。

## 9. 权限和官方包

第三方包无论是否签名，都必须使用：

```json
"permissions": []
```

官方包可以声明由官方目录授予的能力。目前包括：

| 能力 | 用途 |
|---|---|
| `weather.refresh`、`weather.locate` | 天气刷新与用户主动定位 |
| `music.state`、`music.toggle`、`music.previous`、`music.next` | 读取和控制全站播放器 |
| `music.open`、`music.random` | 打开音乐页、随机播放 |
| `battery.state`、`battery.refresh` | 浏览器允许时读取电池状态 |
| `time.live` | 使用宿主时间 |
| `prompt.rotate` | 切换本地灵感 |
| `ai.open` | 打开全站 AI 浮层 |
| `content.latest` | 读取站内公开内容摘要 |
| `local.calendar.add`、`local.todo.add` | 维护宿主的本地日程和待办 |

第三方包填写这些字符串会直接被拒绝。只有官方目录中的包路径、权限、尺寸、包 ID 和整包哈希全部一致，宿主才会加载官方能力。

## 10. 完整性、签名和性能限制

### integrity.json 的准确 Schema

`integrity.json` 使用 `wideck.integrity` v1。下面展示的是完整数据结构，不是可自行增删字段的建议格式：

```json
{
  "format": "wideck.integrity",
  "version": 1,
  "packageId": "hello-wideck",
  "release": "1.0.0",
  "files": [
    {
      "path": "app/index.html",
      "bytes": 1234,
      "sha256": "0000000000000000000000000000000000000000000000000000000000000000"
    }
  ]
}
```

示例中的字节数和零哈希只是结构占位，不能直接用于真实包。为保证下载本文后仍然离线完整，下面直接内嵌机器可读的完整 JSON Schema；无需再另外下载文件：

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "WIDECK integrity.json",
  "description": "WIDECK V5 使用的完整性清单格式。integrity.json 与 signature.json 本身不得列入 files。",
  "type": "object",
  "additionalProperties": false,
  "required": ["format", "version", "packageId", "release", "files"],
  "properties": {
    "format": { "const": "wideck.integrity" },
    "version": { "const": 1 },
    "packageId": {
      "type": "string",
      "pattern": "^[a-z0-9][a-z0-9-]{2,63}$"
    },
    "release": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(?:-[A-Za-z0-9.-]+)?$"
    },
    "files": {
      "type": "array",
      "minItems": 5,
      "uniqueItems": true,
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["path", "bytes", "sha256"],
        "properties": {
          "path": {
            "type": "string",
            "pattern": "^(?:manifest\\.json|(?:app|assets)/[^\\\\]+)$"
          },
          "bytes": { "type": "integer", "minimum": 0 },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          }
        }
      }
    }
  }
}
```

生成规则必须全部满足：

1. 顶层只能包含 `format`、`version`、`packageId`、`release`、`files`。
2. `packageId`、`release` 必须与最终写入包内的 `manifest.json` 完全相同。
3. `files` 必须是对象数组，不能是以路径为键的对象，也不能拆成 `files` 与 `sizes` 两张表。
4. 每项只能包含 `path`、`bytes`、`sha256`；路径不得重复，并按 `path` 升序排列。
5. `bytes` 是文件原始内容的字节数，不是字符数，也不是 ZIP 压缩后的大小。
6. `sha256` 是对应原始文件内容的小写 64 位十六进制 SHA-256。
7. 数组必须逐一列出 `manifest.json` 以及 `app/`、`assets/` 下的全部文件。
8. `integrity.json` 不能列出自身；可选的 `signature.json` 也不能列入。
9. 签名针对 `integrity.json` 文件的准确字节，而不是重新解析后再序列化的 JSON。

以下常见写法不是 WIDECK V5 格式，即使其中的哈希全部正确也会拒绝导入：

```json
{
  "algorithm": "sha256",
  "files": {},
  "sizes": {}
}
```

### ZIP 容器规则

- `.wideck` 本质是 ZIP，但根目录必须直接出现 `manifest.json`、`integrity.json`、`app/`、`assets/`，不能再套一层项目文件夹。
- 路径统一使用 `/`，不得包含绝对路径、`..`、反斜杠、重复文件或加密条目。
- 压缩方式只使用 Store 或 Deflate。
- 常规文件权限统一为 `0644`，目录统一为 `0755`；不要把本机的 `0666`、`0777`、setgid 等权限带入包中。
- 不要先生成自制 `integrity.json` 再调用通用 ZIP 命令。官方打包器会规范化清单、完整性结构和 ZIP 权限，并在输出后再次校验。

权限异常通常是“没有使用官方打包器”的线索，但浏览器不会执行 ZIP 中的 Unix 权限；导致旧包被拒绝的直接原因通常仍是完整性 Schema、路径或哈希不匹配。`wideck-verify` 会把权限也作为规范一致性检查，避免问题包继续流转。

### 包体限制

- 压缩包最大 6MB，解包后最大 10MB。
- 最多 24 个文件。
- HTML、CSS 各最大 256KB。
- JavaScript 最大 512KB。
- 预览图最大 64KB。
- 每个普通资源最大 4MB。
- Canvas 最大 2048×2048。
- DOM 节点不能超过 3000。
- 同时存在的定时器最多 64 个，`setInterval` 最短约 50ms。
- 沙箱动画帧率会被控制在约 30fps，消息超过约 80 条/秒会终止实例。

性能限制针对每个运行实例。第三方卡片实例数没有固定上限，但应主动暂停不可见动画、复用资源，并避免持续创建 DOM、Canvas 或定时器。

### 普通创作者：直接使用网站制作

普通创作者不需要 URSPIRE 源代码、Node.js、`.mjs` 文件或签名密钥：

1. 在卡片页打开导航栏加号，选择“制作”，或直接打开 `/wideck-packager`。
2. “AI 生成”中描述功能、卡片状态和全屏状态，站内 AI 助手会自动读取网站当前的最新版制作说明，再返回符合约束的源文件；浏览器随即校验、打包并下载 `.wideck`。
3. 已经拥有第 2 节源目录时，切换到“文件打包”并选择整个源文件夹。
4. 页面会在当前浏览器中规范化 `manifest.json`、计算哈希、生成 `integrity.json`、制作 ZIP 并下载 `.wideck`。
5. 回到卡片页导入；导入器会再执行一次独立校验。

“文件打包”不会上传源文件；“AI 生成”只会把输入的需求发送给站内模型，模型返回的文件仍由浏览器完成打包。两种方式都不会把生成的卡片包保存到网站服务器。

### 项目维护者与自动化：可选命令行打包

只有已经拥有 URSPIRE 项目仓库的维护者或 CI 才需要执行：

```bash
npm run wideck-pack -- ./hello-wideck ./hello-wideck.wideck
npm run wideck-verify -- ./hello-wideck.wideck
```

第一条命令会规范化清单、生成 `integrity.json`、固定 ZIP 权限并输出 `.wideck` 文件；输出前会自动执行同一套自检。第二条命令可单独验证已有包，适合 AI、测试人员和发布流程使用。只有看到 `WIDECK V5 校验通过` 才应交付或导入。

如果 AI 正在 URSPIRE 项目环境中工作，可以让它直接执行这两条命令；其他 AI 只需交付源目录，再由使用者打开网站打包器选择该目录。不要向普通创作者分发孤立的内部 `.mjs` 文件。

必须自行实现打包器时，应以本节和机器可读 Schema 为准，并至少验证：字段精确匹配、文件覆盖完整、字节数和哈希正确、路径安全、权限规范、生成后的 ZIP 可以被再次读取。自行实现的打包器仍建议用 `wideck-verify` 做最终兼容性检查。

### 可选发布者签名

```bash
npm run wideck-sign -- keygen ./publisher-private.pem ./publisher-public.pem
npm run wideck-sign -- pack ./hello-wideck ./hello-wideck-signed.wideck ./publisher-private.pem
```

私钥应放入密码管理器或加密备份，不得放进卡片包、网站目录或代码仓库。签名包与未签名第三方包拥有完全相同的沙箱权限。

## 11. 测试清单

导入前后至少检查：

- [ ] 文件树、固定入口和 `manifest.json` 正确
- [ ] 小组件内容在声明尺寸内完整显示
- [ ] 手机端 2 列网格下没有拉伸、溢出或遮挡
- [ ] 全屏和退出全屏不闪动、不重置当前状态
- [ ] 右上角没有内容与宿主按钮冲突
- [ ] 浅色、深色和实时切换均正常
- [ ] 全屏文字可以选择，按钮、键盘和触控可用
- [ ] 卡片拖动、刷新后布局和重复添加实例正常
- [ ] 图片、音频和视频全部来自包内资源
- [ ] `prefers-reduced-motion` 下没有非必要持续动画
- [ ] 重复导入、删除包和重新导入行为正常
- [ ] 没有网络请求、远程依赖和未声明权限

项目中的 `wideck-starbound-adventure.wideck` 是高完成度第三方示例，覆盖双模式、键盘与触控、Canvas 游戏、音频、暂停和胜负流程。`wideck-system-packages/` 中的八个包是统一 V5 结构的官方示例。

## 12. 给 AI 的推荐提示词

把下面内容与本文一起交给 AI，并替换方括号中的需求：

```text
你是 WIDECK V5 第三方卡片开发者。请严格依据提供的《WIDECK V5 卡片包制作教程与设计规范》，生成一个可直接打包的完整源目录。

应用需求：[在这里描述卡片，例如“离线番茄钟，2行×2列，带提示音和全屏统计”]

必须做到：
1. 输出 manifest.json、app/index.html、app/style.css、app/main.js、assets/preview.svg 的完整内容；其他素材放在 assets/。
2. manifest 使用 format="wideck.package"、version=5、trust="local"、permissions=[]，固定四个入口路径与完整 ui 声明。
3. 同时设计 widget 与 fullscreen，不机械缩放；使用 html[data-wideck-fullscreen] 切换布局。
4. 使用 html[data-theme="light"] 和 html[data-theme="dark"] 适配网站手动主题切换；prefers-color-scheme 只能作为后备。
5. 右上角预留至少 72×48px，不生成展开、关闭、删除或拖动按钮。
6. 根节点不重复宿主的外圆角、外边框、外阴影和悬停效果；全屏铺满视窗并处理 safe-area。
7. 使用系统字体、灰色辅助文字、清楚对比度、40px 左右触控目标，并适配 prefers-reduced-motion。
8. HTML 不包含 script、iframe、object、embed、form、base、meta、link；JavaScript 不使用任何网络 API 或设备权限。
9. 所有媒体使用 wideck://assets/... 引用，不使用 CDN、远程字体、外部接口或运行时依赖。
10. 不生成 integrity.json、signature.json 或密钥，禁止使用通用 ZIP 命令直接制作最终包。
11. 默认只交付源目录，告诉使用者打开 URSPIRE 网站的 /wideck-packager 选择该目录；只有明确处于 URSPIRE 项目仓库中时才执行 npm run wideck-pack 和 wideck-verify。
12. 先列出文件树，再逐个输出文件的完整内容，最后按规范自检并报告结果；不能只给代码片段或省略号。
```

AI 生成完成后，使用者通过网站打包器生成包，并按第 11 节逐项测试。AI 的文字说明不能替代打包器和导入器的完整性、安全与性能校验。
