Removes Font Awesome entirely
### Changes
- All ~1,000 icon call sites now import Lucide components directly from
`lucide-react` (`<PlayIcon className="..." />`); no FA packages, tokens,
or `fa*` identifiers remain anywhere.
- New `@storyteller/icons` lib holds the pieces Lucide doesn't ship:
`DynamicIcon` for runtime-chosen icons (`icon={loading ?
LoaderCircleIcon : SaveIcon}`), 11 brand logos (Discord, TikTok, ...) as
inlined CC BY 4.0 paths, and HD/SD quality badges drawn in Lucide's
stroke style.
- Global `.lucide { display: inline-block; width/height: 1.2em }` rule
in each app stylesheet: keeps icons font-relative like FA (1.2em
compensates Lucide's 24px-viewBox/20px-grid padding) and undoes Tailwind
preflight's `svg { display: block }`. Explicit `w-*`/`size-*` utilities
still win.
- FA-only props migrated: `spin` → `animate-spin`, FA size strings → em
values.
- Dropped `@fortawesome/*` + `@awesome.me` kit deps, both `.npmrc`
files, and
the FA rule in `shared-vite-config.ts` (now externalizes
`lucide-react`).
## Summary
New marketing page at /minimax-h3 where visitors can try MiniMax H3 for
free,
right on the page.
- Hero is a working promptbox, locked to MiniMax H3 with a "Free" badge
- Full settings available: aspect ratio, resolution, duration, sound,
start/end frames
- New users sign up in a modal when they hit Generate (Google or email),
then their prompt runs immediately without leaving the page
- Generated videos show up right below the promptbox, with a queue
notice and a CTA to keep creating in the app while they wait
- Example videos from MiniMax's announcement, self-hosted with
thumbnails, only load when played, credited to MiniMax
- Overview, highlights, prompt tips, and FAQ copy making the free offer
clear Website promptbox updated to match the current webapp promptbox
(reference deck, drag & drop, focus mode, same buttons)
- SEO, redirects, and URL aliases (/minimax, /hailuo) wired up
## Fixes & improvements
### Model picker
- Removed the "Seedance 2.5 - SOON" placeholder from the video model
picker; it was confusing now that Seedance 2.5 Preview is live.
### "Not all media files could be found" error
- Fixed generations failing when the same media file ended up twice in
one reference list (e.g. added as two references, or the same file
uploaded twice).
- Requests now automatically drop duplicate media references before
sending - applies to image, video, audio, mesh, and splat generation on
both the web app and the desktop app.
### Gallery picker UX
- Media already added to a slot is now greyed out with an "Added" badge
in the library picker, so the same file can't be added twice to the same
field.
- Applies per slot across the create pages (video, image, audio, 3D
object, world) on web and desktop - references, start frame, end frame,
video refs, audio refs, and multi-view slots.
- Reusing the same image across different slots (e.g. start *and* end
frame, or two 3D views) is still allowed.
- **Fixed: picking an audio reference on mobile opened the photo/video
picker.** iPhones don't have an audio library, so the browser fell back
to Photos - the only thing you could "attach as audio" was a video, and
it silently uploaded as an audio ref. Audio pickers now open the Files
app filtered to real audio files (MP3, WAV, M4A, FLAC…).
- **Non-audio files are now rejected with a clear error toast** instead
of being silently attached (also covers drag & drop and Android's "any
file" picker).
- **Fixed: limit warnings never appeared in the web app.** Errors like
"Total audio duration cannot exceed 15s" from shared components were
firing into a toast system the web app doesn't render. They're now
routed to the app's own toasts, so length/limit errors actually show up.
- Applies to the create video, create audio, and desktop reference deck
flows
- Create flow stored `inference_job_token` as the `character_token`, so
mentioning
a new character sent a bogus token - backend drops it and name-matches
the
raw `@name` against stale characters.
- Don't add pending characters to the mention store; they aren't
generatable yet.
- Keep the pending poll running after the modal closes and sync real
tokens into
the store once the character is active.
- Remove unused `addCharacter` store action.
- Same fix in all three copies: webapp, website, shared `promptbox` lib.
Users reported that after using **Recreate**, pressing the generate
arrow did nothing at all (no error, no card, no spinner). Root cause:
several failure paths gave zero feedback, so a rejected request looked
like a dead button or an outage.
### Changes
- **Show real error messages.** The API layer now keeps the server's
error message (e.g. "Image is required") instead of throwing it away, so
users see why a generation was rejected.
- **Toast on every failed submit.** Image and video pages (webapp +
website) now show an error toast when a generation fails to start.
Previously, most failures were recorded invisibly and never shown.
- **Out of credits feedback on the website.** 402 responses now show a
toast (webapp keeps its upgrade modal).
- **Network errors are announced** instead of failing silently.
- **No more silent submit blocks.** Cases that used to quietly ignore
the click now explain themselves:
- Model list still loading
- Model needs a starting image (website; also un-disabled the button so
clicking gives feedback)
- **Recreate model fallback is announced.** If the original generation's
model is no longer available, recreate now tells the user it switched to
the default model instead of silently swapping.
## Changes
- **Prompt character counters on web**: the website and webapp
promptboxes (image + video) now show a `123 / 2000` counter driven by
the API's `text_prompt_max_length`, matching the desktop app. It turns
red when over the limit and generation is blocked with a toast; typing
is never clamped. The webapp's fullscreen prompt editor shows the
counter too.
- **Seedance CJK waiver**: seedance video models normally use the API
limit, but the limit is waived (shows `∞`) while the prompt contains
Chinese characters. Applies on desktop and web via a shared
`effectivePromptMaxLength` helper.
- **API type parity**: added the new `model_creator` field to the web
`OmniGenImageModelInfo` / `OmniGenVideoModelInfo` types (desktop types
were already up to date).
- **Cleanup**: deleted the manual model-to-creator map
(`MODEL_TYPE_TO_CREATOR`, ~150 lines) now that creator info comes from
the API; lightbox model icons use the shared
`getCreatorIconPathForModelId` lookup like every other surface.
### New shared lib: `@storyteller/omni-gen`
- The `omni-gen-hooks.ts` file was copy-pasted between `artcraft-webapp`
and
`artcraft-website` and had drifted (website copy was missing outage
toasts,
carried a stale hardcoded display-name map, and had weaker icon
resolution).
- Extracted the newer (webapp) version into a new lib
`frontend/libs/omni-gen`
(generated via `nx g @nx/react:library`): singleton session cache,
`useOmniGenImageModels()` / `useOmniGenVideoModels()`, outage message
constants.
- App-local toasts are injected via `setOmniGenErrorNotifier()`, called
once in
each app's `main.tsx`, since the hooks are singleton-cache based and
each app
has its own toast component.
### Consolidated model metadata into `@storyteller/model-list`
- `getModelDescription()` / `getModelInfo()` (tagline + info-blurb
fallbacks)
moved to `ModelDescriptions.ts`; API values still always win.
- `getModelDisplayName()` gained an optional `fullName` param (API name
wins);
the app-local wrapper is gone.
- Both apps' local `getModelCreatorIconPath()` replaced with the
existing,
stronger `getCreatorIconPathForModelId()` (checks `ALL_MODELS_LIST`
first,
then prefix match). The loader's duplicate `CREATOR_BY_PREFIX` map now
reuses
the same shared `MODEL_ID_PREFIX_CREATORS` table.
### Dead code removed
- `modelReconciliation.ts`: deprecated `reconcile*()` helpers (never
called) and
the empty, retired `BACKEND_TO_TAURI_*` alias maps.
`buildModelsFromListing()`
dropped its alias-map indirection accordingly (backend ids == tauriIds
since
the 2026-07 id migration). `modelCreatorFromBackend()` moved inline into
the
loader, its only consumer.
- Six unused static `*_PAGE_MODEL_LIST` constants in
`model-selectors-for-pages.tsx`
(superseded by the `use*PageModelList` hooks in #1710). The two
`IMAGE_TO_3D_*`
constants stay: 3D models are not served by the omni backend.
- Untracked the accidentally committed
`apps/artcraft/tsconfig.tsbuildinfo` and
gitignored `*.tsbuildinfo`.
### Website-visible improvements (from adopting the newer hooks)
- Model outage toasts now appear on the website (previously
webapp-only).
- Current display names from the canonical map (previously-unmapped ids
like
`gpt_image_2` and Midjourney models rendered as raw ids).
- Model taglines/descriptions now render in the website model picker.
- Midjourney and SwitchX creator icons now resolve.
Swap the underline for a shared MarkerHighlight SVG stroke behind the word. Desktop reveal wipes left-to-right via clip-path instead of scaleX so the stroke is uncovered, not stretched.
…y CTA
- Manifesto: drop "3D" and animate a blue underline on "control"
- Feature cards: scroll-driven stacking deck (scale back + fade once
covered)
- Use Every Model: fill chips from canonical model list with correct
brand icons
- Use Every Model: alternating-direction marquees, highlight on hover
- Stop renting: italicize "renting"; make the good/bad comparison read
clearly
- Community CTA: "Join our community" banner restyled to fit site + not
steal focus from final CTA
- Renamed the "Supercharge Credits" button to **Use on Web** (globe
icon) in the hero and bottom CTA. Routes subscribers to the webapp
homepage, everyone else to `/pricing`.
- Removed the "Create image / Create video" links from both sections.
- Split Discord + GitHub into a separate "Open source &
community-driven" CTA (subtle container) above the "Ready to craft?"
CTA.
- Adds a new **Referrals** page in both the marketing site and the
webapp, gated by the new `referrals` feature flag
- Users can share two kinds of invite links:
- Their username link (`?u={username}`) - works out of the box
- Up to 5 custom codes (`?r={code}`) they can create and delete
themselves
- Site: link lives in the profile dropdown (desktop + mobile)
- Webapp: link lives in the sidebar under a new "Invite" section
- Users without the flag see nothing - no menu entry, and direct visits
redirect home
- added make video button to the create image page gallery card action
button row and the lightbox modal
- clicking it will redirect to the create video page and insert the
image as a reference automatically
- Lightbox now shows full media metadata: Aspect Ratio, Resolution,
Size, Duration, Audio, and Created (with timestamp).
- Added Author header at the top so the close button no longer overlaps
the Prompt copy button.
- Extracted aspect ratio / resolution / duration formatters into
`@storyteller/common` so the website and desktop app share them.