apptify 0.2.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +40 -239
  2. package/dist/apptify.css +1 -1
  3. package/dist/apptify.js +4153 -2177
  4. package/dist/components/button.d.ts +1 -1
  5. package/dist/components/content.d.ts +73 -362
  6. package/dist/components/date-picker.d.ts +10 -0
  7. package/dist/components/forms.d.ts +77 -18
  8. package/dist/components/foundation.d.ts +2061 -111
  9. package/dist/components/gallery-scrollbar.d.ts +46 -0
  10. package/dist/components/mobile-image-viewer.d.ts +166 -0
  11. package/dist/components/navibar.d.ts +80 -0
  12. package/dist/components/overlays.d.ts +127 -158
  13. package/dist/components/tabs.d.ts +117 -0
  14. package/dist/core/context.d.ts +573 -9
  15. package/dist/core/desktop-image-enter.d.ts +6 -0
  16. package/dist/core/desktop-image-return.d.ts +1 -0
  17. package/dist/core/device.d.ts +5 -0
  18. package/dist/core/image-gallery-motion.d.ts +2 -0
  19. package/dist/core/image-geometry.d.ts +28 -0
  20. package/dist/core/image-motion.d.ts +18 -0
  21. package/dist/core/layers.d.ts +2 -0
  22. package/dist/core/motion.d.ts +2 -0
  23. package/dist/core/popup-motion.d.ts +12 -0
  24. package/dist/core/popup-placement.d.ts +31 -0
  25. package/dist/core/preview-navigation.d.ts +5 -0
  26. package/dist/index.d.ts +1214 -339
  27. package/docs/COMPONENTS.md +18 -16
  28. package/docs/PLAYGROUND-TEMPLATE.md +51 -0
  29. package/docs/VALIDATION.md +25 -1
  30. package/package.json +35 -7
  31. package/templates/playground/README.md +21 -0
  32. package/templates/playground/gitignore +3 -0
  33. package/templates/playground/index.html +26 -0
  34. package/templates/playground/package.json +26 -0
  35. package/templates/playground/playground/App.vue +73 -0
  36. package/templates/playground/playground/ComponentDemo.vue +234 -0
  37. package/templates/playground/playground/catalog.ts +80 -0
  38. package/templates/playground/playground/components/ComponentIndex.vue +62 -0
  39. package/templates/playground/playground/components/PageFooter.vue +3 -0
  40. package/templates/playground/playground/components/UserMenu.vue +132 -0
  41. package/templates/playground/playground/components/not-found-paper-scene.vue +210 -0
  42. package/templates/playground/playground/components/not-found-paper-story.ts +149 -0
  43. package/templates/playground/playground/env.d.ts +1 -0
  44. package/templates/playground/playground/main.ts +8 -0
  45. package/templates/playground/playground/router/index.ts +24 -0
  46. package/templates/playground/playground/style.css +228 -0
  47. package/templates/playground/playground/views/[...all].vue +68 -0
  48. package/templates/playground/playground/views/components.vue +126 -0
  49. package/templates/playground/playground/views/index.vue +75 -0
  50. package/templates/playground/playground/views/settings.vue +98 -0
  51. package/templates/playground/public/images/SOURCES.md +21 -0
  52. package/templates/playground/public/images/airpods-max-orange.jpg +0 -0
  53. package/templates/playground/public/images/architecture.jpg +0 -0
  54. package/templates/playground/public/images/city.jpg +0 -0
  55. package/templates/playground/public/images/coast.jpg +0 -0
  56. package/templates/playground/public/images/desert.jpg +0 -0
  57. package/templates/playground/public/images/food.jpg +0 -0
  58. package/templates/playground/public/images/forest.jpg +0 -0
  59. package/templates/playground/public/images/lake.jpg +0 -0
  60. package/templates/playground/public/images/mountains.jpg +0 -0
  61. package/templates/playground/public/images/snow.jpg +0 -0
  62. package/templates/playground/public/images/sunset.jpg +0 -0
  63. package/templates/playground/public/images/waterfall.jpg +0 -0
  64. package/templates/playground/source-snapshot.json +111 -0
  65. package/templates/playground/tsconfig.json +24 -0
  66. package/templates/playground/vite.config.ts +9 -0
  67. package/tooling/create-playground.mjs +35 -0
  68. package/tooling/prepare-package.mjs +35 -0
  69. package/tooling/prepare-playground.mjs +103 -0
  70. package/tooling/test-create-apptify.mjs +76 -0
  71. package/tooling/test-playground-package.mjs +188 -0
  72. package/tooling/vite.d.ts +5 -0
  73. package/tooling/vite.js +10 -0
@@ -1,6 +1,6 @@
1
1
  # 组件 API 参考
2
2
 
3
- 本页按 `src/index.ts` 合并的组件注册表列出当前 **68 个导出组件**,不是未来功能清单。组件名均可换成 kebab-case 模板标签,例如 `AppleOtpInput` 对应 `<apple-otp-input>`。
3
+ 本页按 `src/index.ts` 合并的组件注册表列出当前 **66 个导出组件**,不是未来功能清单。组件名均可换成 kebab-case 模板标签,例如 `AppleOtpInput` 对应 `<apple-otp-input>`。
4
4
 
