img-upload-kit 1.1.1 → 2.0.0
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.
- package/CHANGELOG.md +56 -41
- package/MIGRATION.md +192 -0
- package/README.md +138 -154
- package/dist/bin.js +1186 -0
- package/dist/bin.js.map +1 -0
- package/dist/blur.cjs +40 -412
- package/dist/blur.cjs.map +1 -1
- package/dist/blur.d.cts +5 -5
- package/dist/blur.d.ts +5 -5
- package/dist/blur.js +39 -393
- package/dist/blur.js.map +1 -1
- package/dist/blurDataUtils-DveBRtqE.d.cts +43 -0
- package/dist/blurDataUtils-yx9Q6HlU.d.ts +43 -0
- package/dist/blurService-D5TpMNji.d.ts +83 -0
- package/dist/blurService-DacwylJx.d.cts +83 -0
- package/dist/chunk-2OVNXHTN.cjs +514 -0
- package/dist/chunk-2OVNXHTN.cjs.map +1 -0
- package/dist/chunk-3ICG2SLL.cjs +135 -0
- package/dist/chunk-3ICG2SLL.cjs.map +1 -0
- package/dist/chunk-4ASFG3KB.js +514 -0
- package/dist/chunk-4ASFG3KB.js.map +1 -0
- package/dist/chunk-AI3JJLSA.js +2370 -0
- package/dist/chunk-AI3JJLSA.js.map +1 -0
- package/dist/chunk-CV23O4YU.js +145 -0
- package/dist/chunk-CV23O4YU.js.map +1 -0
- package/dist/chunk-G7LLSKBV.js +158 -0
- package/dist/chunk-G7LLSKBV.js.map +1 -0
- package/dist/chunk-IB3TEDLR.js +294 -0
- package/dist/chunk-IB3TEDLR.js.map +1 -0
- package/dist/chunk-KJVYWKIG.cjs +158 -0
- package/dist/chunk-KJVYWKIG.cjs.map +1 -0
- package/dist/chunk-NEDBIIE3.cjs +226 -0
- package/dist/chunk-NEDBIIE3.cjs.map +1 -0
- package/dist/chunk-QHU7FUO4.cjs +37 -0
- package/dist/chunk-QHU7FUO4.cjs.map +1 -0
- package/dist/chunk-QQHZJKNA.js +135 -0
- package/dist/chunk-QQHZJKNA.js.map +1 -0
- package/dist/chunk-S2IFAWJK.js +37 -0
- package/dist/chunk-S2IFAWJK.js.map +1 -0
- package/dist/chunk-TT5C7K3Q.js +226 -0
- package/dist/chunk-TT5C7K3Q.js.map +1 -0
- package/dist/chunk-U2TWTEBY.cjs +2370 -0
- package/dist/chunk-U2TWTEBY.cjs.map +1 -0
- package/dist/chunk-WISG74VL.cjs +294 -0
- package/dist/chunk-WISG74VL.cjs.map +1 -0
- package/dist/chunk-YRXXQUDE.cjs +145 -0
- package/dist/chunk-YRXXQUDE.cjs.map +1 -0
- package/dist/cli.cjs +273 -76
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +269 -73
- package/dist/cli.js.map +1 -1
- package/dist/headless.cjs +8 -1880
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +3 -1
- package/dist/headless.d.ts +3 -1
- package/dist/headless.js +25 -1886
- package/dist/headless.js.map +1 -1
- package/dist/index-BIdnqtW8.d.ts +234 -0
- package/dist/index-Brk_uHs6.d.cts +234 -0
- package/dist/{index-FVXXKf2v.d.ts → index-CPMlaQsC.d.ts} +76 -153
- package/dist/{index-FVXXKf2v.d.cts → index-DHINe-rA.d.cts} +76 -153
- package/dist/index.cjs +64 -532
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +63 -504
- package/dist/index.js.map +1 -1
- package/dist/{memoryAdapter-CBu7_3Pp.d.ts → memoryAdapter-Ck0ccZbF.d.cts} +3 -2
- package/dist/{memoryAdapter-BJjyLR-1.d.cts → memoryAdapter-DxEKaG0b.d.ts} +3 -2
- package/dist/placeholders-C4ftwaII.d.ts +5 -0
- package/dist/placeholders-x_3rt65M.d.cts +5 -0
- package/dist/react.cjs +1952 -3257
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +188 -50
- package/dist/react.d.ts +188 -50
- package/dist/react.js +1763 -3013
- package/dist/react.js.map +1 -1
- package/dist/server-handler.cjs +14 -0
- package/dist/server-handler.cjs.map +1 -0
- package/dist/server-handler.d.cts +3 -0
- package/dist/server-handler.d.ts +3 -0
- package/dist/server-handler.js +14 -0
- package/dist/server-handler.js.map +1 -0
- package/dist/server.cjs +1096 -532
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +77 -154
- package/dist/server.d.ts +77 -154
- package/dist/server.js +1050 -448
- package/dist/server.js.map +1 -1
- package/dist/storage.cjs +239 -232
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.d.cts +21 -5
- package/dist/storage.d.ts +21 -5
- package/dist/storage.js +227 -207
- package/dist/storage.js.map +1 -1
- package/dist/styles/animated-image.css +112 -0
- package/dist/styles/dropzone-and-tiles.css +247 -0
- package/dist/styles/modals-and-dock.css +248 -0
- package/dist/styles/primitives.css +197 -0
- package/dist/styles/quality-and-inputs.css +184 -0
- package/dist/styles/tokens.css +123 -0
- package/dist/styles.css +39 -24
- package/dist/types-BrP-o9nd.d.cts +85 -0
- package/dist/types-sZ3KoFQY.d.ts +85 -0
- package/dist/types-yShGNQnW.d.cts +61 -0
- package/dist/types-yShGNQnW.d.ts +61 -0
- package/dist/{types-CiLY0DwB.d.cts → upload-C5Trd6Dh.d.cts} +27 -53
- package/dist/{types-CiLY0DwB.d.ts → upload-C5Trd6Dh.d.ts} +27 -53
- package/package.json +92 -32
- package/dist/blurService-DqsnfPTd.d.cts +0 -172
- package/dist/blurService-DqsnfPTd.d.ts +0 -172
package/dist/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage/localFsAdapter.ts","../src/storage/memoryAdapter.ts","../src/storage/s3Adapter.ts","../src/storage/urlParser.ts","../src/storage/supabaseAdapter.ts","../src/storage/types.ts"],"names":["path"],"mappings":"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","file":"storage.js","sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type {\n StorageAdapter,\n StorageFileEntry,\n StorageUploadParams,\n StorageUploadResult,\n} from './types';\n\nexport interface LocalFsStorageOptions {\n /** Absolute or relative root directory on disk (e.g. `./public/uploads`) */\n rootDir: string;\n /** Public URL prefix matching `rootDir` (e.g. `/uploads` or `https://cdn.example.com/uploads`) */\n baseUrl?: string;\n /** Include the bucket segment as a subdirectory under `rootDir`. Defaults to `true`. */\n includeBucketInPath?: boolean;\n}\n\nfunction sanitizeRelativePath(inputPath: string): string {\n const normalized = inputPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n const parts = normalized\n .split('/')\n .filter((segment) => segment && segment !== '.' && segment !== '..');\n if (parts.length === 0) {\n throw new Error('Invalid storage path');\n }\n return parts.join('/');\n}\n\nexport interface LocalFsStorageAdapter extends StorageAdapter {\n getPublicUrl(bucket: string, relativePath: string): string;\n list(bucket: string, folder?: string): Promise<StorageFileEntry[]>;\n deleteByUrl(url: string): Promise<boolean>;\n}\n\nexport function createLocalFsStorageAdapter(\n options: LocalFsStorageOptions,\n): LocalFsStorageAdapter {\n const resolvedRoot = path.resolve(options.rootDir);\n const baseUrl = (options.baseUrl ?? '/uploads').replace(/\\/+$/, '');\n const includeBucket = options.includeBucketInPath !== false;\n\n const resolveDiskTarget = (bucket: string, relativePath: string) => {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanRel = sanitizeRelativePath(relativePath);\n const subPath = includeBucket ? `${cleanBucket}/${cleanRel}` : cleanRel;\n const diskPath = path.resolve(resolvedRoot, subPath);\n\n if (!diskPath.startsWith(resolvedRoot)) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n\n return { diskPath, subPath, cleanRel };\n };\n\n const getPublicUrl = (bucket: string, relativePath: string): string => {\n const { subPath } = resolveDiskTarget(bucket, relativePath);\n return `${baseUrl}/${subPath}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const { diskPath, cleanRel } = resolveDiskTarget(\n params.bucket,\n params.path,\n );\n\n if (params.upsert === false) {\n try {\n await fs.access(diskPath);\n throw new Error(\n `File already exists at ${params.bucket}/${cleanRel}`,\n );\n } catch (err) {\n if (\n err instanceof Error &&\n err.message.includes('File already exists')\n ) {\n throw err;\n }\n }\n }\n\n await fs.mkdir(path.dirname(diskPath), { recursive: true });\n await fs.writeFile(diskPath, params.data);\n\n return {\n bucket: params.bucket,\n path: cleanRel,\n url: getPublicUrl(params.bucket, cleanRel),\n };\n },\n\n async delete(bucket: string, relativePath: string): Promise<void> {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n try {\n await fs.unlink(diskPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err;\n }\n }\n },\n\n getPublicUrl,\n\n async deleteByUrl(url: string): Promise<boolean> {\n if (!url || typeof url !== 'string') return false;\n\n try {\n const cleanUrl = url.split(/[?#]/)[0];\n const prefix = `${baseUrl}/`;\n let subPath: string | null = null;\n\n if (cleanUrl.startsWith(prefix)) {\n subPath = cleanUrl.slice(prefix.length);\n } else if (baseUrl.startsWith('/')) {\n const parsed = new URL(cleanUrl, 'http://localhost');\n const isLocal =\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n const isRelative =\n !cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://');\n if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {\n subPath = parsed.pathname.slice(prefix.length);\n }\n }\n\n if (!subPath) return false;\n\n const decoded = decodeURIComponent(subPath)\n .replace(/\\\\/g, '/')\n .replace(/^\\/+/, '');\n if (!decoded) return false;\n\n const segments = decoded.split('/').filter(Boolean);\n if (\n segments.length === 0 ||\n segments.includes('..') ||\n segments.includes('.')\n ) {\n return false;\n }\n\n const diskPath = path.resolve(resolvedRoot, ...segments);\n const relative = path.relative(resolvedRoot, diskPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n return false;\n }\n\n await fs.unlink(diskPath);\n return true;\n } catch {\n return false;\n }\n },\n\n async list(bucket: string, folder?: string): Promise<StorageFileEntry[]> {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanFolder = folder ? sanitizeRelativePath(folder) : '';\n const targetDir = path.resolve(\n resolvedRoot,\n includeBucket ? cleanBucket : '',\n cleanFolder,\n );\n\n if (!targetDir.startsWith(resolvedRoot)) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n\n try {\n const dirEntries = await fs.readdir(targetDir, { withFileTypes: true });\n const results: StorageFileEntry[] = [];\n\n for (const entry of dirEntries) {\n if (!entry.isFile()) continue;\n const full = path.join(targetDir, entry.name);\n const stat = await fs.stat(full);\n results.push({\n name: entry.name,\n id: entry.name,\n createdAt: stat.birthtime.toISOString(),\n size: stat.size,\n });\n }\n\n return results;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw err;\n }\n },\n };\n}\n","import type {\n StorageAdapter,\n StorageFileEntry,\n StorageUploadParams,\n StorageUploadResult,\n} from './types';\n\nexport interface StoredMemoryObject {\n bucket: string;\n path: string;\n data: Uint8Array;\n contentType: string;\n createdAt: string;\n metadata?: Record<string, string>;\n}\n\nexport interface MemoryStorageAdapter extends StorageAdapter {\n getPublicUrl(bucket: string, path: string): string;\n list(bucket: string, folder?: string): Promise<StorageFileEntry[]>;\n deleteByUrl(url: string): Promise<boolean>;\n getObject(bucket: string, path: string): StoredMemoryObject | undefined;\n clear(): void;\n readonly size: number;\n}\n\nexport interface MemoryStorageOptions {\n baseUrl?: string;\n}\n\nexport function createMemoryStorageAdapter(\n options: MemoryStorageOptions = {},\n): MemoryStorageAdapter {\n const baseUrl = (options.baseUrl || 'https://memory.local/uploads').replace(\n /\\/+$/,\n '',\n );\n const store = new Map<string, StoredMemoryObject>();\n\n const toKey = (bucket: string, path: string) =>\n `${bucket}::${path.replace(/^\\/+/, '')}`;\n\n const getPublicUrl = (bucket: string, path: string): string => {\n const cleanPath = path.replace(/^\\/+/, '');\n return `${baseUrl}/${bucket}/${cleanPath}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const cleanPath = params.path.replace(/^\\/+/, '');\n const key = toKey(params.bucket, cleanPath);\n\n if (params.upsert === false && store.has(key)) {\n throw new Error(`Object already exists at ${params.bucket}/${cleanPath}`);\n }\n\n store.set(key, {\n bucket: params.bucket,\n path: cleanPath,\n data: new Uint8Array(params.data),\n contentType: params.contentType,\n createdAt: new Date().toISOString(),\n metadata: params.metadata,\n });\n\n return {\n bucket: params.bucket,\n path: cleanPath,\n url: getPublicUrl(params.bucket, cleanPath),\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n const key = toKey(bucket, path);\n store.delete(key);\n },\n\n getPublicUrl,\n\n async list(bucket: string, folder?: string): Promise<StorageFileEntry[]> {\n const cleanFolder = folder ? folder.replace(/^\\/+|\\/+$/g, '') : '';\n const entries: StorageFileEntry[] = [];\n\n for (const obj of store.values()) {\n if (obj.bucket !== bucket) continue;\n if (cleanFolder && !obj.path.startsWith(`${cleanFolder}/`)) continue;\n const name = cleanFolder\n ? obj.path.slice(cleanFolder.length + 1)\n : obj.path;\n entries.push({\n name,\n id: `${obj.bucket}/${obj.path}`,\n createdAt: obj.createdAt,\n size: obj.data.byteLength,\n });\n }\n\n return entries;\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const prefix = `${baseUrl}/`;\n if (!url.startsWith(prefix)) return false;\n const rest = url.slice(prefix.length);\n const slashIdx = rest.indexOf('/');\n if (slashIdx === -1) return false;\n const bucket = rest.slice(0, slashIdx);\n const filePath = rest.slice(slashIdx + 1);\n const key = toKey(bucket, filePath);\n return store.delete(key);\n },\n\n getObject(bucket: string, path: string): StoredMemoryObject | undefined {\n return store.get(toKey(bucket, path));\n },\n\n clear(): void {\n store.clear();\n },\n\n get size(): number {\n return store.size;\n },\n };\n}\n","import type {\n StorageAdapter,\n StorageFileEntry,\n StorageUploadParams,\n StorageUploadResult,\n} from './types';\n\nexport interface S3PutObjectInput {\n Bucket: string;\n Key: string;\n Body: Buffer | Uint8Array;\n ContentType: string;\n CacheControl?: string;\n Metadata?: Record<string, string>;\n}\n\nexport interface S3DeleteObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3ListObjectsInput {\n Bucket: string;\n Prefix?: string;\n MaxKeys?: number;\n}\n\nexport interface S3ListedObject {\n Key?: string;\n Size?: number;\n LastModified?: Date | string;\n}\n\nexport interface S3ListObjectsOutput {\n Contents?: S3ListedObject[];\n}\n\n/** Structural S3 / Cloudflare R2 / MinIO transport with zero runtime SDK dependency. */\nexport interface S3TransportLike {\n putObject(input: S3PutObjectInput): Promise<unknown>;\n deleteObject(input: S3DeleteObjectInput): Promise<unknown>;\n listObjects?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n listObjectsV2?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n}\n\nexport type S3PublicUrlBuilder = (bucket: string, path: string) => string;\n\nexport interface S3StorageBaseOptions {\n /** Public base URL prefix (e.g. `'https://cdn.example.com'`) or `(bucket, path) => string` builder. */\n publicBaseUrl: string | S3PublicUrlBuilder;\n /** Default `Cache-Control` header applied when `StorageUploadParams.cacheControl` is omitted. */\n cacheControl?: string;\n /** Include `bucket` segment in string-prefix URLs (`${publicBaseUrl}/${bucket}/${path}`). Defaults to `true`. */\n includeBucketInUrl?: boolean;\n /** Fallback bucket used by `deleteByUrl` when `includeBucketInUrl` is `false`. */\n defaultBucket?: string;\n}\n\nexport type S3StorageAdapterOptions = S3StorageBaseOptions &\n (\n | (S3TransportLike & { client?: never; transport?: never })\n | { client: S3TransportLike; transport?: never; putObject?: never }\n | { transport: S3TransportLike; client?: never; putObject?: never }\n );\n\nfunction normalizeS3Key(rawPath: string): string {\n const normalized = rawPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (!normalized) throw new Error('Invalid S3 object key');\n return normalized;\n}\n\nfunction resolveTransport(options: S3StorageAdapterOptions): S3TransportLike {\n if ('client' in options && options.client) return options.client;\n if ('transport' in options && options.transport) return options.transport;\n if ('putObject' in options && typeof options.putObject === 'function') return options;\n throw new Error('S3StorageAdapter requires putObject/deleteObject or client/transport');\n}\n\n/** Zero-dependency S3 / Cloudflare R2 / MinIO `StorageAdapter`. */\nexport function createS3StorageAdapter(options: S3StorageAdapterOptions): StorageAdapter {\n const transport = resolveTransport(options);\n const includeBucket = options.includeBucketInUrl !== false;\n\n const getPublicUrl = (bucket: string, path: string): string => {\n const key = normalizeS3Key(path);\n if (typeof options.publicBaseUrl === 'function') return options.publicBaseUrl(bucket, key);\n const base = options.publicBaseUrl.replace(/\\/+$/, '');\n return includeBucket ? `${base}/${bucket}/${key}` : `${base}/${key}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const key = normalizeS3Key(params.path);\n const cacheControl =\n params.cacheControl ??\n options.cacheControl ??\n (params.contentType.includes('image/avif')\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const metadata =\n params.metadata || params.contentHash\n ? {\n ...(params.contentHash ? { contentHash: params.contentHash } : {}),\n ...params.metadata,\n }\n : undefined;\n\n await transport.putObject({\n Bucket: params.bucket,\n Key: key,\n Body: params.data,\n ContentType: params.contentType,\n ...(cacheControl ? { CacheControl: cacheControl } : {}),\n ...(metadata ? { Metadata: metadata } : {}),\n });\n\n return {\n bucket: params.bucket,\n path: key,\n url: getPublicUrl(params.bucket, key),\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n await transport.deleteObject({ Bucket: bucket, Key: normalizeS3Key(path) });\n },\n\n getPublicUrl,\n\n async list(bucket: string, folder?: string): Promise<StorageFileEntry[]> {\n const cleanFolder = folder ? normalizeS3Key(folder).replace(/\\/+$/, '') : '';\n const prefix = cleanFolder ? `${cleanFolder}/` : undefined;\n const input: S3ListObjectsInput = {\n Bucket: bucket,\n ...(prefix ? { Prefix: prefix } : {}),\n MaxKeys: 1000,\n };\n\n const output = transport.listObjects\n ? await transport.listObjects(input)\n : transport.listObjectsV2\n ? await transport.listObjectsV2(input)\n : undefined;\n\n return (output?.Contents ?? [])\n .filter((item): item is S3ListedObject & { Key: string } => Boolean(item.Key))\n .map((item) => {\n const name =\n prefix && item.Key.startsWith(prefix)\n ? item.Key.slice(prefix.length)\n : item.Key;\n const createdAt =\n item.LastModified instanceof Date\n ? item.LastModified.toISOString()\n : item.LastModified;\n return {\n name,\n id: `${bucket}/${item.Key}`,\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(item.Size !== undefined ? { size: item.Size } : {}),\n };\n })\n .filter((entry) => entry.name.length > 0);\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n if (typeof options.publicBaseUrl !== 'string' || !url) return false;\n const prefix = `${options.publicBaseUrl.replace(/\\/+$/, '')}/`;\n const cleanUrl = url.split(/[?#]/)[0];\n if (!cleanUrl.startsWith(prefix)) return false;\n\n try {\n const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\\/+/, '');\n if (!rest) return false;\n\n if (includeBucket) {\n const slashIdx = rest.indexOf('/');\n if (slashIdx <= 0 || slashIdx === rest.length - 1) return false;\n await transport.deleteObject({\n Bucket: rest.slice(0, slashIdx),\n Key: rest.slice(slashIdx + 1),\n });\n return true;\n }\n\n if (!options.defaultBucket) return false;\n await transport.deleteObject({ Bucket: options.defaultBucket, Key: rest });\n return true;\n } catch {\n return false;\n }\n },\n };\n}\n","export interface PublicStorageInfo {\n bucket: string;\n path: string;\n filename: string;\n extension: string | null;\n}\n\nexport const DEFAULT_PUBLIC_STORAGE_PREFIX = '/storage/v1/object/public/';\n\nexport function parsePublicStorageUrl(\n url?: string | null,\n publicPrefix: string = DEFAULT_PUBLIC_STORAGE_PREFIX,\n): PublicStorageInfo | null {\n if (!url) return null;\n\n try {\n const parsed = new URL(url, 'http://localhost');\n const pathname = parsed.pathname;\n const prefixIndex = pathname.indexOf(publicPrefix);\n\n if (prefixIndex === -1) {\n return null;\n }\n\n const relativePath = pathname\n .slice(prefixIndex + publicPrefix.length)\n .replace(/^\\/+/, '');\n\n const segments = relativePath.split('/').filter(Boolean);\n if (segments.length < 2) {\n return null;\n }\n\n const [bucket, ...pathParts] = segments;\n const filePath = pathParts.join('/');\n const filename = pathParts[pathParts.length - 1];\n const lastDotIndex = filename.lastIndexOf('.');\n const extension = lastDotIndex >= 0 ? filename.slice(lastDotIndex) : null;\n\n return {\n bucket,\n path: filePath,\n filename,\n extension,\n };\n } catch {\n return null;\n }\n}\n","import type {\n StorageAdapter,\n StorageFileEntry,\n StorageUploadParams,\n StorageUploadResult,\n} from './types';\nimport {\n DEFAULT_PUBLIC_STORAGE_PREFIX,\n parsePublicStorageUrl,\n} from './urlParser';\n\n/**\n * Minimal structural interface matching `@supabase/supabase-js` Storage client,\n * avoiding a mandatory `@supabase/supabase-js` runtime dependency.\n */\nexport interface SupabaseBucketClientLike {\n upload(\n path: string,\n fileBody: Buffer | Uint8Array,\n fileOptions?: {\n contentType?: string;\n upsert?: boolean;\n cacheControl?: string;\n metadata?: Record<string, string>;\n },\n ): Promise<{\n data: { path: string } | null;\n error: { message: string } | null;\n }>;\n remove(paths: string[]): Promise<{\n data?: unknown;\n error: { message: string } | null;\n }>;\n getPublicUrl(path: string): {\n data: { publicUrl: string };\n };\n list?(\n path?: string,\n options?: {\n limit?: number;\n sortBy?: { column: string; order: string };\n },\n ): Promise<{\n data: Array<{\n name: string;\n id: string | null;\n created_at: string | null;\n }> | null;\n error: { message: string } | null;\n }>;\n}\n\nexport interface SupabaseStorageClientLike {\n storage: {\n from(bucket: string): SupabaseBucketClientLike;\n };\n}\n\nexport interface SupabaseStorageAdapterOptions {\n publicUrlPrefix?: string;\n}\n\n/**\n * Plug in any `@supabase/supabase-js` client instance (e.g., service-role client)\n * as a `StorageAdapter` with zero extra wrapper boilerplate.\n */\nexport function createSupabaseStorageAdapter(\n client: SupabaseStorageClientLike,\n options: SupabaseStorageAdapterOptions = {},\n): StorageAdapter {\n const publicPrefix =\n options.publicUrlPrefix || DEFAULT_PUBLIC_STORAGE_PREFIX;\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const cacheControl =\n params.cacheControl ||\n (params.contentType.includes('image/avif')\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const { data, error } = await client.storage\n .from(params.bucket)\n .upload(params.path, params.data, {\n contentType: params.contentType,\n upsert: params.upsert === undefined ? true : params.upsert,\n cacheControl,\n metadata: {\n uploadedAt: new Date().toISOString(),\n contentHash: params.contentHash || 'unknown',\n ...params.metadata,\n },\n });\n\n if (error || !data) {\n throw new Error(\n `Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${error?.message || 'Unknown error'}`,\n );\n }\n\n const {\n data: { publicUrl },\n } = client.storage.from(params.bucket).getPublicUrl(data.path);\n\n return {\n bucket: params.bucket,\n path: data.path,\n url: publicUrl,\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n const { error } = await client.storage.from(bucket).remove([path]);\n if (error) {\n throw new Error(`Failed to delete from Supabase storage: ${error.message}`);\n }\n },\n\n getPublicUrl(bucket: string, path: string): string {\n const {\n data: { publicUrl },\n } = client.storage.from(bucket).getPublicUrl(path);\n return publicUrl;\n },\n\n async list(bucket: string, folder?: string): Promise<StorageFileEntry[]> {\n const bucketClient = client.storage.from(bucket);\n if (!bucketClient.list) {\n return [];\n }\n\n const { data, error } = await bucketClient.list(folder, {\n limit: 1000,\n sortBy: { column: 'created_at', order: 'desc' },\n });\n\n if (error) {\n throw new Error(`Failed to list Supabase bucket files: ${error.message}`);\n }\n\n return (data || [])\n .filter((f) => f.id !== null)\n .map((f) => ({\n name: f.name,\n id: f.id ?? undefined,\n createdAt: f.created_at ?? undefined,\n }));\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return false;\n\n try {\n const targets = new Set<string>([info.path]);\n if (info.extension?.toLowerCase() === '.avif') {\n const basePath = info.path.slice(0, -info.extension.length);\n targets.add(`${basePath}-blur${info.extension}`);\n targets.add(`${basePath}-thumb${info.extension}`);\n }\n const { error } = await client.storage\n .from(info.bucket)\n .remove([...targets]);\n return !error;\n } catch {\n return false;\n }\n },\n };\n}\n","import type { ImageUploadRecord } from '../types';\n\nexport interface StorageUploadParams {\n bucket: string;\n path: string;\n data: Buffer | Uint8Array;\n contentType: string;\n upsert?: boolean;\n cacheControl?: string;\n contentHash?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface StorageUploadResult {\n bucket: string;\n path: string;\n url: string;\n}\n\nexport interface StorageFileEntry {\n name: string;\n id?: string;\n createdAt?: string;\n size?: number;\n}\n\nexport type StorageUploadOptions = StorageUploadParams;\nexport type StorageFileMetadata = StorageFileEntry;\nexport type StorageListResult = StorageFileEntry[];\n\nexport interface StorageAdapter {\n /**\n * Upload optimized image bytes to your storage backend and return its public URL.\n */\n upload(params: StorageUploadParams): Promise<StorageUploadResult>;\n\n /**\n * Delete an image from your storage backend by bucket and relative path.\n */\n delete(bucket: string, path: string): Promise<void>;\n\n /**\n * Optional: Resolve a public URL for a bucket and path.\n */\n getPublicUrl?(bucket: string, path: string): string | Promise<string>;\n\n /**\n * Optional: List files inside a bucket/folder.\n */\n list?(bucket: string, folder?: string): Promise<StorageFileEntry[]>;\n\n /**\n * Optional: Delete an asset directly from its public URL.\n */\n deleteByUrl?(url: string): Promise<boolean>;\n}\n\nexport type UploadRecordHook = (\n record: ImageUploadRecord,\n) => void | Promise<void>;\n\n/**\n * Type-safe helper for authoring a custom StorageAdapter (S3, R2, Vercel Blob, Firebase, etc.).\n */\nexport function createStorageAdapter(adapter: StorageAdapter): StorageAdapter {\n return adapter;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/storage/localFsAdapter.ts","../src/storage/s3Adapter.ts","../src/storage/supabaseAdapter.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n} from './types';\n\nexport interface LocalFsStorageOptions {\n /** Absolute or relative root directory on disk (e.g. `./public/uploads`) */\n rootDir: string;\n /** Public URL prefix matching `rootDir` (e.g. `/uploads` or `https://cdn.example.com/uploads`) */\n baseUrl?: string;\n /** Include the bucket segment as a subdirectory under `rootDir`. Defaults to `true`. */\n includeBucketInPath?: boolean;\n}\n\nfunction sanitizeRelativePath(inputPath: string): string {\n if (!inputPath || typeof inputPath !== 'string') {\n throw new Error('Invalid storage path');\n }\n if (inputPath.includes(':') || inputPath.includes('\\0')) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n\n const normalized = inputPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n const rawSegments = normalized.split('/').filter(Boolean);\n\n if (rawSegments.length === 0) {\n throw new Error('Invalid storage path');\n }\n\n for (const segment of rawSegments) {\n if (segment === '.' || segment === '..') {\n throw new Error('Invalid storage path: path traversal blocked');\n }\n }\n\n return rawSegments.join('/');\n}\n\nfunction assertContainedInRoot(resolvedRoot: string, targetPath: string): void {\n const relative = path.relative(resolvedRoot, targetPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n}\n\nexport interface LocalFsStorageAdapter extends StorageAdapter {\n exists(bucket: string, relativePath: string): Promise<boolean>;\n getPublicUrl(bucket: string, relativePath: string): string;\n list(\n bucket: string,\n folder?: string,\n options?: StorageListOptions,\n ): Promise<StoragePaginatedListResult>;\n deleteByUrl(url: string): Promise<boolean>;\n parseUrl(url: string): { bucket?: string; path: string } | null;\n}\n\nexport function createLocalFsStorageAdapter(\n options: LocalFsStorageOptions,\n): LocalFsStorageAdapter {\n const resolvedRoot = path.resolve(options.rootDir);\n const baseUrl = (options.baseUrl ?? '/uploads').replace(/\\/+$/, '');\n const includeBucket = options.includeBucketInPath !== false;\n\n const resolveDiskTarget = (bucket: string, relativePath: string) => {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanRel = sanitizeRelativePath(relativePath);\n const subPath = includeBucket ? `${cleanBucket}/${cleanRel}` : cleanRel;\n const diskPath = path.resolve(resolvedRoot, subPath);\n\n assertContainedInRoot(resolvedRoot, diskPath);\n\n return { diskPath, subPath, cleanRel };\n };\n\n const getPublicUrl = (bucket: string, relativePath: string): string => {\n const { subPath } = resolveDiskTarget(bucket, relativePath);\n return `${baseUrl}/${subPath}`;\n };\n\n const parseUrl = (url: string): { bucket?: string; path: string } | null => {\n if (!url || typeof url !== 'string') return null;\n\n try {\n const cleanUrl = url.split(/[?#]/)[0];\n const prefix = `${baseUrl}/`;\n let subPath: string | null = null;\n\n if (cleanUrl.startsWith(prefix)) {\n subPath = cleanUrl.slice(prefix.length);\n } else if (baseUrl.startsWith('/')) {\n const parsed = new URL(cleanUrl, 'http://localhost');\n const isLocal =\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n const isRelative =\n !cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://');\n if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {\n subPath = parsed.pathname.slice(prefix.length);\n }\n }\n\n if (!subPath) return null;\n\n const decoded = decodeURIComponent(subPath)\n .replace(/\\\\/g, '/')\n .replace(/^\\/+/, '');\n if (!decoded || decoded.includes(':') || decoded.includes('\\0')) {\n return null;\n }\n\n const segments = decoded.split('/').filter(Boolean);\n if (\n segments.length === 0 ||\n segments.includes('..') ||\n segments.includes('.')\n ) {\n return null;\n }\n\n const diskPath = path.resolve(resolvedRoot, ...segments);\n const relative = path.relative(resolvedRoot, diskPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n return null;\n }\n\n if (includeBucket) {\n if (segments.length < 2) return null;\n return {\n bucket: segments[0],\n path: segments.slice(1).join('/'),\n };\n }\n\n return { path: segments.join('/') };\n } catch {\n return null;\n }\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const { diskPath, cleanRel } = resolveDiskTarget(\n params.bucket,\n params.path,\n );\n\n await fs.mkdir(path.dirname(diskPath), { recursive: true });\n\n try {\n await fs.writeFile(\n diskPath,\n params.data,\n params.upsert === false ? { flag: 'wx' } : undefined,\n );\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n const existsError = new Error(\n `File already exists at ${params.bucket}/${cleanRel}`,\n ) as NodeJS.ErrnoException;\n existsError.code = 'EEXIST';\n throw existsError;\n }\n throw err;\n }\n\n return {\n bucket: params.bucket,\n path: cleanRel,\n url: getPublicUrl(params.bucket, cleanRel),\n };\n },\n\n async delete(bucket: string, relativePath: string): Promise<void> {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n try {\n await fs.unlink(diskPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err;\n }\n }\n },\n\n async exists(bucket: string, relativePath: string): Promise<boolean> {\n try {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n const stat = await fs.stat(diskPath);\n return stat.isFile();\n } catch {\n return false;\n }\n },\n\n getPublicUrl,\n parseUrl,\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = parseUrl(url);\n if (!parsed) return false;\n\n try {\n const subSegments =\n includeBucket && parsed.bucket\n ? [parsed.bucket, ...parsed.path.split('/')]\n : parsed.path.split('/');\n const diskPath = path.resolve(resolvedRoot, ...subSegments);\n assertContainedInRoot(resolvedRoot, diskPath);\n await fs.unlink(diskPath);\n return true;\n } catch {\n return false;\n }\n },\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanFolder = folder ? sanitizeRelativePath(folder) : '';\n const targetDir = path.resolve(\n resolvedRoot,\n includeBucket ? cleanBucket : '',\n cleanFolder,\n );\n\n assertContainedInRoot(resolvedRoot, targetDir);\n\n try {\n const dirEntries = await fs.readdir(targetDir, { withFileTypes: true });\n const results: StorageFileEntry[] = [];\n\n for (const entry of dirEntries) {\n if (!entry.isFile()) continue;\n const full = path.join(targetDir, entry.name);\n const stat = await fs.stat(full);\n results.push({\n name: entry.name,\n id: entry.name,\n createdAt: stat.birthtime.toISOString(),\n size: stat.size,\n });\n }\n\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n const limit = listOptions?.limit ?? results.length;\n const sliced =\n listOptions?.limit !== undefined || offset > 0\n ? results.slice(offset, offset + limit)\n : results;\n const nextCursor =\n listOptions?.limit !== undefined && offset + sliced.length < results.length\n ? String(offset + sliced.length)\n : undefined;\n\n return toPaginatedListResult(sliced, nextCursor);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return toPaginatedListResult([]);\n }\n throw err;\n }\n },\n };\n}\n","import {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n validateStorageMetadata,\n} from './types';\n\nexport interface S3PutObjectInput {\n Bucket: string;\n Key: string;\n Body: Buffer | Uint8Array;\n ContentType: string;\n CacheControl?: string;\n Metadata?: Record<string, string>;\n IfNoneMatch?: string;\n}\n\nexport interface S3DeleteObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3HeadObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3ListObjectsInput {\n Bucket: string;\n Prefix?: string;\n MaxKeys?: number;\n ContinuationToken?: string;\n}\n\nexport interface S3ListedObject {\n Key?: string;\n Size?: number;\n LastModified?: Date | string;\n}\n\nexport interface S3ListObjectsOutput {\n Contents?: S3ListedObject[];\n NextContinuationToken?: string;\n IsTruncated?: boolean;\n}\n\n/** Structural S3 / Cloudflare R2 / MinIO transport with zero runtime SDK dependency. */\nexport interface S3TransportLike {\n putObject(input: S3PutObjectInput): Promise<unknown>;\n deleteObject(input: S3DeleteObjectInput): Promise<unknown>;\n headObject?(input: S3HeadObjectInput): Promise<unknown>;\n listObjects?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n listObjectsV2?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n}\n\nexport type S3PublicUrlBuilder = (bucket: string, path: string) => string;\n\nexport interface S3StorageBaseOptions {\n /** Public base URL prefix (e.g. `'https://cdn.example.com'`) or `(bucket, path) => string` builder. */\n publicBaseUrl: string | S3PublicUrlBuilder;\n /** Default `Cache-Control` header applied when `StorageUploadParams.cacheControl` is omitted. */\n cacheControl?: string;\n /** Include `bucket` segment in string-prefix URLs (`${publicBaseUrl}/${bucket}/${path}`). Defaults to `true`. */\n includeBucketInUrl?: boolean;\n /** Fallback bucket used by `deleteByUrl` when `includeBucketInUrl` is `false`. */\n defaultBucket?: string;\n}\n\nexport type S3StorageAdapterOptions = S3StorageBaseOptions &\n (\n | (S3TransportLike & { client?: never; transport?: never })\n | { client: S3TransportLike; transport?: never; putObject?: never }\n | { transport: S3TransportLike; client?: never; putObject?: never }\n );\n\nfunction normalizeS3Key(rawPath: string): string {\n const normalized = rawPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (!normalized) throw new Error('Invalid S3 object key');\n return normalized;\n}\n\nfunction resolveTransport(options: S3StorageAdapterOptions): S3TransportLike {\n if ('client' in options && options.client) return options.client;\n if ('transport' in options && options.transport) return options.transport;\n if ('putObject' in options && typeof options.putObject === 'function') return options;\n throw new Error('S3StorageAdapter requires putObject/deleteObject or client/transport');\n}\n\nfunction isS3PreconditionError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as {\n code?: unknown;\n name?: unknown;\n statusCode?: unknown;\n $metadata?: { httpStatusCode?: number };\n };\n const status = e.$metadata?.httpStatusCode ?? Number(e.statusCode);\n const code = String(e.code ?? e.name ?? '').toLowerCase();\n return status === 412 || code === 'preconditionfailed' || code === 'eexist';\n}\n\n/** Zero-dependency S3 / Cloudflare R2 / MinIO `StorageAdapter`. */\nexport function createS3StorageAdapter(options: S3StorageAdapterOptions): StorageAdapter {\n const transport = resolveTransport(options);\n const includeBucket = options.includeBucketInUrl !== false;\n\n const getPublicUrl = (bucket: string, path: string): string => {\n const key = normalizeS3Key(path);\n if (typeof options.publicBaseUrl === 'function') return options.publicBaseUrl(bucket, key);\n const base = options.publicBaseUrl.replace(/\\/+$/, '');\n return includeBucket ? `${base}/${bucket}/${key}` : `${base}/${key}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const key = normalizeS3Key(params.path);\n validateStorageMetadata(params.metadata);\n\n const isImmutable =\n params.upsert === false && params.contentType.includes('image/avif');\n const cacheControl =\n params.cacheControl ??\n options.cacheControl ??\n (isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const metadata =\n params.metadata || params.contentHash\n ? {\n ...params.metadata,\n ...(params.contentHash ? { contentHash: params.contentHash } : {}),\n }\n : undefined;\n\n try {\n await transport.putObject({\n Bucket: params.bucket,\n Key: key,\n Body: params.data,\n ContentType: params.contentType,\n ...(cacheControl ? { CacheControl: cacheControl } : {}),\n ...(metadata ? { Metadata: metadata } : {}),\n ...(params.upsert === false ? { IfNoneMatch: '*' } : {}),\n });\n } catch (err) {\n if (isS3PreconditionError(err)) {\n const existsErr = new Error(\n `Object already exists at ${params.bucket}/${key}`,\n ) as Error & { code?: string; statusCode?: number };\n existsErr.code = 'EEXIST';\n existsErr.statusCode = 412;\n throw existsErr;\n }\n throw err;\n }\n\n return {\n bucket: params.bucket,\n path: key,\n url: getPublicUrl(params.bucket, key),\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n await transport.deleteObject({ Bucket: bucket, Key: normalizeS3Key(path) });\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n const key = normalizeS3Key(path);\n if (transport.headObject) {\n try {\n await transport.headObject({ Bucket: bucket, Key: key });\n return true;\n } catch {\n return false;\n }\n }\n const lastSlash = key.lastIndexOf('/');\n const folder = lastSlash > 0 ? key.slice(0, lastSlash) : undefined;\n const targetName = lastSlash > 0 ? key.slice(lastSlash + 1) : key;\n const listed = await this.list!(bucket, folder);\n return listed.some((item) => item.name === targetName);\n },\n\n getPublicUrl,\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanFolder = folder ? normalizeS3Key(folder).replace(/\\/+$/, '') : '';\n const prefix = cleanFolder ? `${cleanFolder}/` : undefined;\n const input: S3ListObjectsInput = {\n Bucket: bucket,\n ...(prefix ? { Prefix: prefix } : {}),\n MaxKeys: listOptions?.limit ?? 1000,\n ...(listOptions?.cursor ? { ContinuationToken: listOptions.cursor } : {}),\n };\n\n const output = transport.listObjects\n ? await transport.listObjects(input)\n : transport.listObjectsV2\n ? await transport.listObjectsV2(input)\n : undefined;\n\n const entries: StorageFileEntry[] = (output?.Contents ?? [])\n .filter((item): item is S3ListedObject & { Key: string } => Boolean(item.Key))\n .map((item) => {\n const name =\n prefix && item.Key.startsWith(prefix)\n ? item.Key.slice(prefix.length)\n : item.Key;\n const createdAt =\n item.LastModified instanceof Date\n ? item.LastModified.toISOString()\n : item.LastModified;\n return {\n name,\n id: `${bucket}/${item.Key}`,\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(item.Size !== undefined ? { size: item.Size } : {}),\n };\n })\n .filter((entry) => entry.name.length > 0);\n\n return toPaginatedListResult(entries, output?.NextContinuationToken);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n if (typeof options.publicBaseUrl !== 'string' || !url) return null;\n const prefix = `${options.publicBaseUrl.replace(/\\/+$/, '')}/`;\n const cleanUrl = url.split(/[?#]/)[0];\n if (!cleanUrl.startsWith(prefix)) return null;\n\n try {\n const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\\/+/, '');\n if (!rest) return null;\n\n if (includeBucket) {\n const slashIdx = rest.indexOf('/');\n if (slashIdx <= 0 || slashIdx === rest.length - 1) return null;\n return {\n bucket: rest.slice(0, slashIdx),\n path: rest.slice(slashIdx + 1),\n };\n }\n\n if (!options.defaultBucket) return null;\n return { bucket: options.defaultBucket, path: rest };\n } catch {\n return null;\n }\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = this.parseUrl?.(url);\n if (!parsed || !parsed.bucket) return false;\n\n try {\n await transport.deleteObject({\n Bucket: parsed.bucket,\n Key: parsed.path,\n });\n return true;\n } catch {\n return false;\n }\n },\n };\n}\n","import {\n type StorageAdapter,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n validateStorageMetadata,\n} from './types';\nimport {\n DEFAULT_PUBLIC_STORAGE_PREFIX,\n parsePublicStorageUrl,\n} from './urlParser';\n\n/**\n * Minimal structural interface matching `@supabase/supabase-js` Storage client,\n * avoiding a mandatory `@supabase/supabase-js` runtime dependency.\n */\nexport interface SupabaseBucketClientLike {\n upload(\n path: string,\n fileBody: Buffer | Uint8Array,\n fileOptions?: {\n contentType?: string;\n upsert?: boolean;\n cacheControl?: string;\n metadata?: Record<string, string>;\n },\n ): Promise<{\n data: { path: string } | null;\n error: { message: string } | null;\n }>;\n remove(paths: string[]): Promise<{\n data?: unknown;\n error: { message: string } | null;\n }>;\n getPublicUrl(path: string): {\n data: { publicUrl: string };\n };\n list?(\n path?: string,\n options?: {\n limit?: number;\n offset?: number;\n sortBy?: { column: string; order: string };\n },\n ): Promise<{\n data: Array<{\n name: string;\n id: string | null;\n created_at: string | null;\n }> | null;\n error: { message: string } | null;\n }>;\n}\n\nexport interface SupabaseStorageClientLike {\n storage: {\n from(bucket: string): SupabaseBucketClientLike;\n };\n}\n\nexport interface SupabaseStorageAdapterOptions {\n publicUrlPrefix?: string;\n}\n\n/**\n * Plug in any `@supabase/supabase-js` client instance (e.g., service-role client)\n * as a `StorageAdapter` with zero extra wrapper boilerplate.\n */\nexport function createSupabaseStorageAdapter(\n client: SupabaseStorageClientLike,\n options: SupabaseStorageAdapterOptions = {},\n): StorageAdapter {\n const publicPrefix =\n options.publicUrlPrefix || DEFAULT_PUBLIC_STORAGE_PREFIX;\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n validateStorageMetadata(params.metadata);\n\n const isImmutable =\n params.upsert === false && params.contentType.includes('image/avif');\n const cacheControl =\n params.cacheControl ||\n (isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const { data, error } = await client.storage\n .from(params.bucket)\n .upload(params.path, params.data, {\n contentType: params.contentType,\n upsert: params.upsert === undefined ? true : params.upsert,\n cacheControl,\n metadata: {\n ...params.metadata,\n uploadedAt: new Date().toISOString(),\n contentHash: params.contentHash || 'unknown',\n },\n });\n\n if (error || !data) {\n throw new Error(\n `Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${error?.message || 'Unknown error'}`,\n );\n }\n\n const {\n data: { publicUrl },\n } = client.storage.from(params.bucket).getPublicUrl(data.path);\n\n return {\n bucket: params.bucket,\n path: data.path,\n url: publicUrl,\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n const { error } = await client.storage.from(bucket).remove([path]);\n if (error) {\n throw new Error(`Failed to delete from Supabase storage: ${error.message}`);\n }\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n const cleanPath = path.replace(/^\\/+/, '');\n const lastSlash = cleanPath.lastIndexOf('/');\n const folder = lastSlash > 0 ? cleanPath.slice(0, lastSlash) : undefined;\n const targetName = lastSlash > 0 ? cleanPath.slice(lastSlash + 1) : cleanPath;\n const entries = await this.list!(bucket, folder);\n return entries.some((entry) => entry.name === targetName);\n },\n\n getPublicUrl(bucket: string, path: string): string {\n const {\n data: { publicUrl },\n } = client.storage.from(bucket).getPublicUrl(path);\n return publicUrl;\n },\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const bucketClient = client.storage.from(bucket);\n if (!bucketClient.list) {\n return toPaginatedListResult([]);\n }\n\n const limit = listOptions?.limit ?? 1000;\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n\n const { data, error } = await bucketClient.list(folder, {\n limit,\n ...(offset > 0 ? { offset } : {}),\n sortBy: { column: 'created_at', order: 'desc' },\n });\n\n if (error) {\n throw new Error(`Failed to list Supabase bucket files: ${error.message}`);\n }\n\n const entries = (data || [])\n .filter((f) => f.id !== null)\n .map((f) => ({\n name: f.name,\n id: f.id ?? undefined,\n createdAt: f.created_at ?? undefined,\n }));\n\n const nextCursor =\n listOptions?.limit !== undefined && entries.length >= limit\n ? String(offset + entries.length)\n : undefined;\n\n return toPaginatedListResult(entries, nextCursor);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return null;\n return { bucket: info.bucket, path: info.path };\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return false;\n\n try {\n const { error } = await client.storage\n .from(info.bucket)\n .remove([info.path]);\n return !error;\n } catch {\n return false;\n }\n },\n };\n}\n"],"mappings":"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","names":["path","path"]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* ── AnimatedImage: blur-up crossfade image (Imagine-Global parity) ─────── */
|
|
2
|
+
.iuk-animated-image {
|
|
3
|
+
position: relative;
|
|
4
|
+
width: 100%;
|
|
5
|
+
height: 100%;
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
overflow: clip;
|
|
8
|
+
border-radius: inherit;
|
|
9
|
+
clip-path: inset(0 round inherit);
|
|
10
|
+
contain: paint;
|
|
11
|
+
transition: background-color var(--iuk-animated-image-color-transition, 300ms ease);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.iuk-animated-image--fill {
|
|
15
|
+
position: absolute;
|
|
16
|
+
inset: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.iuk-animated-image__placeholder {
|
|
20
|
+
position: absolute;
|
|
21
|
+
inset: 0;
|
|
22
|
+
width: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
object-position: center;
|
|
25
|
+
/* Tiny placeholder stretched to full size is blurred and scaled slightly so
|
|
26
|
+
blurred edges stay clipped inside the overflow: hidden wrapper. */
|
|
27
|
+
filter: blur(var(--iuk-animated-image-blur, 20px));
|
|
28
|
+
transform: scale(var(--iuk-animated-image-placeholder-scale, 1.1));
|
|
29
|
+
transform-origin: center;
|
|
30
|
+
z-index: 1;
|
|
31
|
+
opacity: 1;
|
|
32
|
+
transition: opacity var(--iuk-ai-ph-dur, 500ms) ease-in-out;
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.iuk-animated-image__placeholder--hidden {
|
|
37
|
+
opacity: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.iuk-animated-image__img {
|
|
41
|
+
position: relative;
|
|
42
|
+
display: block;
|
|
43
|
+
z-index: 2;
|
|
44
|
+
width: 100%;
|
|
45
|
+
height: auto;
|
|
46
|
+
opacity: 0;
|
|
47
|
+
transform: scale(var(--iuk-animated-image-reveal-scale, 1.02));
|
|
48
|
+
border-radius: inherit;
|
|
49
|
+
user-select: none;
|
|
50
|
+
-webkit-user-select: none;
|
|
51
|
+
-webkit-user-drag: none;
|
|
52
|
+
transition:
|
|
53
|
+
opacity var(--iuk-animated-image-duration, 600ms) ease-in-out,
|
|
54
|
+
transform var(--iuk-animated-image-duration, 600ms) ease-in-out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.iuk-animated-image__img[data-state='loaded'] {
|
|
58
|
+
opacity: 1;
|
|
59
|
+
transform: scale(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.iuk-animated-image__img[data-state='error'] {
|
|
63
|
+
opacity: 0;
|
|
64
|
+
visibility: hidden;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.iuk-animated-image--fill .iuk-animated-image__img {
|
|
68
|
+
position: absolute;
|
|
69
|
+
inset: 0;
|
|
70
|
+
height: 100%;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* In contain mode, the generated blur placeholder stays visible with object-fit: cover
|
|
74
|
+
and scale(1.1) behind the contained image so letterbox/pillarbox bars show the blur. */
|
|
75
|
+
.iuk-animated-image--contain .iuk-animated-image__img {
|
|
76
|
+
transform: none;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.iuk-animated-image__shimmer {
|
|
80
|
+
position: absolute;
|
|
81
|
+
inset: 0;
|
|
82
|
+
z-index: 3;
|
|
83
|
+
background: linear-gradient(
|
|
84
|
+
90deg,
|
|
85
|
+
transparent 0%,
|
|
86
|
+
var(--iuk-animated-image-shimmer, rgba(255, 255, 255, 0.1)) 50%,
|
|
87
|
+
transparent 100%
|
|
88
|
+
);
|
|
89
|
+
animation: iuk-animated-image-shimmer 2s infinite;
|
|
90
|
+
pointer-events: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.iuk-animated-image__shimmer--hidden {
|
|
94
|
+
opacity: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@keyframes iuk-animated-image-shimmer {
|
|
98
|
+
from { transform: translateX(-100%); }
|
|
99
|
+
to { transform: translateX(100%); }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@media (prefers-reduced-motion: reduce) {
|
|
103
|
+
.iuk-animated-image__img,
|
|
104
|
+
.iuk-animated-image__placeholder {
|
|
105
|
+
transform: none !important;
|
|
106
|
+
transition-duration: 0ms !important;
|
|
107
|
+
transition-delay: 0ms !important;
|
|
108
|
+
}
|
|
109
|
+
.iuk-animated-image__shimmer {
|
|
110
|
+
animation: none;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/* image-upload-kit — Layer 4: Dropzones, Grids & PreviewTile */
|
|
2
|
+
|
|
3
|
+
/* SingleImageUpload & Dropzone */
|
|
4
|
+
.iuk-single {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: 10px;
|
|
8
|
+
width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.iuk-single__preview-wrap {
|
|
12
|
+
width: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.iuk-single__preview-wrap .iuk-tile {
|
|
16
|
+
aspect-ratio: 16 / 10;
|
|
17
|
+
max-height: 320px;
|
|
18
|
+
cursor: default;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.iuk-single__download {
|
|
22
|
+
align-self: flex-start;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.iuk-dropzone {
|
|
26
|
+
position: relative;
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
min-height: 200px;
|
|
32
|
+
padding: 28px 20px;
|
|
33
|
+
border-radius: var(--iuk-radius-md);
|
|
34
|
+
border: 1.5px dashed var(--iuk-border-strong);
|
|
35
|
+
background: var(--iuk-surface);
|
|
36
|
+
text-align: center;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
transition: border-color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.iuk-dropzone:hover:not(.iuk-dropzone--disabled):not(.iuk-dropzone--has-image) {
|
|
42
|
+
border-color: var(--iuk-primary);
|
|
43
|
+
background: var(--iuk-surface-hover);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.iuk-dropzone:focus-visible {
|
|
47
|
+
outline: 2px solid var(--iuk-primary);
|
|
48
|
+
outline-offset: 2px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.iuk-dropzone--dragging {
|
|
52
|
+
border-color: var(--iuk-primary);
|
|
53
|
+
background: var(--iuk-primary-soft);
|
|
54
|
+
box-shadow: 0 0 0 3px var(--iuk-primary-soft);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.iuk-dropzone--has-image {
|
|
58
|
+
min-height: 0;
|
|
59
|
+
padding: 0;
|
|
60
|
+
border: 0;
|
|
61
|
+
background: transparent;
|
|
62
|
+
cursor: default;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.iuk-dropzone--disabled { opacity: 0.6; cursor: not-allowed; }
|
|
66
|
+
.iuk-dropzone--error { border-color: var(--iuk-danger); }
|
|
67
|
+
|
|
68
|
+
.iuk-dropzone__prompt {
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 8px;
|
|
73
|
+
max-width: 360px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.iuk-dropzone__icon {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
width: 56px;
|
|
81
|
+
height: 56px;
|
|
82
|
+
margin-bottom: 4px;
|
|
83
|
+
border-radius: 9999px;
|
|
84
|
+
border: 1px solid var(--iuk-border);
|
|
85
|
+
background: var(--iuk-bg);
|
|
86
|
+
color: var(--iuk-primary);
|
|
87
|
+
box-shadow: var(--iuk-shadow-sm);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.iuk-dropzone__text { margin: 0; font-size: 0.9375rem; font-weight: 600; color: var(--iuk-text); }
|
|
91
|
+
.iuk-dropzone__hint { margin: 0 0 4px; font-size: 0.8125rem; color: var(--iuk-text-muted); }
|
|
92
|
+
.iuk-dropzone__progress { margin: 0; font-size: 0.8125rem; font-weight: 600; color: var(--iuk-primary); }
|
|
93
|
+
|
|
94
|
+
/* MultiImageUpload & Grid */
|
|
95
|
+
.iuk-multi { display: flex; flex-direction: column; gap: 12px; width: 100%; }
|
|
96
|
+
.iuk-multi__header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
|
|
97
|
+
.iuk-multi__count { font-weight: 500; color: var(--iuk-text-muted); }
|
|
98
|
+
.iuk-multi__bulk { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
|
|
99
|
+
|
|
100
|
+
.iuk-multi__empty {
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
align-items: center;
|
|
104
|
+
justify-content: center;
|
|
105
|
+
min-height: 220px;
|
|
106
|
+
padding: 32px 20px;
|
|
107
|
+
border-radius: var(--iuk-radius-md);
|
|
108
|
+
border: 1.5px dashed var(--iuk-border-strong);
|
|
109
|
+
background: var(--iuk-surface);
|
|
110
|
+
text-align: center;
|
|
111
|
+
transition: border-color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.iuk-multi__empty--dragging { border-color: var(--iuk-primary); background: var(--iuk-primary-soft); box-shadow: 0 0 0 3px var(--iuk-primary-soft); }
|
|
115
|
+
.iuk-multi__empty--disabled { opacity: 0.6; cursor: not-allowed; }
|
|
116
|
+
.iuk-multi__empty--error { border-color: var(--iuk-danger); }
|
|
117
|
+
|
|
118
|
+
.iuk-multi__title { margin: 0; font-size: 1rem; font-weight: 600; color: var(--iuk-text); }
|
|
119
|
+
.iuk-multi__btn-row { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
|
|
120
|
+
.iuk-multi__subtext { margin: 4px 0 0; font-size: 0.75rem; color: var(--iuk-text-muted); }
|
|
121
|
+
|
|
122
|
+
.iuk-multi__grid {
|
|
123
|
+
display: grid;
|
|
124
|
+
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
125
|
+
gap: 12px;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* PreviewTile */
|
|
129
|
+
.iuk-tile {
|
|
130
|
+
position: relative;
|
|
131
|
+
width: 100%;
|
|
132
|
+
aspect-ratio: 1 / 1;
|
|
133
|
+
border-radius: var(--iuk-radius-md);
|
|
134
|
+
border: 1px solid var(--iuk-border);
|
|
135
|
+
background: var(--iuk-surface);
|
|
136
|
+
overflow: hidden;
|
|
137
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.iuk-tile--selected {
|
|
141
|
+
border-color: var(--iuk-primary);
|
|
142
|
+
box-shadow: 0 0 0 2px var(--iuk-primary);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.iuk-tile--error { border-color: var(--iuk-danger); }
|
|
146
|
+
.iuk-tile--add-more { display: flex; align-items: center; justify-content: center; border: 1.5px dashed var(--iuk-border-strong); }
|
|
147
|
+
.iuk-tile--dragging { border-color: var(--iuk-primary); background: var(--iuk-primary-soft); }
|
|
148
|
+
.iuk-tile--disabled { opacity: 0.55; cursor: not-allowed; }
|
|
149
|
+
|
|
150
|
+
.iuk-tile__media { position: relative; width: 100%; height: 100%; }
|
|
151
|
+
|
|
152
|
+
.iuk-tile__checkbox {
|
|
153
|
+
position: absolute;
|
|
154
|
+
top: 8px;
|
|
155
|
+
left: 8px;
|
|
156
|
+
z-index: 3;
|
|
157
|
+
display: flex;
|
|
158
|
+
align-items: center;
|
|
159
|
+
justify-content: center;
|
|
160
|
+
width: 24px;
|
|
161
|
+
height: 24px;
|
|
162
|
+
border-radius: 6px;
|
|
163
|
+
background: rgba(15, 23, 42, 0.66);
|
|
164
|
+
backdrop-filter: blur(4px);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.iuk-tile__checkbox input { width: 15px; height: 15px; margin: 0; accent-color: var(--iuk-primary); cursor: pointer; }
|
|
168
|
+
|
|
169
|
+
.iuk-tile__action {
|
|
170
|
+
position: absolute;
|
|
171
|
+
top: 8px;
|
|
172
|
+
z-index: 3;
|
|
173
|
+
display: inline-flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
width: 28px;
|
|
177
|
+
height: 28px;
|
|
178
|
+
padding: 0;
|
|
179
|
+
border-radius: 9999px;
|
|
180
|
+
border: 1px solid rgba(255, 255, 255, 0.18);
|
|
181
|
+
background: rgba(15, 23, 42, 0.68);
|
|
182
|
+
backdrop-filter: blur(4px);
|
|
183
|
+
color: #ffffff;
|
|
184
|
+
cursor: pointer;
|
|
185
|
+
transition: background-color 0.15s ease, transform 0.12s ease;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.iuk-tile__action:hover { background: rgba(15, 23, 42, 0.9); transform: scale(1.05); }
|
|
189
|
+
.iuk-tile__action--remove { right: 8px; }
|
|
190
|
+
.iuk-tile__action--remove:hover { background: var(--iuk-danger); }
|
|
191
|
+
.iuk-tile__action--blur { right: 40px; }
|
|
192
|
+
.iuk-tile__action--alt { right: 72px; }
|
|
193
|
+
.iuk-tile__action--has-alt { background: rgba(22, 163, 74, 0.9); }
|
|
194
|
+
.iuk-tile__action--has-alt:hover { background: var(--iuk-success); }
|
|
195
|
+
|
|
196
|
+
.iuk-tile__overlay {
|
|
197
|
+
position: absolute;
|
|
198
|
+
inset: 0;
|
|
199
|
+
z-index: 4;
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
align-items: center;
|
|
203
|
+
justify-content: center;
|
|
204
|
+
gap: 6px;
|
|
205
|
+
padding: 12px;
|
|
206
|
+
background: rgba(15, 23, 42, 0.68);
|
|
207
|
+
backdrop-filter: blur(3px);
|
|
208
|
+
color: #ffffff;
|
|
209
|
+
text-align: center;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.iuk-tile__overlay--error { background: rgba(127, 29, 29, 0.88); color: #fecaca; }
|
|
213
|
+
.iuk-tile__overlay--error p { margin: 0; font-size: 0.75rem; line-height: 1.35; }
|
|
214
|
+
.iuk-tile__status { margin: 0; font-size: 0.75rem; font-weight: 500; }
|
|
215
|
+
|
|
216
|
+
.iuk-tile__badge {
|
|
217
|
+
position: absolute;
|
|
218
|
+
bottom: 8px;
|
|
219
|
+
z-index: 3;
|
|
220
|
+
display: inline-flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 4px;
|
|
223
|
+
padding: 3px 7px;
|
|
224
|
+
border-radius: 9999px;
|
|
225
|
+
background: rgba(15, 23, 42, 0.74);
|
|
226
|
+
backdrop-filter: blur(4px);
|
|
227
|
+
color: #ffffff;
|
|
228
|
+
font-size: 0.6875rem;
|
|
229
|
+
font-weight: 600;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.iuk-tile__badge--success { left: 8px; background: rgba(22, 163, 74, 0.9); }
|
|
233
|
+
.iuk-tile__badge--color { right: 8px; }
|
|
234
|
+
.iuk-tile__badge--color code { font-size: 0.6875rem; color: #ffffff; }
|
|
235
|
+
.iuk-tile__color-dot { width: 10px; height: 10px; border-radius: 9999px; border: 1px solid rgba(255, 255, 255, 0.6); flex-shrink: 0; }
|
|
236
|
+
|
|
237
|
+
.iuk-tile__add-content { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 12px; text-align: center; color: var(--iuk-text-muted); }
|
|
238
|
+
.iuk-tile__add-content p { margin: 0; font-size: 0.8125rem; font-weight: 600; color: var(--iuk-text); }
|
|
239
|
+
.iuk-tile__add-btns { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
|
|
240
|
+
|
|
241
|
+
/* RTL positioning flips for tile badges, actions & checkbox */
|
|
242
|
+
[dir="rtl"] .iuk-tile__checkbox { left: auto; right: 8px; }
|
|
243
|
+
[dir="rtl"] .iuk-tile__action--remove { right: auto; left: 8px; }
|
|
244
|
+
[dir="rtl"] .iuk-tile__action--blur { right: auto; left: 40px; }
|
|
245
|
+
[dir="rtl"] .iuk-tile__action--alt { right: auto; left: 72px; }
|
|
246
|
+
[dir="rtl"] .iuk-tile__badge--success { left: auto; right: 8px; }
|
|
247
|
+
[dir="rtl"] .iuk-tile__badge--color { right: auto; left: 8px; }
|