@baique/pi-web-sky 0.1.45 → 0.1.46

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 (40) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +9 -9
  3. package/.next/build-manifest.json +2 -2
  4. package/.next/required-server-files.js +1 -1
  5. package/.next/required-server-files.json +1 -1
  6. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  7. package/.next/server/app/_global-error.html +1 -1
  8. package/.next/server/app/_global-error.rsc +1 -1
  9. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  12. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/_not-found.html +1 -1
  14. package/.next/server/app/_not-found.rsc +2 -2
  15. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  16. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  18. package/.next/server/app/api/app-update/route.js +1 -1
  19. package/.next/server/app/index.html +1 -1
  20. package/.next/server/app/index.rsc +4 -4
  21. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  22. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  23. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  24. package/.next/server/app/page.js +3 -3
  25. package/.next/server/app/page_client-reference-manifest.js +1 -1
  26. package/.next/server/app-paths-manifest.json +9 -9
  27. package/.next/server/middleware-build-manifest.js +1 -1
  28. package/.next/server/pages/404.html +1 -1
  29. package/.next/server/pages/500.html +1 -1
  30. package/.next/static/chunks/app/{layout-19e02b79476a6b19.js → layout-4131e9abd303855e.js} +1 -1
  31. package/.next/static/chunks/app/{page-e4d01ab7584fd052.js → page-60d31cc657a66941.js} +3 -3
  32. package/.next/trace +8 -7
  33. package/.next/trace-build +1 -1
  34. package/lib/bg-image.ts +54 -14
  35. package/lib/i18n/messages/en.ts +1 -1
  36. package/lib/i18n/messages/zh-CN.ts +1 -1
  37. package/lib/wallpaper-settings.ts +0 -23
  38. package/package.json +1 -1
  39. /package/.next/static/{mi_AqqMn6gfU7PwatOjlB → bwC_pehF8TcdwJmfZPhhX}/_buildManifest.js +0 -0
  40. /package/.next/static/{mi_AqqMn6gfU7PwatOjlB → bwC_pehF8TcdwJmfZPhhX}/_ssgManifest.js +0 -0