5
5
  ## 公共约定
6
6
 
@@ -34,7 +34,7 @@ Tabs、TabBar、SegmentedControl、DatePicker 日期/时间页签均不使用 Ri
34
34
  | `AppleButton` | M;`variant=primary`(primary/secondary/outline/ghost/danger)、`size=medium`(small/medium/large)、`icon: Component`、`iconOnly`、`label`、`disabled`、`loading`、`ripple=true`、`href`、`type=button` | `click(event)`;default slot。纯图标按钮须提供 label。只有允许完整动效时启用 Ripple。 |
35
35
  | `AppleLink` | `href`、`external`、`disabled` | default slot;external 使用新窗口并设置 `noopener noreferrer`。 |
36
36
  | `AppleCard` | M;`title`、`subtitle`、`text`、`eyebrow`、`icon: Component 或图片 URL`、`iconColor`、`image`、`imageAlt`、`href`、`zoom=small`(big/small/none)、`shadow=normal`(normal/static/focused/none) | default、media、icon、title、actions slots;href 为标题链接,不会把所有嵌套控件变成一个大按钮。 |
37
- | `AppleImage` | M;必填 `src`、`alt`;`preview=true`、`gallery: string[]`、`index=0`、`squared`、`aspectRatio='4/3'`、`fit=cover`(cover/contain) | caption slot;默认点击预览;图片错误状态;squared 覆盖 aspectRatio。 |
37
+ | `AppleImage` | M;`gallery: AppleImageItem | (string/AppleImageItem)[]`(唯一来源,默认空数组,单图可传对象);`preview=true`、`carousel=false`、`disabled=false`、`label`、`galleryLayout=compact`(compact/tiled/tiled-wrap,所有设备)、`galleryShape=natural`(natural/square)、`index=0`、`squared`、`aspectRatio='4/3'`、`fit=cover`(cover/contain) | caption 与 item({item,index,active}) slots;carousel 强制 compact、常显控件且保留自由滚动;默认点击预览;统一入口,按触屏能力选择预览交互;完整渲染图片组,compact 在固定框内横滑,桌面触控板支持自由连续横滑、一次手势可跨越多张,快速点击翻页延续当前位置和速度;hover 不缩放,多图显示左右翻页按钮(单图小窗与全屏均无翻页箭头),跨多张点击小点时直接衔接当前画面与目标图并支持连续改选,tiled 等高横向平铺,溢出时底部常显自定义滚动条,支持拖动、点击轨道与键盘,滑块比例随内容和容器尺寸同步,tiled-wrap 等宽保留图片比例,按列从上往下紧密堆叠,避免按行对齐产生空白;平铺 hover 只缩放当前图片;galleryShape=square 强制 1:1 并居中裁剪(覆盖 fit),squared 同样作用于图片组;`update:index`、`change(index)`;图片错误状态;squared 覆盖 aspectRatio。 |
38
38
  | `AppleSearch` | `v-model: string`、`placeholder='搜索'`、`label='搜索'`、`disabled` | `search(value)` 在 Enter 时发出;清空更新 model。 |
39
39
  | `AppleContainer` | `width=1200`(number/string) | default slot;数字宽度按 px,宽度不会超过可用空间。 |
40
40
  | `AppleStack` | `direction=column`、`gap=16`、`align=stretch`、`wrap=true` | default slot;Flex 布局;数字 gap 按 px。 |
@@ -77,13 +77,16 @@ validator 返回 `true` 才通过;字符串作为错误信息,`false` 显示
77
77
 
78
78
  日期格式示例:`YYYY/MM`、`YYYY/MM/DD`、`YYYY/MM/DD HH:mm`、`YYYY/MM/DD HH:mm:ss`、`HH:mm`、`HH:mm:ss`。显示格式与模型格式分离:模型依次返回 `YYYY-MM`、`YYYY-MM-DD`、`YYYY-MM-DDTHH:mm[:ss]` 或 `HH:mm[:ss]`。独立 `AppleTimePicker` 已移除,纯时间使用 `<apple-date-picker format="HH:mm" />`;不支持原生 ISO week 模式及日期范围选择。
79
79
 
80
- ## 内容与移动交互 · 29
80
+ ## 内容与移动交互 · 30
81
81
 
82
82
  以下组件均接受 M。
83
83
 
84
+ 导航组件默认协调同一文档中的图片预览;对话框内预览只协调该对话框内的导航。独立页面区域可用 `data-apple-navigation-scope` 容器隔离;多个预览都开始退出后恢复导航。关闭、取消和卸载均释放状态。
85
+
84
86
  | 组件 | 主要 Props | Events / Slots / 说明 |
85
87
  | --- | --- | --- |
