Commit Graph

14806 Commits

Author SHA1 Message Date
Tanish Manku e9ea2a938c Scene Builder: T-pose in clip gaps + drag-preview card for animations
- Gaps show the default pose: a disabled three.js action leaves the skeleton
  frozen on its last frame, so evaluateAt now resets any character with no clip
  under the playhead to its bind (T) pose via Skeleton.pose() (skeletons cached
  per character). Fixes blank space after a clip holding the clip's last pose.
- Animation drags now show the shared DragGhost tilt card with the clip
  thumbnail: the drawer's native-DnD handlers drive dragItem/dragPosition/
  assetDraggingUnder and suppress the browser's default drag image, matching
  object/character pickup motion.
2026-07-09 00:59:24 -07:00
Tanish Manku dc5608613d webapp: stop dev API host override pointing at localhost:12345
The DEV block set the API host to window.location.origin (so /v1 calls hit the
Vite proxy → api.storyteller.ai) and then immediately called setDevelopment(),
which overrode it to http://localhost:12345 — a local backend that isn't
running. Every media-token resolution failed, so getMediaUrlByToken returned ''
and GLB fetches fell back to localhost, serving nothing. The inline comment
already described this line as disabled; it just wasn't commented out. Now it
is, so local dev resolves media through the proxy as intended.
2026-07-09 00:59:24 -07:00
Tanish Manku 1a90bd54c2 Scene Builder: one clip row per character + overlap guard
Collapse the per-character clips onto a single timeline row (playback is
sequential — one clip at a time), replacing stacked lanes. Stacking only earned
its keep for additive layering, which is out of scope.

- TimelineController: add/move route through resolveFreeStart (snap to nearest
  non-overlapping slot); resizeClipLane and resolveClipDuration clamp against
  nextStartAfter so a strip never overruns the following clip. A character's
  strips stay a non-overlapping sequence, so evaluateAt has at most one active
  clip at any playhead.
- TimelineClipRow: renders all of a character's strips on one shared row
  (shown even when empty, as a drop hint); per-strip move/trim/loop and an
  inline x to remove.
- TimelineEditor: one TimelineClipRow per character instead of one per lane.
2026-07-08 00:04:13 -07:00
Tanish Manku 2920a189f0 Scene Builder: Mixamo clip-lane timeline UI + real clip length (phase C)
Stacked clip-strip rendering on the timeline with move/trim/loop/delete, drag-
and-drop from the Animations drawer onto a character's lane, and backfill of the
clip's real length into the strip once its GLB loads.

- TimelineClipRow: one stacked strip per clip lane under its character's
  keyframe row; body drags to move, right edge trims length, loop chip toggles
  repeat, trash removes. Clip edits ride the timeline Save/Cancel session (no
  per-op undo, matching keyframes).
- TimelineEditor: each character row is a drop target for clip drags; drop time
  is read from the ruler rect. Non-character rows ignore clip drags.
- AnimationsDrawer clips are now draggable (ANIMATION_CLIP_MIME) as well as
  click-to-add.
- ClipStrip.autoDuration: a fresh drop seeds a placeholder width, then
  CharacterAnimationManager.resolveClipDuration adopts the loaded clip's true
  length (re-clamping start); a user trim clears the flag so reload never
  clobbers a hand-set duration.
- clipBindsToCharacter warns when a clip resolves 0 tracks on the character —
  the diagnostic for whether retargeting is needed.
2026-07-07 23:51:37 -07:00
Tanish Manku 7f5e82cbd9 Scene Builder: Mixamo clip playback engine + drawer (phase B, spike)
Deterministic, playhead-driven skeletal-animation playback for character clip
lanes, plus a minimal click-to-add drawer so retargeting/binding can be verified
in the viewport before the full drag-drop / stacked-strip UI is built.

- CharacterAnimationManager: one AnimationMixer per character; each clip lane is
  an AnimationAction posed at an absolute time from the playhead + mixer.update(0)
  (scrub / play / record all pose identically). syncClipLanes() reconciles the
  runtime on every lane change; a load token guards superseded async clip loads.
- Scene.loadRawGlb(media_id): raw GLTF (scene + animations) with no scene-add /
  placeholder — sources clips for binding. Clips bind by node name (shared
  mixamorig:* naming); SkeletonUtils.retargetClip is the documented fallback.
- TimelineController: getPlayhead(); evaluate() poses characters; sync on
  add/move/remove clip lane + load/cancel/clear.
- actions: addClipToCharacter / removeClipLane.
- comps/AnimationsDrawer: right-docked, gated on a selected character; click a
  curated clip to drop it as a lane at the playhead.

