[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-93337":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":11,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":14,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":15,"rankGlobal":9,"rankLanguage":9,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":17,"hasPages":17,"topics":19,"createdAt":9,"pushedAt":9,"updatedAt":26,"readmeContent":27,"aiSummary":28,"trendingCount":14,"starSnapshotCount":14,"syncStatus":29,"lastSyncTime":30,"discoverSource":31},93337,"react-native-system-thumbnails","MarshallBear1\u002Freact-native-system-thumbnails","MarshallBear1","Native iOS and Android system thumbnails for React Native, with caching, cancellation, and icon fallbacks.",null,"Kotlin",137,10,7,0,43.12,"MIT License",false,"main",[20,21,22,23,24,25],"android","ios","new-architecture","react-native","thumbnails","turbo-module","2026-07-22 04:02:08","# react-native-system-thumbnails\n\nGenerate the thumbnail the operating system would show for a local file — or\nfall back to a useful file icon — through one typed React Native API.\n\n```tsx\nconst result = await generateThumbnail({\n  uri: pickedDocument.uri,\n  size: { width: 320, height: 240 },\n});\n\n\u002F\u002F result.uri is an app-owned file:\u002F\u002F URL ready for \u003CImage \u002F>\n```\n\nThe library uses Quick Look on iOS and native content, image, PDF, and media\nAPIs on Android. It does not download remote files and does not move file bytes\nthrough JavaScript.\n\n## Native demo\n\n| iOS                                                                                                                | Android                                                                                                                    |\n| ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |\n| ![iOS demo](https:\u002F\u002Fraw.githubusercontent.com\u002FMarshallBear1\u002Freact-native-system-thumbnails\u002Fmain\u002Fdocs\u002Fdemo-ios.jpg) | ![Android demo](https:\u002F\u002Fraw.githubusercontent.com\u002FMarshallBear1\u002Freact-native-system-thumbnails\u002Fmain\u002Fdocs\u002Fdemo-android.jpg) |\n\nThe demo only shows `PASS` after the generated thumbnail renders, the cache\nhits, strict fallback rejects an icon-only file, icon fallback decodes, and the\nmanaged cache files are removed.\n\n## Why this exists\n\nReact Native has good libraries for video frames and separate libraries for\nPDF pages. Apps that display mixed attachments still have to combine those\nlibraries, special-case Android `content:\u002F\u002F` URLs, and invent a fallback for\ndocuments the device cannot preview.\n\n`react-native-system-thumbnails` provides one result shape for images, PDFs,\nvideos, audio artwork, text and office documents, provider-backed content, and\nunknown files:\n\n- native previews when the OS can render them;\n- a deterministic file icon when it cannot;\n- bounded output dimensions;\n- PNG or JPEG output;\n- source-aware native caching;\n- cancellation with `AbortSignal`;\n- stable, typed errors.\n\n## Requirements\n\n- React Native's New Architecture\n- React Native 0.86 or newer\n- iOS 15.1 or newer\n- Android API 24 or newer\n\n## Install from GitHub Packages\n\nGitHub's npm registry requires an authenticated token, including when the\npackage is public. Create a classic personal access token with `read:packages`,\nthen configure the package scope in the consuming project or your user-level\n`.npmrc`:\n\n```ini\n@marshallbear1:registry=https:\u002F\u002Fnpm.pkg.github.com\n\u002F\u002Fnpm.pkg.github.com\u002F:_authToken=${GITHUB_PACKAGES_TOKEN}\n```\n\nInstall the package and, on iOS, update CocoaPods:\n\n```sh\nnpm install @marshallbear1\u002Freact-native-system-thumbnails\nnpx pod-install\n```\n\nNever commit the token itself. In CI, provide it through the runner's secret\nstore.\n\n## Usage\n\n```tsx\nimport { Image } from 'react-native';\nimport {\n  generateThumbnail,\n  type ThumbnailResult,\n} from '@marshallbear1\u002Freact-native-system-thumbnails';\n\nconst thumbnail: ThumbnailResult = await generateThumbnail({\n  uri: 'file:\u002F\u002F\u002Fpath\u002Fto\u002Freport.pdf',\n  size: { width: 256, height: 256 },\n  format: 'png',\n  cache: true,\n  fallback: 'icon',\n});\n\nexport function Preview() {\n  return (\n    \u003CImage\n      source={{ uri: thumbnail.uri }}\n      style={{ width: thumbnail.width, height: thumbnail.height }}\n    \u002F>\n  );\n}\n```\n\nThe result is:\n\n```ts\ntype ThumbnailResult = {\n  uri: string; \u002F\u002F file:\u002F\u002F URL in this app's cache\n  width: number; \u002F\u002F actual output pixel width\n  height: number; \u002F\u002F actual output pixel height\n  kind: 'thumbnail' | 'icon';\n  mimeType: 'image\u002Fpng' | 'image\u002Fjpeg';\n  fromCache: boolean;\n};\n```\n\n### Cancel a request\n\n```ts\nconst controller = new AbortController();\n\nconst pending = generateThumbnail({\n  uri,\n  signal: controller.signal,\n});\n\ncontroller.abort();\nawait pending; \u002F\u002F rejects with ThumbnailError whose code is CANCELLED\n```\n\n### Fail instead of returning an icon\n\n```ts\nawait generateThumbnail({\n  uri,\n  fallback: 'error',\n});\n```\n\n### Clear managed thumbnails\n\n```ts\nimport { clearThumbnailCache } from '@marshallbear1\u002Freact-native-system-thumbnails';\n\nconst deletedFileCount = await clearThumbnailCache();\n```\n\nOnly files inside the library's own cache directory are ever removed.\n\n## API\n\n### `generateThumbnail(options)`\n\n| Option     | Type                | Default     | Notes                                                                                             |\n| ---------- | ------------------- | ----------- | ------------------------------------------------------------------------------------------------- |\n| `uri`      | `string`            | required    | Absolute path, `file:\u002F\u002F`, iOS `bundle:\u002F\u002F`, Android `content:\u002F\u002F`, or Android `android.resource:\u002F\u002F` |\n| `size`     | `{ width, height }` | `256 × 256` | Each dimension must be an integer from 1 through 4096                                             |\n| `format`   | `'png' \\| 'jpeg'`   | `'png'`     | The returned MIME type matches the encoding                                                       |\n| `quality`  | `number`            | `0.9`       | From 0 through 1; used by JPEG only                                                               |\n| `cache`    | `boolean`           | `true`      | Reuse output when source metadata and options match                                               |\n| `fallback` | `'icon' \\| 'error'` | `'icon'`    | Whether an unpreviewable file becomes an icon or an error                                         |\n| `signal`   | `AbortSignal`       | —           | Cancels the matching native request                                                               |\n\nRemote `http:\u002F\u002F` and `https:\u002F\u002F` URLs are intentionally rejected. Download\nremote content with the app's existing networking stack first; this keeps the\nlibrary's permissions, caching, authentication, and privacy behavior\npredictable.\n\n### `ThumbnailError`\n\nEvery failure is exposed as a `ThumbnailError` with one stable `code`:\n\n- `INVALID_ARGUMENT`\n- `UNSUPPORTED_URI`\n- `NOT_FOUND`\n- `PERMISSION_DENIED`\n- `CANCELLED`\n- `GENERATION_FAILED`\n- `CACHE_ERROR`\n\n## Platform behavior\n\n| Capability              | iOS                       | Android                                     |\n| ----------------------- | ------------------------- | ------------------------------------------- |\n| Local paths \u002F `file:\u002F\u002F` | Quick Look                | Native decoders                             |\n| Bundled fixture \u002F asset | `bundle:\u002F\u002Ffilename.ext`   | `android.resource:\u002F\u002F`                       |\n| Provider-backed URI     | Security-scoped file URL  | `content:\u002F\u002F` \u002F `ContentResolver`            |\n| Images                  | Quick Look                | `loadThumbnail` or sampled image decode     |\n| PDF                     | Quick Look                | `loadThumbnail` or first-page `PdfRenderer` |\n| Video                   | Quick Look                | `loadThumbnail` or `MediaMetadataRetriever` |\n| Audio artwork           | Quick Look                | Provider thumbnail or embedded artwork      |\n| Text \u002F office documents | Quick Look when supported | Provider thumbnail, otherwise icon          |\n| Unknown format          | File icon                 | File icon                                   |\n\nAn icon is a successful result with `kind: 'icon'`; it is not mislabeled as a\nreal preview. OS versions and document providers can legitimately produce\ndifferent artwork, so consumers should depend on dimensions and `kind`, not\npixel-identical output.\n\n## How this repository proves the native module works\n\nThe test strategy deliberately goes beyond mocking JavaScript:\n\n1. TypeScript\u002FJest tests cover public validation, option defaults,\n   cancellation, native error mapping, and result validation.\n2. Android Robolectric tests cover cache keys, bounded image rendering, file\n   icon fallback, cache reuse, and cancellation-sensitive paths.\n3. CocoaPods\u002FGradle and full example-app builds compile the actual iOS Quick\n   Look and Android implementations against generated React Native 0.86 code.\n4. The example app runs the TurboModule on bundled fixtures, renders the\n   returned thumbnail, verifies a cache hit, proves strict fallback rejection,\n   decodes an icon fallback, clears the managed files, and only then exposes a\n   stable `PASS` state.\n5. A Maestro flow drives that self-test on iOS Simulator and Android Emulator.\n6. CI builds both native apps and checks the packed package, so autolinking,\n   Codegen, CocoaPods, Gradle, and published-file omissions are caught.\n7. The release workflow publishes tags to GitHub Packages and verifies the\n   registry metadata after publication.\n\nSee [CONTRIBUTING.md](.\u002FCONTRIBUTING.md) for local commands.\n\n## Design constraints\n\n- Generated files are app-cache data. The OS may remove them at any time.\n- Inputs must already be readable by the app. This package requests no broad\n  storage or photo-library permission.\n- Android document providers decide whether they can supply their own preview.\n- A thumbnail is presentation data, not proof that a file is safe. Validate\n  uploads independently on the server.\n\n## License\n\nMIT\n","这是一个为 React Native 应用提供原生系统文件缩略图生成能力的库，支持 iOS 和 Android 平台。它调用系统级 API（iOS 的 Quick Look、Android 的 ContentResolver\u002F媒体框架）直接生成本地文件的预览图，并在无法预览时自动回退到语义化文件图标；具备内存与磁盘双层缓存、AbortSignal 可取消、严格尺寸约束、PNG\u002FJPEG 输出及类型安全错误处理等特性。适用于需要统一展示文档、PDF、视频、音频、图片等混合附件缩略图的移动应用，尤其适合基于 React Native 新架构构建的跨平台文件管理或消息类应用。",2,"2026-07-16 02:30:09","CREATED_QUERY"]