feat: 书库导入导出/图片变体/书籍搜索/小组件运行时等
新增: - 书库导入导出(library_import/library_export)及测试 - 图片变体生成(image_variants)与响应式图片(responsiveImage) - 书籍搜索(bookSearch)+ BookSearch/LibrarySearchGrid 组件 - 小组件运行时(widgetRuntime)与静态检查(widgetLint) - 上传缓存中间件(upload_cache)与字体 CSS 提取脚本 其它: - 后端 handlers/services 全量调整 - 前端页面、组件、库函数与配置更新
This commit is contained in:
52
frontend/lib/responsiveImage.ts
Normal file
52
frontend/lib/responsiveImage.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// 自适应图片:统一生成后端变体地址(GET /api/img?u=&w=)。
|
||||
// 宽度白名单与后端 backend/service/image_variants.go 同值,约 1.5x 步进。
|
||||
// 源内容不可变(随机哈希文件名),变体可被浏览器/CDN 长期 immutable 缓存。
|
||||
|
||||
/** 宽度白名单(48~1920) */
|
||||
export const VARIANT_WIDTHS = [48, 96, 160, 256, 384, 512, 768, 1080, 1536, 1920] as const;
|
||||
|
||||
/**
|
||||
* pickVariantWidth 选「≥ 需求」的最小白名单档(保证清晰度);
|
||||
* 需求超过最大档时回落最大档(源图不够大时后端会原样透传,不会放大)。
|
||||
*/
|
||||
export function pickVariantWidth(cssPx: number, dpr = 1): number {
|
||||
const need = Math.max(1, Math.round(cssPx * dpr));
|
||||
for (const w of VARIANT_WIDTHS) {
|
||||
if (w >= need) return w;
|
||||
}
|
||||
return VARIANT_WIDTHS[VARIANT_WIDTHS.length - 1];
|
||||
}
|
||||
|
||||
/** variantURL 指定宽度的变体地址 */
|
||||
export function variantURL(src: string, w: number): string {
|
||||
return `/api/img?u=${encodeURIComponent(src)}&w=${w}`;
|
||||
}
|
||||
|
||||
/** isOwnImage 是否本站图片(/uploads 本地文件或 /api/media 对象),只有本站图能走变体端点 */
|
||||
export function isOwnImage(src: string): boolean {
|
||||
return src.startsWith("/uploads/") || src.startsWith("/api/media/");
|
||||
}
|
||||
|
||||
/**
|
||||
* densitySrcSet 固定尺寸位的密度 srcset(如头像、固定广告位):
|
||||
* 返回 "url 1x, url 2x";不同 DPR 选中同档时自动去重。
|
||||
*/
|
||||
export function densitySrcSet(src: string, cssPx: number, dprs: number[] = [1, 2]): string {
|
||||
const seen = new Set<number>();
|
||||
const parts: string[] = [];
|
||||
for (const dpr of dprs) {
|
||||
const w = pickVariantWidth(cssPx, dpr);
|
||||
if (seen.has(w)) continue;
|
||||
seen.add(w);
|
||||
parts.push(`${variantURL(src, w)} ${dpr}x`);
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
/**
|
||||
* widthSrcSet 可变宽度位的宽度 srcset(如封面、正文图),配合 sizes 使用。
|
||||
* widths 请直接传白名单档位。
|
||||
*/
|
||||
export function widthSrcSet(src: string, widths: readonly number[]): string {
|
||||
return widths.map((w) => `${variantURL(src, w)} ${w}w`).join(", ");
|
||||
}
|
||||
Reference in New Issue
Block a user