Compiles clean (tsc exit 0); playback is NOT yet runtime-verified.
2026-07-07 15:42:24 -07:00
Tanish Manku 5c2fbbc9f3 Scene Builder: animation clip-lane data model (Mixamo drawer, phase A)
Foundation for placing Mixamo skeletal-animation clips on the timeline as
stacked lanes under character objects, alongside the existing keyframe tracks.

- ClipStrip / ClipLane types; TimelineData gains a `clipLanes` array (only the
  clip reference + placement is serialized — the AnimationClip is resolved and
  retargeted from sourceMediaId at runtime).
- TimelineController: addClipLane / moveClipLane / removeClipLane / clipLanesFor,
  create()/loadTimeline() init + backfill clipLanes, emitChanged carries them.
- TimelineChangedEvent → store mirror (timelineClipLanes) via EngineStoreBridge.
- Persistence rides the existing timeline save/load (getTimeline/loadTimeline).
2026-07-07 15:28:50 -07:00
Tanish Manku 11c7aa979d Scene Builder fixes: record-mode camera lock, Blender default framing, auto-keyframe
- Record mode is now fully immutable: gate MouseControls mouse-look + focus()
  on the viewport lock (new MouseControlsDeps.isViewportLocked → cameraController
  .locked), matching the existing FreeCam/tick gates.
- Default main (viewport) camera pulled back + elevated to a Blender-style 3/4
  view so a blank scene shows the full render-camera frustum.
- Auto-keyframe: mutating an already-keyframed object (gizmo drag-end or panel
  commit) replaces/creates a keyframe at the playhead via
  TimelineController.autoKeyIfTracked.
- loadTimeline backfills fps/duration/tracks defensively (legacy save safety).
2026-07-07 15:23:50 -07:00
bflatastic 26aa3d3839 3D scene editor: exact camera framing, timeline UX, outliner + chrome redesign
Camera framing
- Replace the full-canvas Letterbox with CameraFrame: an inset, aspect-correct
  frame (dimmed surround, rule-of-thirds, focal label) shown only in camera
  view and record mode — the free viewport no longer shows mattes
- The frame shows the EXACT render framing: CameraController.setCameraFrameRect
  offsets the viewport projection via camera.setViewOffset; snapshots and the
  timeline recorder clear/reapply the offset so captures stay full-bleed
- recordTimeline now encodes at the render camera's resolution/aspect with the
  grid hidden (was grabbing the raw viewport canvas)
- Frame avoids the bottom UI; expanded-timeline height is measured live

Aspect ratio
- Default is now 16:9 via DEFAULT_CAMERA_ASPECT_RATIO (store, controller,
  scene reset, newScene); removed the model cold-sync that forced Square
- New top-right aspect popover (AspectRatioMenu rewritten on PopoverMenu with
  proportional AspectRatioIcon, now exported from ui-promptbox)

Timeline
- Expanded editor shows every scene object as a track row (no selection
  needed), capped at ~3.5 rows then scrollable (scrollbar hidden so lanes
  stay width-aligned with the ruler)
- Restyled per design: dark lanes, keyframe diamonds + connecting line,
  playhead diamond handle, select-none everywhere
- Scrubbing: drag the ruler to scrub smoothly, following the pointer 1:1;
  times quantize to the timeline's frame grid (30fps) — transport readout
  shows frame timecode (m:ss:ff); keyframe drags quantize the same way
- Fixed playhead/keyframe misalignment (scrollbar shrank lanes vs ruler;
  label indent must be padding, not margin, to keep the w-32 column)
- Delete/Backspace removes the selected keyframe (capture-phase, so the
  engine's delete-scene-object keybind doesn't fire); clicking away
  deselects it
- Motion (easing) popover opens from the curve chip BETWEEN keyframes (new
  timelineEasingKeyframeId store field), centered above the chip in lane
  coordinates, backdrop-blurred, dismissed on outside click; selecting a
  keyframe no longer opens it
- Camera-track playback/scrubbing is now visible in camera view: the
  timeline mirrors the animated ::CAM:: pose onto the viewport camera

Record mode
- Viewport camera fully locked: mouse-look blocked via isMovable(), right-drag
  pan and wheel zoom gated in useFreeCam (build mode unaffected)

Chrome
- Outliner restyled as the "Scene" panel (+ Add via shared openAssetModal,
  two-line rows with friendly kind labels, blue selected card), open by
  default, vertically centered, fixed 45vh height independent of aspect ratio
- Build/Record pill fully rounded (new TabSelector listClassName prop)
- CameraStatusPill moved to the top-right camera cluster, height-matched
  with the aspect popover
