feat: complete TASK-WP5-02 sticker catalog

This commit is contained in:
suyx
2026-08-03 17:05:12 +08:00
parent 2b189e3186
commit 609c71a3f7
17 changed files with 758 additions and 19 deletions
@@ -0,0 +1,17 @@
{
"name": "@dada/static-sticker-catalog",
"version": "0.0.0",
"private": true,
"type": "module",
"exports": {
".": "./dist/index.js"
},
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"devDependencies": {
"typescript": "7.0.2"
}
}
@@ -0,0 +1,89 @@
export const STATIC_STICKER_CATALOG_SCHEMA_VERSION = "StaticStickerCatalog/v1" as const;
export const STATIC_STICKER_CATALOG_ORIGIN = "bundled_read_only" as const;
export interface StaticStickerThumbnailReference {
media: "thumbnail";
resource_id: string;
resource_version: string;
url: string;
}
export interface StaticStickerCatalogItem {
stable_id: string;
part: number;
order: number;
original_filename: string;
relative_path: string;
width: number;
height: number;
mime_type: "image/png";
mime: "image/png";
sha256: string;
original_reference: string;
thumbnail_reference: StaticStickerThumbnailReference;
resource_version: string;
enabled: boolean;
origin: "bundled_read_only" | "admin_uploaded";
}
export interface StaticStickerCatalog {
schema_version: typeof STATIC_STICKER_CATALOG_SCHEMA_VERSION;
release_version: string;
manifest_sha256: string;
count: number;
part_counts: Record<string, number>;
duplicate_sha256_groups: Array<{ sha256: string; stable_ids: string[] }>;
items: StaticStickerCatalogItem[];
}
export interface VirtualStickerWindowOptions {
itemHeight: number;
viewportHeight: number;
columns: number;
overscanScreens: number;
scrollTop: number;
}
export interface VirtualStickerWindow<T extends { stable_id: string }> {
start: number;
end: number;
top_spacer_px: number;
bottom_spacer_px: number;
items: T[];
}
export function stableStickerId(index: number): string {
if (!Number.isInteger(index) || index < 1) throw new Error("sticker_index_invalid");
return `STK${String(index).padStart(index < 1_000 ? 3 : 4, "0")}`;
}
export function getVirtualStickerWindow<T extends { stable_id: string }>(items: readonly T[], options: VirtualStickerWindowOptions): VirtualStickerWindow<T> {
const itemHeight = Math.max(1, options.itemHeight);
const viewportHeight = Math.max(1, options.viewportHeight);
const columns = Math.max(1, Math.floor(options.columns));
const overscanScreens = Math.max(0, options.overscanScreens);
const scrollTop = Math.max(0, options.scrollTop);
const totalRows = Math.ceil(items.length / columns);
const visibleRows = Math.max(1, Math.ceil(viewportHeight / itemHeight));
const overscanRows = Math.ceil((visibleRows * overscanScreens) / 2);
const firstVisibleRow = Math.min(totalRows, Math.floor(scrollTop / itemHeight));
const startRow = Math.max(0, firstVisibleRow - overscanRows);
const endRow = Math.min(totalRows, firstVisibleRow + visibleRows + overscanRows);
const start = startRow * columns;
const end = Math.min(items.length, endRow * columns);
return {
bottom_spacer_px: Math.max(0, (totalRows - endRow) * itemHeight),
end,
items: items.slice(start, end) as T[],
start,
top_spacer_px: startRow * itemHeight,
};
}
export function staticStickerThumbnailUrl(item: Pick<StaticStickerCatalogItem, "resource_version" | "stable_id">): string {
return `/api/v1/assets/public/${encodeURIComponent(item.resource_version)}/${encodeURIComponent(item.stable_id)}?variant=thumbnail`;
}
export function staticStickerOriginalUrl(item: Pick<StaticStickerCatalogItem, "resource_version" | "stable_id">): string {
return `/api/v1/assets/public/${encodeURIComponent(item.resource_version)}/${encodeURIComponent(item.stable_id)}`;
}
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2024"],
"declaration": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}