2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30
2026-07-18 01:34:52 +05:30

Photo Gallery Web SDK

A reusable, macOS Photosstyle photo gallery for React / Next.js — packaged as an installable SDK (@photo-gallery/sdk). Think of it as the shadcn of photo galleries: drop in one component and get a nearpixel recreation of the macOS Photos app, in light and dark mode, fully responsive from ultrawide desktops down to a Galaxy Fold.

Original implementation. Not affiliated with, or endorsed by, Apple. All icons and assets are drawn from scratch.

import { PhotoGallery } from '@photo-gallery/sdk';
import '@photo-gallery/sdk/styles.css';

export default function App() {
  return <PhotoGallery photos={myPhotos} theme="system" features={{ editor: true, map: true }} />;
}

What's implemented

Every capability below is optional via features={{…}} flags, so a consuming app enables only what it needs. The full, living checklist is in docs/ROADMAP.md.

UI, library & albums

Area Details
Faithful macOS UI Sidebar, Library (Years / Months / All), Collections, Map, People & Pets, Documents, Recently Deleted (locked), Shared Albums, Activity — light / dark / semidark, with macOS "glass" vibrancy (backdropblur menus + sidebar)
Library Adaptive responsive grid, zoom, lazy loading, day/month/year grouping; click toggles select, Select All ↔ Unselect All
Albums Create · rename · duplicate · delete · nested folders · covers · Copy to / Move to Album
Smart albums Screenshots / Videos / Favourites / Recently Saved / RAW / Live / Panoramas / Documents (OCR), recomputed live
Map Free OSM / Esri tiles (no key). macOS thumbnail pins with count badges + clustering; tap a pin → that location's photos dategrouped. Grid tab = Memoriesstyle mosaic (autosliding hero + irregular tiles)
Import / capture Draganddrop & filepicker (validated types); custom camera (photo/video, front/back, grid, live annotation + measurement, geolocation, auto objectdetect)
Sharing Share a photo / selection / oneormore albums → copyable link + download; Shared Albums + Activity feed
Recycle bin 30day trash; password lock (Web Crypto, devicelocal) with lock/unlock states + Settings

AI — all free & inbrowser (no API key), lazyloaded, pluggable

Capability Engine
Object detection → tags, search, Objects browser TensorFlow.js COCOSSD
Face clustering → People (rename); pet detection → Pets faceapi.js (128D descriptors)
OCR → searchable text + Documents album tesseract.js (confidencefiltered)
Semantic search — "show me snowy mountains" CLIP via transformers.js
Remove Background @imgly (inbrowser WASM)
Generative edits (restore / colorize / sky / prompt) 🔑 Gemini (serverproxied; needs a key)

Editors

Area Details
Photo editor Adjust (13 sliders), 10 filters, crop (free + fixedratio) & straighten, markup (rect/oval/line/arrow/doublearrow measurement/text/freehand), AI tab — Save or Save as Copy, Cancelconfirm
Video editor Adjust · Filters · Trim · Markup · Overlay (composite an image) · Audio (mute + add music) — exports a new clip inbrowser (canvas + MediaRecorder + WebAudio, no ffmpeg); Save / Save as Copy

Platform

Area Details
Backend Pluggable StorageAdapter — zeroconfig localStorage/IndexedDB default, or Supabase (Postgres + Storage) adapter included
Search Name / place / tag / object / OCR text / semantic — blended and relevanceranked
Theming accentColor + borderRadius props; Light / Dark / SemiDark / System
A11y / responsive Keyboard nav, focus traps, reducedmotion, ARIA; works down to ~280 px
Security Perrequest nonce CSP, security headers, filename sanitisation, URLscheme allowlist, serveronly API keys

🗂 Monorepo layout

advance-photo-gallery-web-sdk/
├── packages/
│   └── photo-sdk/          # @photo-gallery/sdk — the reusable product
│       ├── src/
│       │   ├── components/ # PhotoGallery, Sidebar, views, editor, lightbox …
│       │   ├── store/      # Zustand store + selectors + React context
│       │   ├── adapters/   # StorageAdapter interface + localStorage default
│       │   ├── ai/         # AIProvider interface (pluggable)
│       │   ├── lib/        # classify, smart albums, grouping, edits, media, format
│       │   ├── icons/      # original SFstyle SVG icon set
│       │   └── styles/     # sdk.css (selfcontained, no Tailwind needed)
│       └── dist/           # built ESM + CJS + d.ts + styles.css (after `build:sdk`)
└── apps/
    └── web/                # Next.js demo: landing page + /gallery

🚀 Run it locally

Prerequisites: Node ≥ 20 and pnpm (corepack enable ships it with Node).

# 1. install
pnpm install

# 2. start the demo (Next.js)
pnpm dev

# 3. open
#    http://localhost:3000          → landing page
#    http://localhost:3000/gallery  → the full gallery (40+ sample photos)

The demo runs with no backend and no API keys — sample photos come from the free Picsum service and the map uses free OpenStreetMap/Esri tiles.

Other scripts

pnpm build:sdk     # build the publishable SDK (dist/: ESM, CJS, types, styles.css)
pnpm build         # build SDK + production Next.js demo
pnpm start         # serve the production demo
pnpm typecheck     # type-check every package
pnpm format        # prettier

📦 Using the SDK in your own app

pnpm add @photo-gallery/sdk   # (after publishing, or via workspace link)
'use client';
import { PhotoGallery, type MediaItem } from '@photo-gallery/sdk';
import '@photo-gallery/sdk/styles.css';