- ExitCameraView is a regular labeled button docked right of the timeline bar
2026-07-07 23:15:54 +07:00
Tanish Manku 06315490b7 Scene Builder launch re-scope: drop promptbox, lightbox handoff, editable timeline duration
- Remove SceneBuilderPromptBox (MCP not ready). The animation timeline is the
  sole build-mode bottom UI: collapsed bar by default, expanded editor via
  chevron; an empty timeline is auto-created on init; auto-expands on load when
  the scene has keyframes or an imported THREE animation.
- Editable max timeline duration: TimelineController.setDuration (clamp 1-60s,
  covers 5-30) + click-to-edit DurationLabel in the bar/ruler.
- Capture/Record output → review modal (CompletionModal) that appears FIRST
  with a local preview (offline-safe), then uploads (images auto, videos manual)
  with status; on success opens the app Lightbox on the token (all destinations
  for the kind), on failure shows Retry.
- Adapter: add openMediaLightbox(token, kind); keep uploadMedia; remove
  openImageInEditor/openVideoInEditor (Lightbox owns destination routing). Host
  renders <Lightbox> and resolves cdnUrl via MediaFilesApi.

Compile-verified (tsc -b clean for pagescene + changed webapp files).
2026-07-06 23:54:47 -07:00
Tanish Manku c15d8bd1ec Scene Builder checkpoint: camera view fixes, record/capture + completion modal, save/load + undo persistence
Builds on the prior Scene Builder commit. Compile-verified (tsc -b clean for
pagescene + changed webapp files); see frontend/libs/components/pagescene/CLAUDE.md.

- Camera view: fixed record→build engine-teardown wipe (PreviewEngineCamera /
  cam-view canvas must stay mounted — hide via CSS, never unmount); Esc +
  viewport double-click exit; double-click frustum to enter; robust enter/exit
  pose; frustum pick-proxy hitbox; CameraStatusPill (Viewport vs render cam).
- Build promptbox: dedicated SceneBuilderPromptBox (MCP stub) replaced
  PromptBox3D; buildMode/toggle removed.
- Record mode: Capture (still) + Record (timeline→mp4 via mediabunny),
  RenderOverlay progress, producedArtifact cache, CompletionModal
  (preview/playback + Delete/Upload/Edit) + adapter handoff to 2D/create-video/
  video-editor (lazy, data-URL image handoff; best-effort uploads).
- Persistence: timeline + render-camera transform in scene JSON (save/load +
  unmount cache); ::CAM:: recreated on load after cameras restore.
- Undo: timeline transactional (SaveTimelineAction); camera moves via TransformAction.
2026-07-06 23:26:18 -07:00
Tanish Manku 40ec291395 Scene Builder: mode pill, prompted mode, timeline, camera split, record mode
Adds the Scene Builder feature set to the pagescene 3D editor (+ the shared
ui-promptbox). All UI-level; compile-verified only (tsc -b), not yet runtime
tested — see frontend/libs/components/pagescene/CLAUDE.md for the full spec and
per-slice "unverified" list.

- Build/Record mode pill (SceneModePill, TabSelector) + store `sceneMode`.
- Build sub-modes: Manual (current) vs Prompted. Prompted prompt bar shows a
  textbox + add-animation-timeline + prompt history + Update (Update is a stub
  pending the MCP scene-descriptor backend). Toggle via ButtonIconSelect.
- Animation timeline (in-memory): TimelineController + keyframe model +
  cubic-bezier interpolation ticked in the render loop; collapsed bar +
  expanded multi-track editor + Motion easing popover (draggable bezier);
  Save is an undoable action, Cancel reverts.
- Camera split (single camera): render camera is a selectable wireframe-frustum
  placeholder ("::CAM::") in the scene; look-through camera view via
  double-click / outliner view-from-camera icon; ExitCameraView button.
- Record mode: read-only playback through the locked render camera; build
  chrome hidden; Capture (still) works, Record (video) is a stub.
