Commit Graph
136 Commits
Author SHA1 Message Date
Tanish Manku 46c0bcb019 Merge services/main: catch the design lineage up to the split repo
The design branch forked from the old monorepo main at f3442f3146;
main then advanced through #1910 (last-viewed badge), #1911 (iOS
upload fix — uploading queue stores previewUrl, not File), #1912,
#1699 (keybinds), the auth-page video update (976f2ae757, Vimeo
iframe → muted <video>), and the modal freeze fix (ed57c6de47,
auto-merged) before the artcraft/artcraft-services split.

Seven files conflicted where main's functional work crossed the
branch's #1913/#1916 webapp restyling. Resolution rule: main's
functionality, the design branch's styling —

- auth-showcase: main's <video> element, squared corners kept
- ImagePromptRow (webapp + libs): main's previewUrl refactor; kept
  square corners/hairline borders. The branch's `spin` prop was a
  latent bug (raw lucide icons have no such prop — no animation and
  a React DOM warning), so main's animate-spin class wins.
- GalleryCard/GalleryRow: main's lastViewed ring + badge woven into
  the branch's squared-corner classes (badge pill squared to match)
- old-site app.tsx: union of both new routes (landingp0 + creator)
- TimelineClipRow: main's enableClipTransition undo-step flow
  superseded the branch's setClipTransitionEasing(DEFAULT_EASING)

artcraft-website-next is untouched by the incoming side. Webapp
typechecks clean after the follow-up spinner fixes.

