import { buildGraphicPreviewUrl, buildDefaultGraphicInstance, graphicsRegistry, registerDefaultGraphics } from "@/graphics"; import type { ParamValues } from "@/params"; import { buildStickerId, parseStickerId } from "../sticker-id"; import type { StickerBrowseResult, StickerItem, StickerProvider, StickerSearchResult, } from "../types"; const SHAPES_PROVIDER_ID = "shapes"; type ShapeGraphicPreset = { shapeKey: string; name: string; definitionId: string; params?: ParamValues; }; const LEGACY_SHAPE_PRESETS: Record = { square: { shapeKey: "square", name: "Square", definitionId: "rectangle" }, circle: { shapeKey: "circle", name: "Circle", definitionId: "ellipse" }, triangle: { shapeKey: "triangle", name: "Triangle", definitionId: "polygon", params: { sides: 3 }, }, hexagon: { shapeKey: "hexagon", name: "Hexagon", definitionId: "polygon", params: { sides: 6 }, }, diamond: { shapeKey: "diamond", name: "Diamond", definitionId: "polygon", params: { sides: 4 }, }, star: { shapeKey: "star", name: "Star", definitionId: "star" }, }; function getShapePresets(): ShapeGraphicPreset[] { registerDefaultGraphics(); return graphicsRegistry.getAll().map((definition) => ({ shapeKey: definition.id, name: definition.name, definitionId: definition.id, })); } function getShapePreset({ shapeKey, }: { shapeKey: string; }): ShapeGraphicPreset | null { return ( getShapePresets().find((preset) => preset.shapeKey === shapeKey) ?? LEGACY_SHAPE_PRESETS[shapeKey] ?? null ); } function getShapeParams({ preset, }: { preset: ShapeGraphicPreset; }): ParamValues { return { ...buildDefaultGraphicInstance({ definitionId: preset.definitionId }).params, ...preset.params, }; } export function parseShapeStickerId({ stickerId, }: { stickerId: string; }): ShapeGraphicPreset | null { try { const { providerValue } = parseStickerId({ stickerId }); return getShapePreset({ shapeKey: providerValue }); } catch { return null; } } function buildShapeUrl({ shapeKey }: { shapeKey: string }): string { const preset = getShapePreset({ shapeKey }); if (!preset) { return buildGraphicPreviewUrl({ definitionId: "rectangle" }); } return buildGraphicPreviewUrl({ definitionId: preset.definitionId, params: getShapeParams({ preset }), }); } function toStickerItem({ preset, }: { preset: ShapeGraphicPreset; }): StickerItem { return { id: buildStickerId({ providerId: SHAPES_PROVIDER_ID, providerValue: preset.shapeKey, }), provider: SHAPES_PROVIDER_ID, name: preset.name, previewUrl: buildShapeUrl({ shapeKey: preset.shapeKey }), metadata: { definitionId: preset.definitionId, params: preset.params ?? {}, }, }; } function filterShapesByQuery({ query }: { query: string }): ShapeGraphicPreset[] { const normalizedQuery = query.trim().toLowerCase(); const presets = getShapePresets(); if (!normalizedQuery) { return presets; } return presets.filter((preset) => { const definition = graphicsRegistry.get(preset.definitionId); return ( preset.name.toLowerCase().includes(normalizedQuery) || definition.keywords.some((keyword) => keyword.toLowerCase().includes(normalizedQuery), ) ); }); } function paginateShapes({ shapes, options, }: { shapes: ShapeGraphicPreset[]; options?: { page?: number; limit?: number }; }): { items: ShapeGraphicPreset[]; hasMore: boolean; total: number } { const page = Math.max(1, options?.page ?? 1); const limit = Math.max(1, options?.limit ?? getShapePresets().length); const startIndex = (page - 1) * limit; const endIndex = startIndex + limit; const pagedItems = shapes.slice(startIndex, endIndex); return { items: pagedItems, hasMore: endIndex < shapes.length, total: shapes.length, }; } export const shapesProvider: StickerProvider = { id: SHAPES_PROVIDER_ID, async search({ query, options, }: { query: string; options?: { limit?: number }; }): Promise { const filteredShapes = filterShapesByQuery({ query }); const paged = paginateShapes({ shapes: filteredShapes, options: { page: 1, limit: options?.limit ?? getShapePresets().length }, }); return { items: paged.items.map((preset) => toStickerItem({ preset })), total: paged.total, hasMore: paged.hasMore, }; }, async browse({ options, }: { options?: { page?: number; limit?: number }; }): Promise { const paged = paginateShapes({ shapes: getShapePresets(), options, }); return { sections: [ { id: "all", items: paged.items.map((preset) => toStickerItem({ preset })), hasMore: paged.hasMore, layout: "grid", }, ], }; }, resolveUrl({ stickerId, }: { stickerId: string; options?: { width?: number; height?: number }; }): string { const preset = parseShapeStickerId({ stickerId }); return buildShapeUrl({ shapeKey: preset?.shapeKey ?? "rectangle" }); }, };