2026-07-03 05:24:48 -07:00
Brandon Thomas 3a2e3b30ec debug logs #2 (#1723) 2026-07-02 19:36:48 -04:00
Brandon Thomas 82c0a7a4d9 debug logs: new field, new queries, new APIs, new dashbord UX (#1722) 2026-07-02 16:43:00 -04:00
Brandon Thomas cbd3710f4f seedance text prompt max length (#1721) 2026-07-02 11:26:02 -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 353c49c55f bump artcraft to 0.36 (#1718) 2026-07-02 03:45:07 -04:00
Brandon Thomas 98ea5cdb62 shink assets that are way too large (#1717) 2026-07-02 03:42:41 -04:00
Brandon Thomas 8fdc5100af vidu (#1716) 2026-07-02 03:12:15 -04:00
Brandon Thomas b782cb0c67 veo (#1715) 2026-07-02 02:18:16 -04:00
Brandon Thomas f01e81b66a artcraft 0.35 (#1714) 2026-07-02 02:02:16 -04:00
Brandon Thomas 3c0d1415e5 update pathing 2026-07-02 01:30:07 -04:00
Brandon Thomas 5e4b7f271d artcraft 0.34 (#1713) 2026-07-02 01:16:31 -04:00
Brandon Thomas 2371dfd49e move assets out of build to decrease binary size (#1712) 2026-07-02 01:15:30 -04:00
Brandon Thomas 2f20e7b3a3 fix midjourney (#1711) 2026-07-02 00:06:27 -04:00
Brandon Thomas 9de8fb8979 tauri frontend - read from omni api (#1710) 2026-07-01 23:20:11 -04:00
Brandon Thomas a61fcd50ed tauri omni client (#1709) 2026-07-01 16:31:23 -04:00
Brandon Thomas c16db8793c dashboard - change icon; group sidebar (#1708) 2026-06-30 11:05:18 -04:00
Brandon Thomas b3311d1574 payment informatics 4 (#1707) 2026-06-30 10:40:28 -04:00
Brandon Thomas 35add74498 payment informatics 3 (#1706) 2026-06-30 08:19:52 -04:00
Brandon Thomas d99da56df0 payment informatics #2 (#1705) 2026-06-29 21:21:26 -04:00
Brandon Thomas 3e4f998599 payments informatics (part 1) (#1704) 2026-06-29 17:49:28 -04:00
Brandon Thomas 3bccac5b2e block usernames that look like tokens (#1703) 2026-06-27 19:47:56 -04:00
Brandon Thomas aa9e862ff2 grok fixes (#1702) 2026-06-27 13:52:51 -04:00
BFlat db871126df Webapp/website (promptbox): full editing controls in fullscreen focus mode (#1701)
Add a footer to the fullscreen prompt modal (desktop lib + webapp) so
focus mode is a complete editing surface, not just the textarea:

- Model selector, always-on reference-image row, and a Done button
  (closes the modal) instead of Generate.
- Video: also surfaces the input-mode (keyframe/reference) picker and
  the @Characters button (seedance_2p0). Shared image-row / character /
  input-mode elements are reused inline and in the modal (DRY).
- Webapp: model selector + leftToolbar + image row in the footer.

Layout: editor area is flex-1/min-h-0/overflow-hidden with the image row
and footer shrink-0, so the editor takes only the leftover space and
scrolls internally — resizing the window shorter no longer overflows.
MentionTextarea gains a `style` prop so the modal can cap resize/height.
2026-06-26 23:16:35 +07:00
BFlat aaff33955e Webapp/website: Promptbox fullscreen "focus mode" + 3D & tooltip fixes (#1700)
### Fullscreen focus mode (desktop + webapp)
- Added a top-right expand icon on every prompt editor that opens a
"Prompt" focus-mode overlay (blurred backdrop, `max-w-4xl` centered
card) for more prompting space.
- Same editor state — overlay binds to the same value + onChange, so
text/@-mentions persist. Auto-focuses with the caret at the end on open.
- Editor fills full height; @-mention dropdown no longer clipped; closes
on Esc / backdrop click.
- Reused `@storyteller/ui-modal`; shared `PromptFullscreenModal` +
button + `useFullscreenPrompt` hook across all 5 desktop promptboxes,
mirrored in the webapp's own PromptBox.

### 3D editor
- Model selector now renders inside the promptbox (was standalone on the
canvas).
- Pose button hides on deselect; pressing "Done" keeps it (flips back to
"Enter Pose Mode") while the character is still selected.
- Drag and drop into folders fix within gallery modal - doesn't close
immediately unless on 3D editor page.

### Tooltips & expand
- Fixed top-left menu tooltips getting stuck open after a tab change.
- Reworked image promptbox auto-grow/expand into a shared
`useAutoGrowEditorHeight` hook (no per-render style writes) with a
smooth height transition.
2026-06-26 21:54:29 +07:00
Tanish Manku ad5ae7968f Fix login modal: stop Reveal motion wrappers from stealing input focus (#1698)
Reveal/RevealGroup called `motion(as)` inside the render body. Unlike
the cached `motion.div` accessor, calling `motion()` as a function
returns a brand-new component type on every invocation. So each
re-render produced a different element type at that position, React
unmounted and remounted the whole subtree, and any focused <input>
inside (the login/signup/forgot-password forms) lost focus on every
keystroke — you could only type one character at a time.

Cache one motion component per element type via a module-level map so
the component reference stays stable across renders. React now
reconciles in place and focus is preserved.

Verified with a Playwright A/B test on the login form: before the fix
typing "test@example.com" left only "t" with focus on <body> (1 blur
mid-type); after the fix the full value is retained, focus stays on the
input, zero blurs.
2026-06-25 13:48:53 -07:00
Brandon Thomas fef83ad271 mini models in api gateway (#1697) 2026-06-25 15:33:51 -04:00
BFlat 864cfb5faf Fix image refs not attaching when selected from a folder in gallery modal (#1696)
handleUseSelected only searched groupedItems (root library), so picks
made
while browsing inside a folder — which live in folderGroupedItems —
resolved
to nothing and an empty array was passed to onUseSelected. Search both
sources, deduping by id, so folder selections attach correctly.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-25 20:38:53 +07:00
Brandon Thomas 2aed062790 seedance mini client (#1695) 2026-06-25 07:45:25 -04:00
Brandon Thomas 4d5f8e9c21 router 4k (#1693) 2026-06-24 17:18:37 -04:00
Brandon Thomas 98f9504bdd video models list refactor (#1692) 2026-06-24 13:56:56 -04:00
Brandon Thomas 7ff72d2ecc seedance 4k (#1691) 2026-06-24 12:45:11 -04:00
bflatastic 10eb062b6f seedance 2.1 -> 2.5 text + uncomment sidebar edit page 2026-06-24 22:52:32 +07:00
Brandon Thomas 3b225a7c6d omni api: status endpoint, api docs, etc (#1690) 2026-06-24 00:24:26 -04:00
Tanish Manku 61a0880c80 Moodboard: Extensive feature support (#1661)
Co-authored-by: BFlat <bombayapirat@gmail.com>
2026-06-23 20:15:52 -07:00
Tanish Manku c5c68d301a Webapp: Premium motion pass across app (#1662)
Motion foundation + expressive entrance/interaction polish for the
webapp, following a "balanced" direction (restrained chrome, expressive
canvas).

Foundation
- Motion tokens: --ease-*/--dur-* CSS vars (styles.css) mirrored as JS
constants/presets in lib/motion.ts; prefers-reduced-motion respected.
- Shared Reveal/RevealGroup primitive (components/motion) — fade-up +
stagger.

Chrome & navigation
- Global route fade (components/motion/page-transition) keyed by
section.
- Sidebar active-indicator slide via framer layoutId + icon hover.

Pages
- Home: hero/eyebrow reveal, staggered launcher cards, spring lift/press
hover, double-bezel cards, nested arrow chip.
- Auth: AuthHeader staggers in (login+signup); form blocks cascade
after.
- Pricing: wire the orphaned data-reveal hero through the real Reveal.
- Library: sliding active indicator on tab/filter switchers (layoutId),
shimmer-sweep loading skeleton.

Pagescene entrance animations (user-drop only)
- EntranceAnimator: meshes get a transform-based pop/rise/fall drop with
a rubbery squash overshoot; splats get a per-gaussian radial settle
(Spark dyno) with center-out fade + height drop. Dev-only speed slider.

Gallery + asset drag-under UX
- On drag-start the gallery/asset modal becomes pointer-transparent so
the drag passes under onto the canvas; steps back to translucent (reopen
on) or eases fully out (reopen off), all interpolated. Shared DragGhost
(lift/tilt/follow) feeds both DnD systems. Media fades to placeholders
so thumbnails don't block the canvas. Asset modal always reopens fully
visible.

PageDraw: entrance fade on add + workspace vignette.

Dev-only (local testing): drop setDevelopment() API host pin; add /__cdn
proxy + maybeProxyCdnUrl so CDN splat/GLB assets dodge CORS.
2026-06-23 20:14:06 -07:00
Brandon Thomas 81de7838d3 omni api url support (#1689) 2026-06-23 23:10:14 -04:00
Brandon Thomas 9653e9082e minor change to test build works 2026-06-23 19:40:13 -04:00
Brandon Thomas ee6c064f4f dashboard netlify build script (#1687)
The netlify_build.sh referenced by the Netlify build command was never
committed, so the build failed with "No such file or directory".

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 19:36:19 -04:00
Brandon Thomas 630b132ae7 modify netlify configs for dashboard 2026-06-23 19:16:06 -04:00