package/.next/trace-build CHANGED
@@ -1 +1 @@
1
- [{"name":"run-webpack","duration":9055753,"timestamp":530754013031,"id":14,"parentId":1,"tags":{},"startTime":1788793247583,"traceId":"3e8aa30f90ee0535"},{"name":"run-typescript","duration":3317493,"timestamp":530763070348,"id":771,"parentId":1,"tags":{},"startTime":1788793256640,"traceId":"3e8aa30f90ee0535"},{"name":"static-check","duration":1974175,"timestamp":530766393578,"id":774,"parentId":1,"tags":{},"startTime":1788793259964,"traceId":"3e8aa30f90ee0535"},{"name":"static-generation","duration":2892213,"timestamp":530768544924,"id":918,"parentId":1,"tags":{},"startTime":1788793262115,"traceId":"3e8aa30f90ee0535"},{"name":"collect-build-traces","duration":17535952,"timestamp":530768369512,"id":915,"parentId":1,"tags":{},"startTime":1788793261940,"traceId":"3e8aa30f90ee0535"},{"name":"telemetry-flush","duration":19,"timestamp":530785907514,"id":927,"parentId":1,"tags":{},"startTime":1788793279478,"traceId":"3e8aa30f90ee0535"},{"name":"next-build","duration":32019077,"timestamp":530753888461,"id":1,"tags":{"buildMode":"default","version":"16.3.1","bundler":"webpack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1788793247459,"traceId":"3e8aa30f90ee0535"}]
1
+ [{"name":"run-webpack","duration":9840715,"timestamp":619164489038,"id":14,"parentId":1,"tags":{},"startTime":1788881657164,"traceId":"4e696be0f98f4b64"},{"name":"run-typescript","duration":7910134,"timestamp":619174332357,"id":782,"parentId":1,"tags":{},"startTime":1788881667008,"traceId":"4e696be0f98f4b64"},{"name":"static-check","duration":1819697,"timestamp":619182248807,"id":785,"parentId":1,"tags":{},"startTime":1788881674924,"traceId":"4e696be0f98f4b64"},{"name":"static-generation","duration":2624262,"timestamp":619184204786,"id":929,"parentId":1,"tags":{},"startTime":1788881676880,"traceId":"4e696be0f98f4b64"},{"name":"collect-build-traces","duration":16171206,"timestamp":619184070053,"id":926,"parentId":1,"tags":{},"startTime":1788881676745,"traceId":"4e696be0f98f4b64"},{"name":"telemetry-flush","duration":18,"timestamp":619200243194,"id":938,"parentId":1,"tags":{},"startTime":1788881692918,"traceId":"4e696be0f98f4b64"},{"name":"next-build","duration":35850977,"timestamp":619164392240,"id":1,"tags":{"buildMode":"default","version":"16.3.1","bundler":"webpack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1788881657068,"traceId":"4e696be0f98f4b64"}]
package/lib/bg-image.ts CHANGED
@@ -3,18 +3,21 @@
3
3
  import { useCallback, useEffect, useState } from "react";
4
4
 
5
5
  /**
6
- * App wallpaper: original file Blob stored raw in IndexedDB (no re-encode,
7
- * no compression), exposed to CSS as `--app-bg-image` on <html>.
8
- * Images go through the CSS background layer on `body`; videos are rendered
9
- * as an autoplaying muted <video> element by AppShell instead.
10
- * Display settings live in lib/wallpaper-settings.ts.
6
+ * App wallpaper: image Blob stored in IndexedDB, exposed to CSS as
7
+ * `--app-bg-image` on <html>. Images go through the CSS background layer
8
+ * on `body`. Display settings live in lib/wallpaper-settings.ts.
9
+ *
10
+ * 存储前经 compressWallpaper 处理:真无损——不降采样、不重采样,保持原始
11
+ * 像素。仅尝试 PNG 无损重编码(对 BMP/TIFF/低效 PNG 等未压缩格式有压缩收益),
12
+ * 并对体积取更小者(JPEG/WebP 等有损源 PNG 往往更大,则直存原文件,零损失)。
13
+ * GIF 不支持(拒绝入库)。
11
14
  */
12
15
 
13
16
  const DB_NAME = "pi-web";
14
17
  const STORE = "bg-image";
15
18
  const KEY = "wallpaper";
16
19
 
17
- export type BgKind = "image" | "video" | null;
20
+ export type BgKind = "image" | null;
18
21
 
19
22
  function openDb(): Promise<IDBDatabase> {
20
23
  return new Promise((resolve, reject) => {
@@ -33,16 +36,51 @@ let currentUrl: string | null = null;
33
36
  let bgKind: BgKind = null;
34
37
 
35
38
  function kindOf(blob: Blob | null): BgKind {
36
- if (!blob) return null;
37
- if (blob.type.startsWith("video/")) return "video";
38
- if (blob.type.startsWith("image/")) return "image";
39
- return null;
39
+ if (!blob || !blob.type.startsWith("image/")) return null;
40
+ return "image";
41
+ }
42
+
43
+ /**
44
+ * 真无损压缩壁纸(上传时调用)。GIF 不支持返回 null;解码失败兜底直存。
45
+ * 已是高效格式(webp/avif)→ 直存原样(无损重编码对其无收益)。
46
+ * 其余:不降采样、保持原始像素,仅尝试 PNG 无损重编码并取更小者——
47
+ * 体积绝不劣化,像素零损失。
48
+ */
49
+ async function compressWallpaper(file: Blob): Promise<Blob | null> {
50
+ if (file.type === "image/gif") return null;
51
+ // 已是高效格式:直存,不做二次编码(无损重编码同样无收益)
52
+ if (file.type === "image/webp" || file.type === "image/avif") return file;
53
+
54
+ let bmp: ImageBitmap;
55
+ try {
56
+ bmp = await createImageBitmap(file);
57
+ } catch {
58
+ return file;
59
+ }
60
+ try {
61
+ // 真无损:canvas 尺寸 = 原始像素尺寸,不重采样
62
+ const canvas = document.createElement("canvas");
63
+ canvas.width = bmp.width;
64
+ canvas.height = bmp.height;
65
+ const ctx = canvas.getContext("2d");
66
+ if (!ctx) return file;
67
+ ctx.drawImage(bmp, 0, 0);
68
+
69
+ // PNG 是 canvas 唯一可靠的无损输出;对 BMP/TIFF/低效 PNG 有压缩收益,
70
+ // 对 JPEG 等有损源通常更大 → 取小者(直存原文件,体积不劣化)
71
+ const png = await new Promise<Blob | null>((resolve) =>
72
+ canvas.toBlob(resolve, "image/png"),
73
+ );
74
+ if (!png) return file;
75
+ return png.size < file.size ? png : file;
76
+ } finally {
77
+ bmp.close();
78
+ }
40
79
  }
41
80
 
42
81
  /**
43
82
  * Swap the stored media: revokes the previous object URL, points
44
- * `--app-bg-image` at the new one (images only video uses its own element)
45
- * and returns the live object URL.
83
+ * `--app-bg-image` at the new one and returns the live object URL.
46
84
  */
47
85
  function setBackground(blob: Blob | null): { url: string | null; kind: BgKind } {
48
86
  if (currentUrl) URL.revokeObjectURL(currentUrl);
@@ -110,8 +148,10 @@ export function useAppBackground() {
110
148
  }, []);
111
149
 
112
150
  const pick = useCallback(async (file: Blob) => {
113
- await writeBackground(file);
114
- const res = setBackground(file);
151
+ const stored = await compressWallpaper(file);
152
+ if (!stored) return; // GIF 等不支持格式:拒绝,不改变现有壁纸
153
+ await writeBackground(stored);
154
+ const res = setBackground(stored);
115
155
  setUrl(res.url);
116
156
  setKind(res.kind);
117
157
  setHasBg(true);
@@ -326,7 +326,7 @@ export const enLocale: LocalePlugin = {
326
326
  "bg.choose": "Choose image…",
327
327
  "bg.replace": "Replace image…",
328
328
  "bg.remove": "Remove wallpaper",
329
- "bg.hint": "The original file is stored locally in your browser — never re-encoded or uploaded.",
329
+ "bg.hint": "Images are stored locally in your browser — never uploaded. Large images are auto-compressed to your screen size.",
330
330
  "bg.repeat": "Tile horizontally",
331
331
  "bg.fill": "Edge colour fill",
332
332
  "bg.adjust": "Adjust horizontal position…",
@@ -327,7 +327,7 @@ export const zhCNLocale: LocalePlugin = {
327
327
  "bg.choose": "选择图片…",
328
328
  "bg.replace": "更换图片…",
329
329
  "bg.remove": "移除背景图",
330
- "bg.hint": "原图保存在浏览器本地,不重新编码、不上传。",
330
+ "bg.hint": "图片保存在浏览器本地,不上传。大图会按屏幕尺寸自动压缩。",
331
331
  "bg.repeat": "平铺(横向重复)",
332
332
  "bg.fill": "边缘色彩填充",
333
333
  "bg.adjust": "调整横向位置…",
@@ -142,29 +142,6 @@ export async function sampleEdgeColors(
142
142
 
143
143
  export type WallpaperDims = { width: number; height: number };
144
144
 
145
- /** Natural dimensions of an image or video URL (video reads metadata only). */
146
- export function loadMediaDims(
147
- url: string,
148
- isVideo: boolean,
149
- ): Promise<WallpaperDims | null> {
150
- if (isVideo) {
151
- return new Promise((resolve) => {
152
- const v = document.createElement("video");
153
- v.preload = "metadata";
154
- v.onloadedmetadata = () =>
155
- resolve({ width: v.videoWidth, height: v.videoHeight });
156
- v.onerror = () => resolve(null);
157
- v.src = url;
158
- });
159
- }
160
- return new Promise((resolve) => {
161
- const img = new Image();
162
- img.onload = () => resolve({ width: img.naturalWidth, height: img.naturalHeight });
163
- img.onerror = () => resolve(null);
164
- img.src = url;
165
- });
166
- }
167
-
168
145
  /** Load, persist and live-apply wallpaper display settings. */
169
146
  export function useWallpaperSettings(hasImage: boolean) {
170
147
  // 惰性初始化直接读 localStorage:首帧即存储值(无存储时为默认)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baique/pi-web-sky",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "Web UI for the pi coding agent — Sky skin edition (fork of @agegr/pi-web / earendil-labs pi-web, MIT)",
5
5
  "homepage": "https://github.com/baique/pi-web-sky#readme",
6
6
  "repository": {