const photos = [
  { src: '/img/beach.jpg', name: 'Beach.jpg', takenAt: Date.now(), tags: ['beach'] },
];

export default function Gallery() {
  return (
    <div style={{ position: 'fixed', inset: 0 }}>
      <PhotoGallery photos={photos} theme="system" />
    </div>
  );
}

Next.js note: mount the gallery in a Client Component (it uses browser APIs). The demo gates it behind a mount check in apps/web/src/components/GalleryClient.tsx. Import leaflet/dist/leaflet.css once (e.g. in your root layout) if you use the Map view.

Key props

Prop Type Default Description
photos MediaItem[] | { src, … }[] [] Initial library; loose inputs are normalised + autoclassified
albums Album[] [] Initial user albums
adapter StorageAdapter localStorage Persistence backend (swap for S3/DB/REST)
ai AIProvider | boolean false Object/face/caption/search/generative provider
theme 'system' | 'light' | 'dark' 'system' Appearance
accentColor string #0a84ff Accent colour
borderRadius number 10 Base corner radius (px) for all rounded UI
themeTokens ThemeTokens Pertheme background / gradient / opacity / sidebar colour + radius overrides
features Partial<GalleryFeatures> all on Toggle editor / camera / ai / map / import / export / sharing
showWindowChrome boolean false Render the macOS trafficlight title bar

🎨 Customization (props or env)

Every colour, gradient, opacity, sidebar radius, and feature flag is configurable. Pass themeTokens / features as props, or — in the demo — set NEXT_PUBLIC_APG_* env vars and the gallery reconfigures with no code changes. Unset values fall back to the builtin light / dark / semidark themes. Full list + examples: docs/ENV.md.

<PhotoGallery
  theme="dark"
  accentColor="#ff375f"
  themeTokens={{ bgDark: 'linear-gradient(165deg,#141018,#241426)', sidebarBgDark: 'rgba(30,20,38,0.6)', sidebarRadius: 18 }}
  features={{ camera: false, sharing: false }}
/>

Custom storage backend

import type { StorageAdapter } from '@photo-gallery/sdk';

const myApiAdapter: StorageAdapter = {
  name: 'rest',
  async load() { return (await fetch('/api/library')).json(); },
  async save(state) { await fetch('/api/library', { method: 'PUT', body: JSON.stringify(state) }); },
};

<PhotoGallery adapter={myApiAdapter} />;

Custom AI provider

Every method is optional and the UI degrades gracefully. Implement object detection clientside (TensorFlow.js), or proxy to a cloud model:

import type { AIProvider } from '@photo-gallery/sdk';

const provider: AIProvider = {
  name: 'coco-ssd',
  async detectObjects(item, image) { /* return DetectedObject[] */ return []; },
};

<PhotoGallery ai={provider} />;

🔒 Security

  • Strict ContentSecurityPolicy + XFrameOptions: DENY, nosniff, ReferrerPolicy, PermissionsPolicy (see apps/web/next.config.mjs).
  • No eval, no dangerouslySetInnerHTML anywhere in the SDK.
  • Import allowlist — only image/video MIME types and extensions are accepted.
  • Filename sanitisation on export (pathtraversal guard).
  • Defensive persistence — persisted state is shapevalidated before use.

🛣 Roadmap (Phase 2+)

Live now (all free & inbrowser unless marked 🔑):

  • Object detection (TF.js COCOSSD) → tags, search, Objects browser.
  • Face clustering → People (rename) + pet detection → Pets (faceapi.js).
  • OCR → searchable text + Documents album (tesseract.js, confidencefiltered).
  • Semantic search — naturallanguage "looks like" queries (CLIP via transformers.js).
  • Remove Background (@imgly, inbrowser WASM).
  • Supabase backend adapter (Postgres + Storage), alongside the zeroconfig localStorage default.
  • Full video editor (trim / filters / markup / image overlay / mute + music) exporting a new clip inbrowser.
  • Sharing (links + Shared Albums + Activity), passwordlocked Recently Deleted, custom camera.
  • Generative AI editor 🔑 (Gemini) — the editor's AI tab (Restore, Colorize, Replace Sky, freeform prompts). The key stays serverside only: the browser calls the app's own /api/ai/edit route, which proxies to Gemini.

Enabling the generative editor

  1. Put your key in apps/web/.env.local (gitignored — never committed):
    GEMINI_API_KEY=your-key
    GEMINI_IMAGE_MODEL=gemini-2.5-flash-image
    
  2. Restart pnpm dev.

Note: Gemini image generation requires a billingenabled / paid plan. A freetier key authenticates but returns HTTP 429 ("check your plan and billing") for image output. The editor handles this gracefully (shows the error, no crash); it produces images as soon as the key's project has imagegeneration quota.

Still planned / not yet implemented:

  • Auth & multiuser sharing (real recipients, permissions). Sharing today creates devicelocal links.
  • Captions (transformers.js BLIP) and a firstclass Hugging Face Inference provider option.
  • Map heatmaps / trips / reversegeocode place names; true grid virtualization for millions of items.
  • Serverside 30day purge (pg_cron), Storage blob deletion on permanentdelete.
  • Storybook + API docs, Docker/K8s, automated CI.

🧱 Tech

Next.js · React · TypeScript · Zustand · Framer Motion · Leaflet · TanStack Virtual · pnpm workspaces · tsup. The SDK ships its own selfcontained stylesheet — Tailwind is not required by consumers.

S
Description
No description provided
Readme 1.3 MiB
Languages
TypeScript 88.5%
CSS 11.3%
JavaScript 0.2%