Refactoring round one: HttpFontStore/HttpSoundStore/HttpTextureStore collapse into a generic HttpFileStore over a FileStore contract; fontImport/soundImport/textureImport share assetImport with an ImportKind per sort; ImagePicker/FontPicker/SoundPicker sit on FilePicker and describe only their row. The group/comment drag logic leaves GraphCanvas for annotationDrag.ts (with its own unit test), declaredRows.ts replaces the duplicated row build in particles.ts and shaders.ts, and dead code in font/sound/compile/emission/transform/actions is gone. README: asset list, check count 958, the two-job CI, the Scenes wiki page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
25 lines
961 B
TypeScript
25 lines
961 B
TypeScript
import { findAsset, type Project } from './Project';
|
||
|
||
/** Mirrors `FontStore::isValidName` on the server, where the name is derived from the bytes. */
|
||
const FILE_PATTERN = /^[a-f0-9]{64}\.(ttf|otf|woff|woff2)$/;
|
||
|
||
export function isFontFile(name: string): boolean {
|
||
return FILE_PATTERN.test(name);
|
||
}
|
||
|
||
/**
|
||
* The CSS family a file is registered under. Derived from the content-addressed name, so the same
|
||
* bytes are always one family and two uploads of one typeface cannot fight over a name – the name
|
||
* inside the font is never used for this, only for the asset's label.
|
||
*/
|
||
export function fontFamilyOf(file: string): string {
|
||
return `gm-font-${file.slice(0, 16)}`;
|
||
}
|
||
|
||
/** The file a `font` property points at, empty when the id is unset or no longer resolves. */
|
||
export function fontFileOf(project: Project, id: string): string {
|
||
if (id === '') return '';
|
||
const asset = findAsset(project, id);
|
||
return asset?.type === 'font' ? asset.file : '';
|
||
}
|