Claude-Session: https://claude.ai/code/session_01KpbGFYxEXuaYU9J75vTK9j
2026-09-11 12:27:13 -07:00
Brandon ThomasandHanashi 7fb81500a7 rename CLAUDE.md to AGENTS.md (#1940)
Co-authored-by: Hanashi <shared2@storyteller.ai>
2026-09-10 21:55:17 -04:00
Tanish Mankuandbflatastic e3f48033f9 Keybind settings: New menu and revamped keybind support (#1699)
## Summary

Adds a unified, remappable keyboard-shortcut system
(`@storyteller/keybinds`) across the webapp and desktop app, replacing
the scattered hardcoded `keydown` listeners in the 3D editor, 2D editor,
and moodboard with one registry, two presets (**Gamer** = today's
defaults, **Blender-like**), a Settings → Keybinds pane, and a hold-Ctrl
/ pinnable cheatsheet on every editor surface.

This PR was approved in June and then sat behind ~195 commits on `main`
(timeline, build/record modes, lucide migration). It has been merged
with current `main` and extended to cover the features that landed
since. **Nothing changes by default** — the Gamer preset reproduces
every existing key exactly.

## Architecture

- **Registry of `ActionDef`s** keyed by surface-namespaced ids
(`pagescene.timeline.playPause`), bound to physical `event.code`.
Resolution: user override → preset delta → base bindings. Persisted in
the `artcraft-keybinds` zustand store.
- **Five surfaces**: `global`, `pagescene` (3D), `pagedraw` (2D),
`moodboard` (canvas), `moodboard-grid` (board library / inspector /
presentation).
- **Declared availability** — `ActionDef.when(ctx)` gates decide whether
a binding is live for the current editor state (`sceneMode`, `encoding`,
`timelineExpanded`, `timelineSelection`, …). An unavailable binding
falls through *without consuming the event*, which is what lets Space /
arrows / Delete / Escape be shared by context-exclusive actions. A
sampled-context checker (`actionsCoAvailable`) proves the exclusivity in
tests and drives the conflict UI in Settings.
- **Global surface is a pure trigger layer**: features register behavior
with `useGlobalAction(id, handler)`; a window capture-phase dispatcher
fires above the modal key-swallow, and an unregistered binding is inert
per host. First inhabitant: Ctrl/Cmd+B sidebar. This is the intended
home for the upcoming privacy-blur toggle (one `ActionDef` + one binding
+ one `useGlobalAction` call).
- Cross-surface conflict detection (global ↔ everything, both
directions); preset-integrity tests guard every preset against
same-surface collisions.
- **Video editor keeps its own keybinds system** — deliberate non-goal.

## What's in it

**3D editor (pagescene)**
- Timeline actions: Space play/pause, ←/→ frame step or keyframe/strip
nudge, Home/End, `I` add keyframe (Blender parity), Shift+T expand,
Delete/Backspace on a valid timeline selection (exact complement of
scene-object delete).
- Record mode: Tab toggles build/record, `C` capture still, `R` record
video (bare letters — Firefox eats Ctrl+Shift+S/E), Escape cancels an
in-flight encode (overlay Cancel button too). Record mode is immutable:
all scene-mutating actions gate off, Escape no longer drops out of the
render cam.
- **Per-edit timeline undo** — every timeline edit is one Ctrl+Z step
(`TimelineEditAction` with whole-timeline snapshots; drags and the
easing popover coalesce into single steps). Save now only moves the
Cancel baseline; Cancel is itself undoable.
- The old "Shortcuts" modal is replaced by the live cheatsheet, pinnable
from the same button.
- Dead code removed: `MouseControls.onkeydown`, `OnboardingHelper`, the
hardcoded `Help` view, three unused pagedraw hotkey hooks.

**2D editor (pagedraw)**
- Tool hotkeys 1–5 with live key badges on the toolbar icons; the shape
key cycles rectangle → circle → triangle (submenu-aware).
- Ctrl+= / Ctrl+- (and numpad) resize the active tool. The eraser now
has its own size (was sharing the brush's) and its own slider popout;
also fixes the eraser button never highlighting.

**Moodboard**
- Canvas keys unchanged (now registry-backed), plus Ctrl+0 reset
viewport.
- Grid: rate 0–5 / `P` pick / Delete / Escape, inspector and
presentation ←/→/Space/Esc — all rebindable. Escape now closes the
inspector; arrows no longer navigate while typing in the tag field.

**Settings & cheatsheet**
- Settings → Keybinds: preset picker, name/key search, per-action rebind
with conflict warnings, per-section reset, and a bottom "danger zone"
reset-all with a two-step confirm (2 s lockout + visible 5 s
auto-disarm).
- Cheatsheet: hold Ctrl (⌘) 3 s to peek; **General → "Keep shortcut
cheatsheet open"** makes it stick until Esc / click-outside; the 3D
"Shortcuts" button pins it.

## Testing

- `@storyteller/keybinds` unit tests (29): resolution layering,
overrides, per-surface/global reset, conflict detection incl.
global↔surface, matcher semantics, and preset integrity across all
presets.
- Builds green: `keybinds`, `ui-pagescene`, `ui-pagedraw`,
`ui-moodboard`, `ui-settings-modal`, `artcraft-webapp`, `artcraft`
(desktop).
- Manual QA pass completed on the webapp for every area above (settings
pane, cheatsheets, Blender modal transforms, timeline/record keys,
pagedraw toolbar, global surface, reset UX, pinned cheatsheet, timeline
undo, Escape contexts).

## Notes for review

- The keybinds store is unversioned-migration-free on purpose — the PR
was never merged, so there's no persisted shape to migrate from.
- Space-hold pan (moodboard) and held WASD camera movement stay as
gestures, resolved from the same store but outside the discrete-action
dispatch.

---------

Co-authored-by: bflatastic <bombayapirat@gmail.com>
2026-08-25 00:34:26 -07:00
BFlat d2ad5820e0 Core UI kit for artcraft-website-next (#1914) 2026-08-21 13:05:39 +07:00
BFlat b36e4fd27b Refactor: replace fontawesome icons with lucide icons (#1873)
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`).
2026-08-10 02:04:45 +07:00
hanashi ea4563c1a8 Frontend dev exp (#1866) 2026-08-08 19:17:34 -04:00
Tanish MankuandBFlat a1435df07d 3D: Animations support + UI (#1856)
Co-authored-by: BFlat <bombayapirat@gmail.com>
2026-08-06 22:48:35 -07:00
hanashi 17c12836ad seedream 5 plus (#1822) 2026-07-30 18:25:19 -04:00
BFlat 9f28c525ec Webapp/desktop: batch download video results, refresh credits after failed generations (#1801)
## Changes

**Batch download**
- Add multi-select mode to the create-video results feed (webapp +
desktop) with a "Select" toggle in the topbar, checkboxes on completed
items, and a floating download bar
- Webapp: "Download" bundles selected videos into a single .zip
client-side (new jszip dependency, concurrent fetches, progress and
partial-failure toasts)
- Desktop: "Download" prompts for a folder and saves each selected video
as its own file via native Tauri save, with progress toasts
- Library: add a Download (zip) button to the existing bulk selection
bar
- Shared `ui-generation-list` lib gains a selection store, select
toggle, action bar, and optional selection props on the grid/list views
(existing hosts unaffected)

**Credits refresh on failed generations**
- Webapp: dispatch a credits refresh when the task queue or generation
feed observes a newly failed job (immediately and again after 2.5s so
server-side refunds settle)
- Desktop: also refresh credits when the 5s task poll discovers a
failure that the push event missed, and listen for `credits-change` in
the TopBar

**UI fixes**
- Selection download bar always sits above the promptbox regardless of
its size
- Mobile Generate/History header no longer overlaps the gallery toggles
(flex layout instead of absolute positioning)
- Reference deck top-aligns in the expanded prompt box instead of
centering
- "Play videos with sound" setting now defaults to on
2026-07-23 19:54:12 +07:00
BFlat 185bf835a1 Desktop: tags in the gallery modal (#1794)
- Tags now work in the desktop app - no separate build, it all comes
through the shared gallery/lightbox components
- Open any item in the gallery → edit its tags in the details panel
(same chip editor as the webapp, with autocomplete)
- New "Tags" section in the gallery sidebar: your 5 most-used tags + an
"All tags" entry
- Click a tag to see all media with that tag in the normal grid
- "All tags" opens a word cloud (bigger = used more), click a word to
jump in
- Webapp sidebar now also caps at 5 tags with a "See all tags" link
- The tag editor is now one shared component used by both apps instead
of two copies
2026-07-17 20:44:49 +07:00
Brandon Thomas 7dffc5fb5c list meshes and splats (#1774) 2026-07-12 09:32:35 +07:00
BFlat bec8c7ede4 Add Create Audio pages (Suno + Seed Audio) with waveform gallery playback to desktop and webapp (#1748)
Frontend for the new omni audio API
(`/v1/omni_gen/{models,generate,cost}/audio`).

### New pages
- **Webapp**: `/create-audio` page (desktop layout + mobile
Generate/History tabs with settings drawers), added to sidebar.
- **Desktop**: new `AUDIO` tab ("Create Audio") with `PromptBoxAudio`,
registered in the app menu / TopBar. Audio enqueues over HTTP and polls
the jobs feed (Tauri task queue untouched).

### Models & promptbox
- Suno Music / Remix / Sounds / Sample + Seed Audio 1.0, fetched live
from `GET /v1/omni_gen/models/audio` with per-model capability gating.
- Always-visible **style prompt** row (Suno tags), instrumental /
keep-lyrics / loop toggles, BPM + musical key popover (Suno Sounds),
sample rate + speed/volume/pitch tuning popover (Seed Audio).
- Audio/image reference uploads with server rules mirrored client-side
(remix/sample require exactly 1 audio track; Seed Audio can't combine
audio + image refs).
- One request can return multiple job tokens (Suno multi-clip) - every
token becomes its own pending card.

### Audio playback in galleries
- New **`@storyteller/ui-audio-player`** lib (wavesurfer.js v7):
waveform + seek, play/pause, volume, duration - only one player plays at
a time.
- Lazy waveform decode (IntersectionObserver + peaks cache) and a
no-waveform `<audio>` fallback if CORS/decode fails.
- Audio cards/rows added once in the shared `ui-generation-list`, so
both hosts' grid + list views get them.
- Audio playback in both lightboxes (webapp lightbox + desktop
lightbox-modal); Library gets an **Audio** filter tab and includes audio
in "All"; downloads keep the right extension.

### Shared plumbing
- `OmniGenApi` gains audio types + `getAudioModels` / `generateAudio` /
`estimateAudioCost`.
- `useOmniGenAudioModels`, request builder, enqueue + cost hook shared
via `@storyteller/omni-gen` (used by both hosts).
- `useGenerationJobs` moved from the webapp into `ui-generation-list`
(with re-export shim) and taught the `audio_generation` category - the
desktop audio feed reuses it.
- Suno model names/descriptions/creator icon added to `model-list`.
2026-07-08 18:18:03 +07:00
Brandon Thomas 3a2e3b30ec debug logs #2 (#1723) 2026-07-02 19:36:48 -04:00
BFlat 044c6d8a88 Model list cleanup: shared omni-gen hooks, dead code removal (#1719)
### 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.
2026-07-02 15:06:15 +07:00
Brandon Thomas fef83ad271 mini models in api gateway (#1697) 2026-06-25 15:33:51 -04:00
Tanish MankuandBFlat 61a0880c80 Moodboard: Extensive feature support (#1661)
Co-authored-by: BFlat <bombayapirat@gmail.com>
2026-06-23 20:15:52 -07:00
BFlat 39dd4dee9a revert dashboard merge (#1681) 2026-06-23 23:31:29 +07:00
bflatastic 6af1493c1c build fix react version nx 2026-06-23 21:05:49 +07:00
bflatastic 7c7e050d02 revert package json/lock file 2026-06-23 20:51:19 +07:00
BFlat 38d0642904 Dashboard: ported over dashboard to monorepo (#1679) 2026-06-23 20:18:49 +07:00
Brandon Thomas 0de867ad13 upgrade tauri 2.8 -> 2.11 (#1645) 2026-06-14 22:20:29 -04:00
BFlat 8da68b009c Desktop create pages redesign: shared generation feed, embedded model selector, gallery folders sidebar (#1635)
## Summary

Reworks the desktop (Tauri) app's Create Image / Create Video pages to
match the webapp,
extracts the webapp's generation gallery into a shared library used by
both apps, embeds the
model selector into every prompt box, adds a folders sidebar + marquee
selection to the gallery
modal, restyles the login/pricing/credits modals to the webapp look, and
turns the Apps page
into the default landing tab.

### Shared generation-list lib

- Extracted the webapp's generation feed into
`@storyteller/ui-generation-list`: list + masonry
grid views, pending/failed/completed rows and cards, thumbnail handling,
gallery/prompt data
hooks, and the shared grid/list view store + toggle. Host-specific
actions (recreate,
make-video, share, download, toasts) inject via render props — webapp
markup unchanged, its
  old files are now thin wrappers.

### Desktop create pages

- Prompt box fixed at the bottom, `max-w-5xl` like the webapp; pages
renamed to
  "Create Image" / "Create Video".
- Generation feed: in-progress/failed from the Tauri task queue, history
from the library API,
rendered as grid or list (toggle in the TopBar next to credits). Typed
Tauri completion events
  push finished media in instantly; the poll reconciles and dedupes.
- Row/card quick actions (recreate, make video, copy link, download)
backed by a shared
  `desktopMediaActions` module extracted from the TopBar gallery wiring.
- Hero + background act as a splash that fades when the feed loads;
bottom gradient behind the
prompt box. Batch generations show "Generating N images" on the single
pending chip.
- Video prompt box now expands vertically as far as the image prompt box
(viewport-relative).

### Embedded model selector

- `ClassyModelSelector` gained `variant="embedded"`: a compact pill that
opens the webapp-style
rich-list popover while keeping the desktop's provider selection.
Embedded on all five
prompt-input pages; floating selectors removed; legacy "10 sec." badges
dropped.

### Gallery modal

- Left sidebar lists the user's folders (drag-and-drop, right-click
management, new-folder
button) and shows on the Folders tab too, highlighting the active
folder's root branch.
- Marquee (drag-rectangle) selection in view mode, with edge
auto-scroll; starts only on blank
  background so it doesn't interfere with drag-to-folder/canvas.

### Restyled modals (desktop)

- Login/signup: webapp 2-column layout (form left, Vimeo showcase
right), no Google button
(Google auth isn't wired for Tauri), `artcraft-icon` logo, eye toggles.
The dismiss button is
removed so login is required; the post-auth Discord + Success onboarding
steps are kept, as is
  the existing Tauri auth flow.
- Pricing + credits: match the webapp visually (unified monochrome plan
cards with
Most Popular / Best Value highlight frames; `#161618` credit-pack cards
with Popular badge).
All billing stays on the existing Tauri `invoke` calls — no billing
logic changed.

### Apps page / navigation

- The Apps page is now the first tab and the default landing tab, with a
Home icon. Restyled to
the webapp home: "What will you craft today?" hero, Create / Edit
sections, gradient-hover
cards sized to their content, full-width scrollbar. The TopBar
quick-menu header reads
  "Create" instead of "Generate".

### Fixes

- Video Editor no longer crashes on the desktop app (`Tooltip must be
used within
TooltipProvider`): the editor shell now mounts its own Radix
`TooltipProvider`. The webapp got
one app-wide via `SidebarProvider`; the Tauri app had none, so panel
tooltips threw.

### Misc

- `getCreatorIconPathForModelId()` added to `@storyteller/model-list`
(model id → creator icon
  path, desktop/webapp aware).
2026-06-13 17:25:40 -04:00
Brandon Thomas 5979002f7e new cost tracking fields (#1630) 2026-06-11 16:37:43 -04:00
bflatastic 633003c43b webapp: styling updates and package versions 2026-06-11 21:18:49 +07:00
Brandon Thomas 9104faf806 fix frontend dev scripts (#1625) 2026-06-10 09:28:25 -04:00
BFlatandTanish Manku 9caaad5e0d Page video editor: Mount and make public (#1611)
Co-authored-by: Tanish Manku <tanish2k09.dev@gmail.com>
2026-06-08 13:01:51 -07:00
Brandon Thomas 6b5dcc8d35 fix artcraft desktop (#1550) 2026-05-22 16:16:23 -04:00
BFlat 934a30c9e1 web/webapp: pricing plan ui update (#1544) 2026-05-21 21:29:28 +07:00
Brandon Thomas 25e4f29568 include prompts with fal, fix alerts (#1507) 2026-05-12 19:47:43 -04:00
BFlat 11a2547beb separate web marketing and web app (#1503)
cloned to webapp project and ripped out marketing stuff
2026-05-12 21:45:36 +07:00
Brandon Thomas d2dbef249c tauri providers - fal api key support (#1502) 2026-05-12 00:02:41 -04:00
BFlat b4da753346 video background change feature (#1480)
- both app and website
2026-05-09 02:31:54 +07:00
Brandon ThomasandTanish Manku 58b64b694c Big tauri refactor: new consolidated commands, deprecate and remove duplication (#1486)
Co-authored-by: Tanish Manku <tanish2k09.dev@gmail.com>
2026-05-08 09:29:25 -04:00
BFlat f666e68dcd app: ask location before download (#1483)
- toggle in settings to enable disable ask location on download button
pressed
2026-05-08 16:25:27 +07:00
BFlat ac9a0c2172 show more media info on desktop app lightbox (#1474)
- 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.
2026-05-05 21:23:07 +07:00
Brandon Thomas a90a27e45d debug logs (#1472) 2026-05-04 02:18:33 -04:00
BFlat 47d58bcd80 website: redesign landing (#1446)
full site redesign + animated scroll trigger sections
2026-05-01 15:59:17 +07:00
BFlat 13f1fb4f63 bug fixes for character and video ref uploads on website, show error … (#1451)
…toasts
2026-04-30 01:16:41 +07:00
Brandon Thomas c2ca096099 desktop frontend support for gpt-image-2 (#1418) 2026-04-22 10:40:47 -04:00
BFlat 128d3e43c5 reuse gallery modal for the artcraft website and app styling aligned (#1415)
- website to reuse shared app lib's gallery modal for image selection
- styling alignments with the desktop app
- new checkbox shared component
2026-04-22 18:38:02 +07:00
Brandon Thomas f397ad825c worldlabs fixes for nsfw 500ing (#1377) 2026-04-15 20:05:07 -04:00
BFlat 99b831c3b0 duration slider for video gen website (#1367) 2026-04-14 21:31:45 +07:00
Brandon Thomas 03edd5faf6 tauri cors fix (#1344) 2026-04-12 03:03:07 -04:00
Brandon Thomas 72d680411f characters API and schema (#1290) 2026-04-03 00:17:03 -04:00
Tanish Manku 2f73e861d1 2D: Make PageDraw a self-contained lib (#1244)
Moves PageDraw from artcraft app to a component lib, leaving an adapter
implementation and import behind instead
2026-03-31 14:43:21 -07:00
BFlat 20d7e3bf31 generations gallery on the website (#1233) 2026-03-29 09:42:00 +07:00
Tanish Manku 32e01a415b Move character ordering and AI Trump category in 3D presets (#1217)
Storyboy, Story Girl, Knight, and News Anchor are now pinned to the
front of the Characters row in that order. AI Trump is moved from
Characters into Memes. Both are resolved client-side after the featured
items fetch by matching on lowercased item names.
2026-03-25 00:42:48 -07:00
Brandon Thomas 41988a4462 plumb references to seedance (#1150) 2026-03-16 10:49:00 -04:00
BFlat c04586c785 genhub start mock to ui (#1138) 2026-03-12 21:12:46 +07:00
BFlat fd89d5ab39 Revert nextjs genhub (#1137) 2026-03-12 15:49:24 +07:00