86
- | `AppleNavibar` | `v-model: string/number`、`items: AppleItem[]`、`brand`、`brandHref='/'`、`label='主导航'`、`fixed=true`、`breakpoint=640` | `change(value,item)`、`toggle(open)`;brand、item `{item,active}`、actions `{close}` slots。固定模式自带占位;栏宽不超过 breakpoint 时折叠为三横杠菜单;选择、Escape、外部点击或焦点移出后收起。`fixed=false` 可嵌入容器。 |
88
+ | `AppleNavibar` | `v-model: string/number`、`items: AppleItem[]`、`brand`、`brandHref='/'`、`label='主导航'`、`fixed=true`、`breakpoint=640`、`hideOnPreview`(默认跟随 fixed) | `change(value,item)`、`toggle(open)`;brand、item `{item,active}`、actions `{close}` slots。固定模式自带占位;栏宽不超过 breakpoint 时折叠为三横杠菜单;选择、Escape、外部点击或焦点移出后收起。`fixed=false` 可嵌入容器。参与预览时保留占位并隐藏,退出开始时与黑色背景淡出同步淡入。 |
89
+ | `AppleAside` | M;`hideOnPreview=true` | 原生 aside 容器,布局由调用方提供;图片预览期间保留尺寸、隐藏并禁用交互,退出开始时 140ms 淡入。正常文档流侧栏可设 `hideOnPreview=false`。 |
87
90
  | `AppleTabs` | `v-model: string/number`、`items`、`label`、`disabled` | `change(value)`;`panel-${value}` slot `{item}` 或 default `{item,value}`;可非受控;方向键/Home/End。 |
88
91
  | `AppleTabBar` | 与 AppleTabs 相同 | 顶角圆角页签,移动选中背景与内容滑动。 |
89
92
  | `AppleBreadcrumbs` | `items`、`label='当前位置'` | `click(item,event)`;非最后项使用 item.href 或按钮;最后项为当前页文本;无下划线。 |
@@ -104,10 +107,8 @@ validator 返回 `true` 才通过;字符串作为错误信息,`false` 显示
104
107
  | `AppleDivider` | `label`、`vertical` | default slot;水平或垂直分隔语义。 |
105
108
  | `AppleSteps` | `v-model: number=0`、`items`、`clickable`、`disabled`、`label` | `change(index)`;模型是从 0 开始的步骤位置,不是 item.value。 |
106
109
  | `AppleTimeline` | `items: AppleTimelineItem[]`、`orientation=vertical`(horizontal/vertical)、`label` | item slot `{item}`;选项增加 `time?` 和 `tone?: default/success/danger`。 |
107
- | `AppleCarousel` | `items: AppleSlide[]`、`v-model: number`、`label`、`disabled` | `change(index)`;item slot `{item,index,active}`;选项增加 src/alt;滚动吸附与按钮/键盘,不自动播放。 |
108
110
  | `ApplePullRefresh` | `v-model: boolean`、`disabled`、`threshold=72`、`label` | `refresh(done)`;default slot `{refresh,refreshing}`;在滚动顶部下拉,也提供刷新按钮;完成必须调用 done 或将 model 设为 false。 |
109
111
  | `AppleInfiniteScroll` | `loading`、`error: boolean/string`、`finished`、`disabled`、`distance=120`、`finishedText` | `load(done)`、`retry(done)`;default slot;IntersectionObserver 及手动加载按钮;完成后调用 done,或令 loading 从 true 回到 false。 |
110
- | `AppleSwipeCell` | `v-model: boolean`、`disabled`、`label` | default、actions slot `{close}`;横向手势或操作按钮展开,Escape 关闭。 |
111
112
  | `AppleBackTop` | `target: CSS selector=''`、`threshold=300`、`label`、`disabled` | `click(event)`;default slot;空 target 使用 window,达到滚动阈值才显示。 |
112
113
  | `AppleFloatingGroup` | `backTop=true`、`threshold=300`、`target`、`label` | default slot 放附加操作,回顶按钮始终排最下;默认固定右下并考虑安全区。 |
113
114
  | `AppleMarquee` | `text`、`duration=24`、`paused`、`label` | default slot;duration 单位为秒;带暂停按钮;减少动态效果时停止连续滚动。 |
@@ -133,7 +134,7 @@ Table 另支持 `virtual=false`、`height=360`、`rowHeight=48`、`overscan=5`
133
134
  </apple-table>
