Fujimod
Try your camera firmware in the browser and build Fujimod SD boot files.
A daily companion for sharing and remembering your life story.
LifestyleTypeScript · CSSGitHub @dbenson2946-altConnection unstableOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Repository for roots-and-echoes
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A daily-use life-story companion for older adults — Story Time (timeline), Photos & Family (with a family tree), Music, Books, Art, and Games — built with large text, high contrast, and voice input throughout. See the full plan for the "why" behind these choices.Read the full README ↗A daily-use life-story companion for older adults — Story Time (timeline), Photos & Family (with a family tree), Music, Books, Art, and Games — built with large text, high contrast, and voice input throughout. See the full plan for the "why" behind these choices.
The app uses a bespoke, jewel-tone keepsake-book visual style called Warm Keepsake: serif display type (Playfair Display for headings, Fraunces for the wordmark's ampersand and italics) paired with the existing accessible sans-serif body text; a single universal rust accent (--color-primary) on every primary button and highlight; a hand-drawn line-icon system (src/components/Icon.tsx) replacing all emoji; a circular double-ring "medallion" icon badge (src/components/Medallion.tsx) marking which mode a piece of content belongs to; and a signature pure-CSS "corner-tick" ornament — four small bracket marks at the corners of every card, built entirely from layered CSS gradients (see .tile in src/app/globals.css) with no extra markup — applied consistently to every tile, list row, photo, and form throughout the app, including the family tree diagram and all four games. All of the app's original accessibility rules (20px+ base type with the A / A+ / A++ control, 56px+ touch targets, a warm high-contrast fixed-light theme, visible focus rings, read-aloud everywhere) are unchanged — this was a visual restyle only, layered on top of the same accessible foundation.
supabase/schema.sql, then
supabase/migration_2_placeholder_fields.sql, then supabase/storage.sql..env.example to .env.local and fill in the three values from your
Supabase project's Settings → API page (see comments in that file).npm install
npm run dev
Open http://localhost:3000 — you'll land on a real sign-in screen: enter an email address and click the link Supabase emails you (no password). The very first sign-in walks you through a one-time onboarding step (senior or caregiver, name, and — for a caregiver — who invited them). There's no seed data; every account starts empty and everything you add is really saved.
src/
app/ routes (Next.js App Router) — one folder per page
actions.ts all server actions (every write to data goes through here)
auth/callback/ finishes the magic-link sign-in (exchanges the emailed code
for a session)
onboarding/ first-time profile setup (senior vs. caregiver, name)
components/ shared UI, including the voice/audio capture widgets
components/games/ the four Games mode components (jigsaw, photo quiz, song quiz, art quiz)
lib/
types.ts the whole data model, typed
store.ts the real Supabase data layer — every function is an
async `supabase.from(...)` query, subject to the
row-level security policies in supabase/*.sql
storage.ts Supabase Storage uploads + signed playback URLs (song
audio, the custom voice sample)
session.ts resolves the signed-in Supabase Auth user + which
senior's story a caregiver is currently helping with
supabase/ browser & server Supabase clients
proxy.ts refreshes the Supabase session cookie on every request
supabase/
schema.sql the base Postgres schema + row-level security
migration_2_placeholder_fields.sql placeholder-tile columns + email-based
caregiver invites (run after schema.sql)
storage.sql the `audio`/`photos` Storage buckets + policies
This app is deployed against a real Supabase project — there is no mock data layer to swap out. What's actually needed to run it somewhere real:
supabase/ run once,
in order (see "Try it locally" above). Auth is Supabase's built-in
passwordless magic-link email — nothing else to configure.NEXT_PUBLIC_SUPABASE_URL,
NEXT_PUBLIC_SUPABASE_ANON_KEY — a publishable-type key, safe to expose to
the browser — and SITE_URL, the app's own deployed URL, used
to build the magic-link redirect) set both locally (.env.local) and in
the hosting provider's project settings.vercel from the CLI), with the same three environment variables
added in the Vercel dashboard.The Supabase secret/service-role key is never used anywhere in this app — every query runs as the signed-in user through Row Level Security, so the publishable key is all the running app ever needs.
Story Time's rotating prompts (PROMPTS_BY_CATEGORY in src/lib/store.ts) are a
curated, hand-written library — about a dozen original prompts per life-area
topic (Childhood, Family, Work, Travel, Music, Love, Milestones, Other),
matching the same MemoryCategory a saved memory is filed under — rather
than prompts fetched live from the web or generated on the fly by an AI
model at request time. That was a deliberate choice, not a shortcut:
The library is easy to grow later exactly like any other file in the repo —
add strings to the relevant array in PROMPTS_BY_CATEGORY and redeploy — so
if it starts feeling repetitive, extending it stays a small, low-risk change
rather than standing up a live content pipeline.
Song audio (via "Add a song" or the upload box on a song's page), the
custom voice sample (Settings), and photos / art-piece images (via
"Add a photo", "Add a piece of art", or the "Upload picture" box shown on
any photo/art piece that doesn't have one yet) are all real uploads to
private Supabase Storage buckets (audio and photos); every read gets a
short-lived signed URL generated fresh on each request (see
src/lib/storage.ts), so files are durable across restarts and deploys, and
never publicly reachable.
Photo/art image uploads go straight from the browser to Supabase Storage
using a one-time signed upload token (createImageUploadTicket in
src/lib/storage.ts), instead of through a Server Action — Vercel caps
every function's request body at a hard, non-configurable 4.5MB, and a real
phone photo routinely exceeds that. Song audio and the custom voice sample
are usually smaller and still go through a Server Action (with Next's own
1MB default raised to 4MB in next.config.ts), which carries the same risk
for an unusually large file — a candidate for the same signed-URL treatment
later if it comes up in practice.
A photo or art piece with no image yet still falls back to a labeled color
tile (photos.storage_path / art_pieces.image_path are nullable — the
image is optional, not required) — the same tile shown throughout the app
before real uploads were wired up.
One thing is still deliberately placeholder, flagged as follow-up work rather than attempted here:
Everything else (memories, people, the family tree, song lyrics/notes, books, art pieces, caregiver access) is fully saved to Postgres and reflected across the app right away.
The Photo Jigsaw, "Who's in the Photo?", and "Guess the Artwork" games use
the real uploaded image (its signed URL) when a photo or art piece has one,
and fall back to the same generated placeholder tile
(placeholderPhotoDataUri in lib/ui.ts) otherwise.
Real distinct "AI voices" and real voice cloning both need a paid third-party provider (e.g. ElevenLabs) with its own API key — not something this app can do on its own, and not something to fake with a fabricated integration. Until one is connected:
lib/voicePresets.ts.audio Storage bucket (see "Photos &
audio" above) and selecting it is fully functional end to end, but actual
playback still uses one of the browser's own voices as a clearly-labeled
stand-in, not a real clone of the uploaded voice.Profile.voicePreference
/ Profile.customVoice in lib/types.ts), not in a per-browser cookie like
text size — so it looks and sounds the same for the senior and any
caregiver helping them, on any device.To wire in real AI voices / real voice cloning: call the provider's API
from submitCustomVoice (and a new preset-preview/synthesis path) instead of
SpeechSynthesisUtterance, store the provider's returned voice ID on
custom_voices.sample_audio_path or a new column (see supabase/schema.sql),
and stream back the generated audio for playback.
.btn-lg minimum touch target.VoiceTextField, not a plain
<textarea>, so typing is never the only option.AppHeader so Home / text size / log out stay consistent everywhere.The complete product plan (personas, all four modes, phased roadmap, open
decisions) lives in the "Roots and Echoes" Claude project as plan.md.