Clears 51 open TypeScript issues. Typecheck and build are left to CI.
**typescript:S6544 (17)**
- A cached promise checked by truthiness (`if (existing)`, `if
(cached)`, `if (ammoLoadPromise)`…) is checked as `!== undefined` / `!==
null` against its map or field type. Same branches, since a Promise is
always truthy.
- `scene.ts` `load_glb_wrapped_no_cors` / `load_splat_wrapped_no_cors`:
the fetch moves out of the `async` Promise executor. The methods were
already `async`, so a failed fetch now `throw`s (rejects the same way),
and only the callback APIs (`GLTFLoader.parse`, `SplatMesh` `onLoad`)
stay inside `new Promise`.
- `SceneManagerAPI` declares the promises its implementation returns
(`create`/`retrieve` → `Promise<THREE.Object3D | undefined>`,
`update`/`delete` → `Promise<void>`). Nothing calls through the
interface.
- `convertFbxToGlb.worker.ts`: `onmessage` calls `void convert(event)`.
**typescript:S3923 / S1764 / S3516 / S905 (12)**: identical branches
collapsed (`variant === "world" ? "max-w-[1600px]" : "max-w-[1600px]"`,
`ScissorIcon : ScissorIcon`, `index % 2 === 0 ? point + offset : point +
offset`, PromptBox3D's two `setIsEnqueueing(false); return;` arms,
`{DEFAULT_GRID_CONFIG ? null : null}` and its now-unused import), `x &&
x`, the `this.gridHelper;` statement, and `CurrentUserBadge`, which
always rendered `null` (its markup was commented out).
**typescript:S6959 (5)**: each `reduce` starts from the first element.
Every one is behind an emptiness check, and comparing the first element
with itself keeps it.
**typescript:S2871 (4)**: `sort((a, b) => a.localeCompare(b))` for the
order-independent cache keys and the board and font name lists.
**typescript:S8786 (8)**
- Markdown link text is `[^\][]+`. `@image(url, size)` drops the `\s*`
before the size (it's `.trim()`ed already). ATX headings `trimStart()`
the text instead of `\s*`. Slugs trim one `-` (runs were already
collapsed to one).
- `color.ts` `stripCssNoise`: `!important` is replaced by a space, then
trailing `;` are trimmed in a loop.
- Data-URL mime: `/:([^;]*);/`.
- `ControlPanelSceneObject` number check: `/^-?\d*(\.\d*)?$/`. The old
`.` was unescaped and matched any character, so `12a3` passed as a
number.
**Others**: `window.open(…, "noopener,noreferrer")` (S5148).
`AlertTitle` renders `children` explicitly (S6850). The folder name hash
wraps with `Math.imul(…, 1)` instead of `| 0`, the same int32 result
(S7767). `autoFocus` is removed from the two rename inputs, which their
refs already focus and select (S9379).
**Not here**: S7059 in `editor.ts` (`ensureAmmoLoaded()` in the
constructor) is on a line #2807 changes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->
## Summary by CodeRabbit
* **Bug Fixes**
* Improved reliability when loading 3D scene assets and completing
drag-and-drop operations.
* Corrected numeric input validation, scene import handling, and video
duration tie handling.
* Improved media, audio, and video cache handling to avoid unnecessary
retries or missed waits.
* Refined markdown parsing for links, headings, image directives, and
heading anchors.
* Discord invites now open with additional browser protections.
* Improved CSS color cleanup and handling of pasted scene elements.
* **Improvements**
* Made ordering of tags, fonts, and prompt-related items more
consistent.
* Improved video editor timeline selections.
* Removed the non-functional account badge from the video editor header.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
**typescript:S1848 (2)**: `new SplatMesh({ …, onLoad })` created an
instance only for its callback. `scene.ts` now awaits `mesh.initialized`
(Spark's promise, which resolves after load), so a failed load rejects
instead of leaving the promise pending. `viewer-3d.tsx` runs the same
body in `initialized.then`, with a `.catch` that logs.
Kept as is: `TabState.setActiveTab` still resolves `true` (S3516 stays
open), because `TabState.spec.ts` and the tab-change benchmark read its
result.
Artcraft frontend
This Nx workspace contains the Tauri desktop frontend in apps/artcraft and its
shared libraries in libs. Website and Netlify builds live in artcraft-services.
Use Node.js 20 or newer and npm. Run the following commands from frontend/:
npm ci
npx nx dev artcraft
npx nx build artcraft
The frontend development server uses port 5173. Start the Rust app separately from the repository root:
./script/artcraft/unix_rust_dev.sh
The repository-root frontend launcher installs dependencies and starts the same server:
./script/artcraft/unix_frontend_dev.sh
On Windows, use script/artcraft/windows_frontend_dev.ps1 and
script/artcraft/windows_rust_dev.ps1 in separate terminals. See
development setup for Rust and Tauri prerequisites.
The Vite build writes apps/artcraft/dist, which Tauri bundles. Public resources
come from apps/artcraft/app/public. The app also needs its retained libraries,
root package lock, TypeScript project references, and Nx/Vite configuration.
The aliases in tsconfig.base.json resolve shared libraries to their source for
both development and production. The app's Vite configuration loads this file
explicitly so imports inside shared libraries use the same aliases. The desktop
build does not require library dist/ outputs or a separate library build.
Import shared libraries by the names declared in their package.json files:
import { Button } from "@storyteller/ui-button";
import { Modal } from "@storyteller/ui-modal";
Use the workspace-local Nx version through npx nx. npm ci and the release
workflows use the checked-in lockfile; do not reinstall different Nx versions
inside CI.
clean_modules.sh is an explicit cleanup command that removes build outputs,
Nx caches, and frontend dependencies. Do not run it when preserving local build
outputs during repository pruning.