134
135
  ```
135
136
 
136
- ## 弹层组件 · 10
137
+ ## 弹层组件 · 9
137
138
 
138
139
  以下组件除 Host 外均接受 M。
139
140
 
@@ -143,12 +144,11 @@ Table 另支持 `virtual=false`、`height=360`、`rowHeight=48`、`overscan=5`
143
144
  | `AppleDrawer` | 同 Modal;`placement=right`(left/right);`width=440` | 默认无 footer;全高侧边抽屉。 |
144
145
  | `AppleSheet` | 同 Modal;`width=440` | 默认无 footer;底部面板,移动端全宽;桌面样式最小设计宽度 540px,仍受可用宽度约束。 |
145
146
  | `AppleSnackbar` | `v-model: boolean=true`、`message`、`title`、`tone=default`(default/info/success/warning/danger/error)、`duration=4000`、`action`、`closable=true` | `close(value,reason)`、`action()`;default slot;duration 单位毫秒,0 常驻;直接使用时位置由业务布局决定。 |
146
- | `AppleOverlayHost` | 无公开配置 | 自动渲染当前上下文 overlays.entries;Provider 已包含,不要重复挂载。 |
147
+ | `AppleOverlayHost` | 无公开配置 | 自动渲染当前上下文 overlays.entries.value;Provider 已包含,不要重复挂载。 |
147
148
  | `ApplePopover` | `v-model?: boolean`、`label='更多'`、`disabled`、`placement=bottom`(top/bottom/left/right)、`align=start`(start/center/end)、`width=280`、`role=dialog`、`openOnHover`、`trapFocus=true`、`panelClass` | `open()`、`close()`;activator `{props,open,close,isOpen}`、default `{close}`;支持非受控模式与屏幕边缘避让。 |
148
149
  | `AppleTooltip` | 必填 `text`;`placement=top`、`disabled` | default slot 为触发元素;悬停/焦点显示,Escape 关闭;短文本提示。 |
149
150
  | `AppleMenu` | `v-model?: boolean`、`label='操作'`、`items: AppleMenuItem[]`、`selected`、`disabled` | `select(value,item)`;activator slot 同 Popover、item slot `{item}`;选择后关闭,方向键/Home/End。 |
150
- | `AppleActionSheet` | `v-model: boolean`、`title`、`message`、`items: AppleMenuItem[]`、`cancelText='取消'` | `select(value,item)`、`close(value,reason)`;选择返回条目 value。 |
151
- | `AppleImageViewer` | `v-model: boolean`、`images: (string/AppleViewerImage)[]`、`v-model:index=0`、`loop` | `change(index)`、`close()`、`error(event)`;缩放与手势来自 @panzoom/panzoom,滚轮按实际滚动量连续缩放并保持鼠标焦点。 |
151
+ | `AppleActionSheet` | `v-model: boolean`、`title`、`message`、`items: AppleMenuItem[]`、`cancelText='取消'` | `select(value,item)`、`close(value,reason)`;默认插槽支持自定义模板,提供 `select(item)`、`close(value)`,覆盖 items 内容;取消按钮保留。无拖拽指示条。 |
152
152
 
153
153
  Modal 公共属性:`title`、`message`、`ariaLabel='对话框'`、`persistent`、`loading`、`closeOnConfirm=true`、`confirmText='确定'`、`cancelText='取消'`、`showFooter`、`closable=true`、`width`、`tone=default`。Dialog 默认 width 为 480px。空按钮文本会隐藏对应按钮。
154
154
 
@@ -176,7 +176,7 @@ Modal slots:`title`、default `{close}`、footer `{close,confirm,cancel}`。
176
176
 
177
177
  `AppleMenuItem = { label; value: string|number; disabled?; danger?; description?; icon?: Component }`。
178
178
 
179
- `AppleViewerImage = { src: string; alt?: string; title?: string }`。数组字符串是 URL;需要可访问描述或标题时传对象。index 从 0 开始。
179
+ `AppleViewerImage = { src: string; alt?: string; title?: string; width?: number; height?: number }` 仍是 `AppleImage` 可用的公共图片类型。图片数组字符串是 URL;需要可访问描述或标题时传对象。`AppleImageItem` 扩展该类型,`src` 可选以支持 carousel 的自定义内容,另有 `label`、`value`、`description`。顶层不接受 src/alt;index 从 0 开始、默认 0,支持 `v-model:index`,非有限值归零、越界值截取到有效范围。普通图片忽略无效空项;空列表显示占位且不打开预览。点击放大由 `AppleImage` 内部管理,不另行注册独立查看器。
180
180
 
181
181
  ## 上下文与服务
182
182
 
@@ -184,10 +184,12 @@ Modal slots:`title`、default `{close}`、footer `{close,confirm,cancel}`。
184
184
 
185
185
  | API | 作用 |
186
186
  | --- | --- |
187
- | `theme.set(name)` | 切换注册主题或 system。未知名称抛出错误。 |
188
- | `theme.register(name,tokens,scheme='light')` | 注册自定义主题并补齐基础 token。 |
189
- | `theme.name / resolved / current / themes` | 当前配置名、解析后名称、主题内容、主题注册表。 |
190
- | `motion.set(mode)` | 即时修改全局动效策略。 |
187
+ | `theme.value.set(name)` | 切换注册主题或 system。未知名称抛出错误。 |
188
+ | `theme.value.register(name,tokens,scheme='light')` | 注册自定义主题并补齐基础 token。 |
189
+ | `theme.value.name / resolved / current / themes` | 当前配置名、解析后名称、主题内容、主题注册表。 |
190
+ | `overlays.entries.value` | 当前弹层列表,`Ref<OverlayEntry[]>`。 |
191
+ | `portalTarget.value` | 弹层挂载 DOM 元素。 |
192
+ | `motion.value.set(mode)` | 即时修改全局动效策略。 |
191
193
  | `onMessage(channel,listener)` | 注册监听器,返回取消订阅函数。 |
192
194
  | `sendMessage(channel,payload?)` | 返回该 channel 全部监听器的返回值数组。 |
193
195
  | `dialog(options)` | 打开 dialog 并返回独立句柄。 |
@@ -197,7 +199,7 @@ Modal slots:`title`、default `{close}`、footer `{close,confirm,cancel}`。
197
199
 
198
200
  `OverlayOptions` 实际字段为 `kind?`、`title?`、`message?`、`component?`、`props?`、`onMessage?`、`persistent?`、`confirmText?`、`cancelText?`、`tone?`、`duration?`。不要把声明式组件的 width、loading、action 等属性假定成当前服务参数;需要这些控制时使用声明式组件或自定义内容。
199
201
 
200
- 返回的 `OverlayHandle<T>` 为 `{ id, close(value?), update(patch), result: Promise<T | undefined> }`。`props` 传给动态内容组件;宿主额外注入 `close` 和 `sendMessage`。`onMessage(channel,payload)` 可返回普通值或 Promise,实现双向请求与响应,示例见 [README](../README.md#dialog-与反向通道)。
202
+ 返回的 `OverlayHandle<T>` 为 `{ id, close(value?), update(patch), result: Promise<T | undefined> }`。`props` 传给动态内容组件;宿主额外注入 `close` 和 `sendMessage`。`onMessage(channel,payload)` 可返回普通值或 Promise,实现双向请求与响应。
201
203
 
202
204
  ## 验证边界
203
205
 
@@ -0,0 +1,51 @@
1
+ # 应用模板
2
+
3
+ npm 包包含完整可编辑的 Vue 应用:导航、账号表单、主题/动效/玻璃设置、404、组件示例与图片资源。
4
+
5
+ ## 创建与运行
6
+
7
+ ```sh
8
+ npm create apptify@latest ./my-app
9
+ cd my-app
10
+ npm install
11
+ npm run dev
12
+ ```
13
+
14
+ 也可运行 `npx --package=apptify apptify-playground ./my-app`。
15
+
16
+ 使用 Node.js 22 或 24 LTS。目标目录必须不存在或为空;复制命令不会安装依赖。
17
+
18
+ 项目的 `vendor/` 保存组件库 tgz,`package.json` 使用相对 `file:` 依赖,移动整个项目后仍可独立安装。改用 npm 更新库时,在新项目执行 `npm install apptify@latest`。
19
+
20
+ ## 开始开发
21
+
22
+ | 位置 | 用途 |
23
+ | --- | --- |
24
+ | `playground/App.vue` | 布局、导航项与账号入口 |
25
+ | `playground/views/` | 页面与自动生成的路由 |
26
+ | `playground/router/index.ts` | 滚动恢复、hash 定位与页面标题 |
27
+ | `playground/components/UserMenu.vue` | 账号菜单与登录/注册表单 |
28
+ | `playground/views/settings.vue` | 主题、动效与玻璃设置 |
29
+ | `playground/views/[...all].vue` | 404 页面 |
30
+ | `playground/style.css` | 应用样式 |
31
+ | `public/images/` | 示例图片与来源说明 |
32
+
33
+ 可删除 `views/index.vue` 和 `views/components.vue`,再创建业务页面。删除后重启开发服务;导航会隐藏不存在的页面。删除组件总览后,可一并删除 `ComponentDemo.vue`、`catalog.ts` 和 `components/ComponentIndex.vue`。
34
+
35
+ 例如 `playground/views/orders.vue` 自动对应 `/orders`;在 `App.vue` 的 `navigation` 数组添加入口即可显示导航。`index.vue` 对应首页。
36
+
37
+ 组件通过 `apptify` 导入,样式通过 `apptify/style.css` 导入;`apptify/vite` 提供文件路由插件。已有项目接入时需安装 `vue-router` 和 `vite-plugin-pages`,模板已配置这些依赖。
38
+
39
+ 账号表单需要接入自己的后端。部署使用 history 路由,服务器需将页面请求回退到 `index.html`,保留 API 与静态资源路由。图片来源说明位于 `public/images/SOURCES.md`。
40
+
41
+ ## 检查与本地打包
42
+
43
+ 复制出的应用使用 `npm run typecheck` 和 `npm run build` 检查。
44
+
45
+ 库开发者编辑原仓库的 `playground/`,模板目录在打包时生成。`npm pack` 自动构建组件库并准备模板;本地安装生成的 tgz 后,也可运行 `npx --no-install apptify-playground ../my-app`。
46
+
47
+ `npm run test:package` 验证包内容、独立安装、模板复制、类型检查、构建与浏览器交互,并检查删除示例页面后的行为。命令输出临时目录与验收结果路径。验证指定归档:
48
+
49
+ ```sh
50
+ node tooling/test-playground-package.mjs /absolute/path/apptify-<version>.tgz
51
+ ```
@@ -10,7 +10,7 @@
10
10
  - WebKit 390px、触屏/移动视口模拟验证了输入、叠层弹窗、滚动锁、图片加载与翻页以及系统减少动态效果。
11
11
  - `npm run test:package`:TypeScript 检查、展示站构建、ESM 库构建、声明生成,以及通过包名 `apptify` 和 `apptify/style.css` 导入的独立消费示例均通过。消费者使用纯 JavaScript Options API,不要求 TypeScript。
12
12
  - `npm audit`:本次锁文件检查为 0 项已知漏洞。此结果仅代表当时的软件漏洞数据库。
13
- - 未发布到 npm;旧 tgz 是整改前版本,当前源码需重新 `npm pack`。演示图片不进入组件库安装包。
13
+ - 此节为 2026-09-26 的历史记录。当前包已发布;模板与示例图片随包分发,最新源码需重新 `npm pack`。
14
14
 
15
15
  ## 回归修复
16
16
 
@@ -34,3 +34,27 @@
34
34
  - 可变行高、服务端数据协议和所有大规模数据场景;Table 当前的虚拟滚动为固定行高,已验证 5000 行示例及原地追加数据。
35
35
 
36
36
  测试执行环境为 macOS、Node.js 23.11.0。构建与测试实际通过,但 Vitest 将奇数版本 Node 标记为非支持版本;日常开发和 CI 建议使用 Node.js 22 或 24 LTS。
37
+
38
+ ## 2026-10-01 手机图片预览
39
+
40
+ - `npm test`:9 个文件、123 项单元测试通过。
41
+ - 本轮相关浏览器回归:`gallery.spec.ts`、`overlays-redesign.spec.ts`、`mobile-images.spec.ts` 共 57 项通过,包含 Chrome 实际双指触摸输入和 WebKit 手机模拟;另增 1 项可控时钟检查通过,精确验证 199ms 第二击可放大、200ms 单击退出。
42
+ - 新增检查覆盖双指同时缩放与平移、松手边界约束、上划不退出、下滑背景渐隐、单点退出、动画中途关闭、缩略图黑色占位切换、compact 两端定位、tiled/contain 比例、画布外图片回位、320px、横竖屏、减少动态效果与卸载清理。
43
+ - 切图额外验证慢拖、快速轻扫、快速拖动的松手速度连续性;动画中途接住、取消下滑、从当前位置关闭;2/5 张循环首尾衔接。双端按钮检查模糊、200% 饱和度、半透明背景和按下态;手机控件淡入期间保留图片的 backdrop 采样范围。
44
+ - 双击额外验证浏览器鼠标与实际触摸输入、点击焦点缩放和恢复、减少/关闭动效、缩放中途接住、第二次触摸长按或拖动、双指取消单击,以及叠层弹窗和立即关闭重开时的等待清理。回位动画在延迟关闭开始时捕获实际帧、黑色占位和滚动锁。
45
+ - `npm run build` 与独立消费示例构建通过,包含 TypeScript 检查、ESM 包和类型声明生成。
46
+ - 手机交互采用独立渲染分支;桌面图片缩放、旋转、方向按钮与键盘回归通过。未进行实体 iPhone 验证。
47
+
48
+ ## 2026-10-02 AppleImage 统一设备判断
49
+
50
+ - AppleImage 使用统一入口,缩略图和预览共用触屏检测:`navigator.maxTouchPoints > 0` 或 `(any-pointer: coarse)`,不再根据窗口宽度选择交互。布局选项在所有设备上可用。
51
+ - `foundation.test.ts`、`overlays.test.ts`、`image-device.test.ts`:40 项单元测试通过,包含混合输入设备、粗指针回退、无 matchMedia 和服务端环境。
52
+ - 图片设备、手势和覆盖层回归 46 项通过;各尺寸总览及 WebKit 手机冒烟另 5 项通过。设备组合覆盖 390px/1440px、触屏/非触屏,并检查打开期间调整窗口及关闭重开。
53
+ - `npm run build` 通过,包含类型检查、演示构建、组件库构建和类型声明。
54
+
55
+ ## 2026-10-02 共享状态统一使用 ref
56
+
57
+ - 主题、主题注册表、动效、系统深色状态、弹层列表、挂载点和 Provider 上下文统一使用 `ref`;组件、演示与文档同步使用 `.value`。
58
+ - 全量既有 127 项单元测试通过,另增 1 项共享 ref 身份与解构后响应更新检查通过。
59
+ - 主题切换、系统动效、通知、嵌套弹窗及主题菜单共 5 项浏览器回归通过。
60
+ - 生产构建及类型声明生成通过。
package/package.json CHANGED
@@ -1,12 +1,22 @@
1
1
  {
2
2
  "name": "apptify",
3
- "version": "0.2.0",
4
- "description": "An Apple-inspired, responsive Vue 3 component library with Vuetify ripple.",
3
+ "version": "0.5.1",
4
+ "description": "An Apple-inspired, responsive Vue 3 component library.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/GARBAGELINUXGRAPHIC/apptify.git"
8
+ },
9
+ "homepage": "https://github.com/GARBAGELINUXGRAPHIC/apptify#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/GARBAGELINUXGRAPHIC/apptify/issues"
12
+ },
5
13
  "type": "module",
6
14
  "license": "MIT",
7
15
  "files": [
8
16
  "dist",
9
- "docs"
17
+ "docs",
18
+ "tooling",
19
+ "templates/playground"
10
20
  ],
11
21
  "main": "./dist/apptify.js",
12
22
  "module": "./dist/apptify.js",
@@ -16,21 +26,29 @@
16
26
  "types": "./dist/index.d.ts",
17
27
  "import": "./dist/apptify.js"
18
28
  },
19
- "./style.css": "./dist/apptify.css"
29
+ "./style.css": "./dist/apptify.css",
30
+ "./vite": {
31
+ "types": "./tooling/vite.d.ts",
32
+ "import": "./tooling/vite.js"
33
+ }
20
34
  },
21
35
  "sideEffects": [
22
36
  "**/*.css"
23
37
  ],
24
38
  "scripts": {
25
- "dev": "vite --host 127.0.0.1",
39
+ "dev": "vite",
26
40
  "build": "vue-tsc --noEmit && vite build && vite build --config vite.lib.config.ts && vue-tsc -p tsconfig.lib.json",
27
41
  "typecheck": "vue-tsc --noEmit",
28
42
  "test": "vitest run",
29
43
  "test:e2e": "playwright test",
30
- "test:package": "npm run build && vite build --config vite.consumer.config.ts",
31
- "preview": "vite preview --host 127.0.0.1"
44
+ "test:package": "npm run prepack && vite build --config vite.consumer.config.ts && node tooling/test-playground-package.mjs",
45
+ "test:create": "node tooling/test-create-apptify.mjs",
46
+ "preview": "vite preview --host 127.0.0.1",
47
+ "prepare:playground": "node tooling/prepare-playground.mjs",
48
+ "prepack": "node tooling/prepare-package.mjs"
32
49
  },
33
50
  "peerDependencies": {
51
+ "vite-plugin-pages": "^0.32.4",
34
52
  "vue": "^3.5.0",
35
53
  "vuetify": "^3.9.0"
36
54
  },
@@ -47,9 +65,19 @@
47
65
  "jsdom": "^26.0.0",
48
66
  "typescript": "^5.8.2",
49
67
  "vite": "^6.3.4",
68
+ "vite-plugin-pages": "^0.32.5",
50
69
  "vitest": "^4.1.11",
51
70
  "vue": "^3.5.13",
71
+ "vue-router": "^4.6.4",
52
72
  "vue-tsc": "^2.2.8",
53
73
  "vuetify": "^3.9.0"
74
+ },
75
+ "peerDependenciesMeta": {
76
+ "vite-plugin-pages": {
77
+ "optional": true
78
+ }
79
+ },
80
+ "bin": {
81
+ "apptify-playground": "./tooling/create-playground.mjs"
54
82
  }
55
83
  }
@@ -0,0 +1,21 @@
1
+ # Apptify 应用模板
2
+
3
+ 可编辑的 Vue 应用,包含导航、账号表单、外观设置、404 与组件示例。建议 Node.js 22 或 24 LTS。
4
+
5
+ ```sh
6
+ npm install
7
+ npm run dev
8
+ ```
9
+
10
+ 检查:`npm run typecheck`、`npm run build`。
11
+
12
+ - 页面:`playground/views/`,文件路由自动生成。
13
+ - 布局与导航:`playground/App.vue` 的 `navigation` 数组。
14
+ - 账号表单:`playground/components/UserMenu.vue`,需要接入自己的后端。
15
+ - 样式:`playground/style.css`;图片与来源:`public/images/`。
16
+
17
+ 可删除 `views/index.vue` 和 `views/components.vue`,再添加业务页面;删除后重启开发服务。删除组件总览后,可一并删除 `ComponentDemo.vue`、`catalog.ts` 和 `components/ComponentIndex.vue`。
18
+
19
+ 复制命令在 `vendor/` 保存库归档,移动整个项目后仍可独立安装。使用 npm 更新库:`npm install apptify@latest`。手工复制模板时,将 `gitignore` 重命名为 `.gitignore`。
20
+
21
+ 部署时将页面请求回退到 `index.html`,保留 API 与静态资源路由。
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ dist/
3
+ .env.local
@@ -0,0 +1,26 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="theme-color" content="#f5f5f7" />
7
+ <style>html, body { background-color: var(--app-page-bg, #f5f5f7); }</style>
8
+ <script>
9
+ // Apply the saved page canvas before the application bundle can paint.
10
+ try {
11
+ const preference = JSON.parse(localStorage.getItem('apptify:preferences') || '{}');
12
+ const dark = preference.theme === 'dark' || (preference.theme === 'system' && matchMedia('(prefers-color-scheme: dark)').matches);
13
+ const background = preference.theme === 'graphite' ? '#f3f4f4' : preference.theme === 'rose' ? '#faf7f8' : dark ? '#161617' : '#f5f5f7';
14
+ document.documentElement.style.setProperty('--app-page-bg', background);
15
+ document.documentElement.style.colorScheme = dark ? 'dark' : 'light';
16
+ document.querySelector('meta[name="theme-color"]').setAttribute('content', background);
17
+ } catch { /* Storage can be unavailable; the default canvas remains valid. */ }
18
+ </script>
19
+ <meta name="description" content="Apptify: Apple-inspired Vue 3 components. Built for every screen." />
20
+ <title>Apptify · 组件实验室</title>
21
+ </head>
22
+ <body>
23
+ <div id="app"></div>
24
+ <script type="module" src="/playground/main.ts"></script>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "apptify-app",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "typecheck": "vue-tsc --noEmit",
9
+ "build": "vue-tsc --noEmit && vite build",
10
+ "preview": "vite preview --host 127.0.0.1"
11
+ },
12
+ "dependencies": {
13
+ "apptify": "^0.5.1",
14
+ "vue": "^3.5.0",
15
+ "vue-router": "^4.6.4",
16
+ "vuetify": "^3.9.0",
17
+ "lucide-vue-next": "^0.468.0"
18
+ },
19
+ "devDependencies": {
20
+ "@vitejs/plugin-vue": "^5.2.3",
21
+ "typescript": "^5.8.2",
22
+ "vite": "^6.3.4",
23
+ "vite-plugin-pages": "^0.32.5",
24
+ "vue-tsc": "^2.2.8"
25
+ }
26
+ }
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <apple-provider>
3
+ <apple-navibar class="site-nav" :model-value="$route.path" :items="navigation" @click.capture="navigate">
4
+ <template #brand>
5
+ <router-link class="brand" :to="homePath" aria-label="Apptify 首页"><span class="brand-symbol">a</span><span>Apptify</span></router-link>
6
+ </template>
7
+ <template #actions><UserMenu /></template>
8
+ </apple-navibar>
9
+ <router-view v-slot="{ Component, route }">
10
+ <div ref="routePage" class="route-page">
11
+ <component :is="Component" :key="route.path" />
12
+ </div>
13
+ </router-view>
14
+ </apple-provider>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
19
+ import { useRoute, useRouter } from 'vue-router'
20
+ import { useApple } from 'apptify'
21
+ import { animateEntrance } from 'apptify'
22
+ import UserMenu from './components/UserMenu.vue'
23
+
24
+ const router = useRouter()
25
+ const route = useRoute()
26
+ const apple = useApple()
27
+ watch(() => [apple.theme.value.current.tokens.bg, apple.theme.value.current.scheme], ([background, scheme]) => {
28
+ document.documentElement.style.setProperty('--app-page-bg', background)
29
+ document.documentElement.style.colorScheme = scheme
30
+ document.querySelector('meta[name="theme-color"]')?.setAttribute('content', background)
31
+ }, { immediate: true, flush: 'sync' })
32
+ const routePage = ref<HTMLElement>()
33
+ let entrances: Animation[] = []
34
+ let entranceFrame = 0
35
+
36
+ function cancelEntrance() {
37
+ cancelAnimationFrame(entranceFrame)
38
+ entrances.forEach(animation => animation.cancel())
39
+ entrances = []
40
+ }
41
+ function queueEntrance() {
42
+ cancelEntrance()
43
+ // Let the router restore the scroll position before animating the page.
44
+ // Animate the fixed sidebar itself so it shares the entrance without changing its containing block.
45
+ entranceFrame = requestAnimationFrame(() => {
46
+ const targets = [routePage.value?.querySelector<HTMLElement>('main'), routePage.value?.querySelector<HTMLElement>('.sidebar')]
47
+ targets.forEach((element, index) => {
48
+ if (!element) return
49
+ const animation = animateEntrance(element, index === 1 ? 'x' : 'y')
50
+ if (animation) entrances.push(animation)
51
+ })
52
+ })
53
+ }
54
+ watch(() => route.path, queueEntrance, { flush: 'post' })
55
+ watch(() => [apple.motion.value.mode, apple.motion.value.reduced], () => {
56
+ if (apple.motion.value.reduced || ['none', 'reduced'].includes(apple.motion.value.mode)) cancelEntrance()
57
+ })
58
+ onMounted(queueEntrance)
59
+ onBeforeUnmount(cancelEntrance)
60
+ const navigation = [
61
+ { label: '首页', value: '/', href: '/' },
62
+ { label: '组件', value: '/components', href: '/components' },
63
+ { label: '设置', value: '/settings', href: '/settings' },
64
+ ].filter(item => router.getRoutes().some(page => page.path === item.href))
65
+ const homePath = navigation[0]?.href ?? '/'
66
+
67
+ function navigate(event: MouseEvent) {
68
+ const link = (event.target as HTMLElement).closest<HTMLAnchorElement>('a.apple-navibar__item')
69
+ if (!link || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return
70
+ event.preventDefault()
71
+ void router.push(link.getAttribute('href')!)
72
+ }
73
+ </script>