This commit is contained in:
Kalle
2026-08-04 22:15:50 +03:00
parent e6a9d8f112
commit 14a9ddb1a6
42 changed files with 37566 additions and 62 deletions

View File

@@ -70,21 +70,30 @@ video file → capture/vod-frames (WebCodecs decode, seek fallback) [VoD tab]
## Assets (CDN) and fonts
Glyph atlases, weapon/ability/special/sub template sets, and the planner
signature atlas live in the **sendou-ink/assets repo** under `assets/cv/v1/**`
(local checkout expected at `../assets`, override with `CV_ASSETS_DIR`;
version segment bumps on breaking atlas-format changes so old deploys keep
reading old files):
Weapon/ability/special/sub template sources are the site's shared game
icons in the **sendou-ink/assets repo** under `assets/img/**` (`.avif`; ids
come from `~/modules/in-game-lists`, plus the CV-only `UNKNOWN` ability
badge — `toCvAbility` narrows template ids back to sendou ids). The
CV-specific sets — glyph atlases and the planner signature atlas — live in
this repo under `public/cv/v1/**` (override with `CV_ASSETS_DIR`; the
version segment bumps on breaking atlas-format changes). xxx: the atlases
are in `public/` only while the feature is in development — move them to
the assets repo (and the worker back to the CDN base) later:
- Browser/worker: fetched from `${Config.staticAssetsUrl}/cv/v1` (the base URL
rides the worker init message). For local dev against fresh regens, serve
the checkout with CORS — `npx serve /Users/kalle/Developer/assets/assets -l 9100 --cors`
- Browser/worker: icons fetched from `Config.staticAssetsUrl` at `img/**`
(the base URL rides the worker init message; the DO Space needs CORS —
GET, sendou.ink + localhost origins — because the worker `fetch()`es
cross-origin, plain `<img>` consumers don't); atlases fetched same-origin
from `/cv/v1/**`. For local dev against fresh icon regens, serve the
checkout with CORS —
`npx serve /Users/kalle/Developer/assets/assets -l 9100 --cors`
— and set `VITE_STATIC_ASSETS_URL=http://localhost:9100` in `.env`.
The DO Space needs CORS (GET, sendou.ink + localhost origins) because the
worker `fetch()`es JSON/PNG cross-origin — plain `<img>` consumers don't.
- Node (tests/scripts): read from the checkout directly, never the CDN.
- Shipping a regen = commit + push the assets repo (CDN mirrors on push to
main); plain regens overwrite in place, breaking format changes bump `v1`.
- Node (tests/scripts): atlases from `public/cv/v1`, icons from the
`../assets` checkout directly, never the CDN. AVIF icons decode through
`sharp` (`node/image-io.ts`) — `@napi-rs/canvas` mis-decodes AVIF
partial-alpha pixels.
- Atlas regens overwrite `public/cv/v1` in place and ship with the app
build; breaking format changes bump `v1`.
Fonts are proprietary and gitignored: `BlitzMain.otf`, `BlitzBold.otf`,
`FOT-RowdyStd-EB.otf`, `FOT-KurokaneStd-EB.otf` in `assets/fonts/` (repo

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { CV_ASSETS_URL } from "~/utils/urls";
import type { MainWeaponId } from "~/modules/in-game-lists/types";
import { mainWeaponImageUrl } from "~/utils/urls";
import { CANONICAL_HEIGHT, CANONICAL_WIDTH, type Roi } from "../core/canonical";
import type { DeathData } from "../core/detectors/death/index";
import * as death from "../core/detectors/death/rois";
@@ -552,7 +553,7 @@ export function ScreenshotPage() {
<span className="cand" key={c.id}>
<img
className="weapon-icon"
src={`${CV_ASSETS_URL}/main-weapons/${c.id}.png`}
src={`${mainWeaponImageUrl(Number(c.id) as MainWeaponId)}.avif`}
alt={c.id}
/>
{c.id}

View File

@@ -11,6 +11,12 @@
* dominated startup.
*/
import { abilities as abilityList } from "~/modules/in-game-lists/abilities";
import {
mainWeaponIds,
specialWeaponIds,
subWeaponIds,
} from "~/modules/in-game-lists/weapon-ids";
import { prepareAbilityTemplates } from "./detectors/death/abilities";
import { BURST_ICON_TEMPLATE_SIZES } from "./detectors/death/rois";
import { prepareMinimapAbilityTemplates } from "./detectors/minimap/abilities";
@@ -31,9 +37,7 @@ import type { GlyphSet } from "./glyphs";
import type { FrameData } from "./image";
export interface ResourceIO {
/** ids listed in <assets>/<dir>/manifest.json */
readManifest(dir: string): Promise<string[]>;
/** decoded RGBA of <assets>/<dir>/<id>.png */
/** decoded RGBA of the shared game icon img/<dir>/<id>.avif */
readIcon(dir: string, id: string): Promise<FrameData>;
/** glyph atlas by name as a (possibly lazy) getter; () => null when absent */
loadAtlas(name: string): Promise<() => GlyphSet | null>;
@@ -75,12 +79,13 @@ function lazy<T>(build: () => T): () => T {
export async function assembleScoreboardResources(
io: ResourceIO,
): Promise<ScoreboardResources> {
const icons = async (dir: string) => {
const ids = await io.readManifest(dir);
return Promise.all(
ids.map(async (id) => ({ id, image: await io.readIcon(dir, id) })),
const icons = (dir: string, ids: readonly (number | string)[]) =>
Promise.all(
ids.map(async (id) => ({
id: String(id),
image: await io.readIcon(dir, String(id)),
})),
);
};
const [
weaponIcons,
@@ -90,10 +95,15 @@ export async function assembleScoreboardResources(
plannerStages,
atlasEntries,
] = await Promise.all([
icons("main-weapons"),
icons("specials"),
icons("sub-weapons"),
icons("abilities"),
icons("main-weapons", mainWeaponIds),
icons("special-weapons", specialWeaponIds),
icons("sub-weapons", subWeaponIds),
// UNKNOWN is the garbled-badge template: it competes in matching and
// wins on unreadable slots (see CvAbility in cv-types.ts)
icons("abilities", [
...abilityList.map((ability) => ability.name),
"UNKNOWN",
]),
io.loadPlannerStages(),
Promise.all(
(Object.entries(ATLASES) as [keyof typeof ATLASES, string][]).map(

View File

@@ -14,8 +14,8 @@ export type CvLobby = (typeof CV_LOBBIES)[number];
/**
* A detected gear ability: a sendou ability id, or the explicit
* unrecognized marker (the UNKNOWN template in assets/cv/abilities
* distinct from null, which means the badge was covered/absent).
* unrecognized marker (the UNKNOWN template in the shared img/abilities
* set — distinct from null, which means the badge was covered/absent).
*/
export type CvAbility = Ability | "UNKNOWN"; // xxx: why not AbilityWithUnknown

View File

@@ -1,9 +1,18 @@
/**
* Where Node-side code (tests, atlas builders) reads and writes the CV
* asset sets. Defaults to the sibling sendou-ink/assets checkout — the same
* files the CDN mirrors — and can be overridden with CV_ASSETS_DIR. The
* version segment must match worker-side CV_ASSETS_URL (app/utils/urls.ts).
* asset sets. CV_ASSETS_DIR holds the CV-specific atlases (glyphs, planner
* signatures; overridable with the env var of the same name) — its version
* segment must match the worker-side ATLAS_BASE (worker/resources.ts).
* GAME_IMG_DIR is the sibling sendou-ink/assets checkout's shared `img/**`
* tree the game icons are read from — the same files the CDN mirrors.
* xxx: atlases temporarily live in this repo's public/ while the feature
* is in development; move them to the assets repo later
*/
export const CV_ASSETS_DIR =
process.env.CV_ASSETS_DIR ??
new URL("../../../../../assets/assets/cv/v1", import.meta.url).pathname;
new URL("../../../../public/cv/v1", import.meta.url).pathname;
export const GAME_IMG_DIR = new URL(
"../../../../../assets/assets/img",
import.meta.url,
).pathname;

View File

@@ -8,9 +8,23 @@
*/
import { readFileSync, writeFileSync } from "node:fs";
import { createCanvas, Image, ImageData } from "@napi-rs/canvas";
import sharp from "sharp";
import type { FrameData } from "../core/image";
export async function readImage(path: string): Promise<FrameData> {
// @napi-rs/canvas mis-decodes AVIF at partial-alpha pixels (white-matte
// leak); sharp returns clean straight-alpha RGBA for those
if (path.endsWith(".avif")) {
const { data, info } = await sharp(path)
.ensureAlpha()
.raw()
.toBuffer({ resolveWithObject: true });
return {
width: info.width,
height: info.height,
data: new Uint8ClampedArray(data),
};
}
const img = new Image();
img.src = readFileSync(path);
await img.decode();

View File

@@ -1,9 +1,10 @@
/**
* Node IO for ScoreboardResources: reads the CV asset sets from the local
* sendou-ink/assets checkout (tests and atlas builders never touch the
* CDN). What the bundle contains — every key, template option set, and
* atlas name — lives in core/resources.ts, shared with the worker's HTTP
* loader.
* CDN). Game icons come from the checkout's shared `img/**` tree, the
* CV-specific atlases from `cv/v1/**`. What the bundle contains — every
* key, template option set, and atlas name — lives in core/resources.ts,
* shared with the worker's HTTP loader.
*/
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
@@ -15,7 +16,7 @@ import {
import type { ScoreboardResources } from "../core/detectors/scoreboard/index";
import { type AtlasMeta, type GlyphSet, loadGlyphSet } from "../core/glyphs";
import { assembleScoreboardResources } from "../core/resources";
import { CV_ASSETS_DIR as ASSETS_DIR } from "./assets-dir";
import { CV_ASSETS_DIR as ASSETS_DIR, GAME_IMG_DIR } from "./assets-dir";
import { readImage } from "./image-io";
/** Decode eagerly, defer the (CPU-heavy) glyph slicing to first access. */
@@ -43,13 +44,7 @@ async function loadPlannerStagesLazy(): Promise<() => PlannerStage[] | null> {
/** Requires loadOpenCV() to have resolved. */
export function loadScoreboardResources(): Promise<ScoreboardResources> {
return assembleScoreboardResources({
readManifest: (dir) =>
Promise.resolve(
JSON.parse(
readFileSync(join(ASSETS_DIR, dir, "manifest.json"), "utf8"),
) as string[],
),
readIcon: (dir, id) => readImage(join(ASSETS_DIR, dir, `${id}.png`)),
readIcon: (dir, id) => readImage(join(GAME_IMG_DIR, dir, `${id}.avif`)),
loadAtlas: loadAtlasLazy,
loadPlannerStages: loadPlannerStagesLazy,
});

View File

@@ -3,7 +3,7 @@
* in-flight frame at a time (the sampler drops frames while busy). Each
* frame yields one result per registered detector, then a single "done".
*/
import { CV_ASSETS_URL } from "../../../utils/urls";
import { Config } from "../../../config";
import type { WorkerResponse } from "./protocol";
export type ResultHandler = (
@@ -69,7 +69,7 @@ export class AnalyzerClient {
};
this.#worker.postMessage({
kind: "init",
assetsBaseUrl: CV_ASSETS_URL,
assetsBaseUrl: Config.staticAssetsUrl,
suppressSteadyFrames: options.suppressSteadyFrames ?? true,
});
}

View File

@@ -3,9 +3,9 @@ import type { DetectedEvent, GateResult } from "../core/detectors/types";
export interface InitRequest {
kind: "init";
/**
* base URL the worker fetches atlases/templates from (e.g.
* `${Config.staticAssetsUrl}/cv/v1`); passed in the init message so the
* worker bundle stays free of the app config graph
* static assets CDN root the worker fetches icons and atlases from
* (`Config.staticAssetsUrl`); passed in the init message so the worker
* bundle stays free of the app config graph
*/
assetsBaseUrl: string;
/**

View File

@@ -1,6 +1,8 @@
/**
* Worker/browser IO for ScoreboardResources: fetches the CDN-hosted CV
* assets (assets repo `assets/cv/v1/**`) over HTTP. What the bundle
* Worker/browser IO for ScoreboardResources: fetches over HTTP. Game icons
* come from the CDN's shared `img/<dir>/<id>.avif` sets the rest of the app
* already uses; the CV-specific atlases (glyphs, planner signatures) are
* served same-origin from this repo's `public/cv/v1/**`. What the bundle
* contains — every key, template option set, and atlas name — lives in
* core/resources.ts, shared with the Node loader. The base URL arrives via
* the worker init message (see worker/protocol.ts) so this module never
@@ -17,6 +19,14 @@ import { type AtlasMeta, type GlyphSet, loadGlyphSet } from "../core/glyphs";
import type { FrameData } from "../core/image";
import { assembleScoreboardResources } from "../core/resources";
/** CV parser atlases; the version segment guards against CDN cache skew —
* bump it together with breaking atlas format changes (must match the
* Node-side CV_ASSETS_DIR default in node/assets-dir.ts).
* xxx: temporarily served same-origin from this repo's public/ while the
* feature is in development; move to the assets repo CDN
* (`${base}/cv/v1`) later */
const ATLAS_BASE = "/cv/v1";
async function fetchImage(url: string): Promise<FrameData> {
const res = await fetch(url);
if (!res.ok) throw new Error(`fetch ${url}: ${res.status}`);
@@ -74,12 +84,8 @@ export function fetchScoreboardResources(
base: string,
): Promise<ScoreboardResources> {
return assembleScoreboardResources({
readManifest: (dir) =>
fetch(`${base}/${dir}/manifest.json`).then(
(r) => r.json() as Promise<string[]>,
),
readIcon: (dir, id) => fetchImage(`${base}/${dir}/${id}.png`),
loadAtlas: makeFetchAtlas(base),
loadPlannerStages: makeFetchPlannerStages(base),
readIcon: (dir, id) => fetchImage(`${base}/img/${dir}/${id}.avif`),
loadAtlas: makeFetchAtlas(ATLAS_BASE),
loadPlannerStages: makeFetchPlannerStages(ATLAS_BASE),
});
}

View File

@@ -200,10 +200,6 @@ export const SECOND_PLACEMENT_ICON_PATH = `${STATIC_ASSETS_URL}/svg/placements/s
export const THIRD_PLACEMENT_ICON_PATH = `${STATIC_ASSETS_URL}/svg/placements/third.svg`;
export const WELCOME_HERO_IMAGE_PATH = `${STATIC_ASSETS_URL}/img/welcome-hero.webp`;
/** CV parser atlases/templates; the version segment guards against CDN cache
* skew — bump it together with breaking atlas format changes */
export const CV_ASSETS_URL = `${STATIC_ASSETS_URL}/cv/v1`;
export const APP_ICON_URL = `${STATIC_ASSETS_URL}/img/app-icon.png`;
export const pwaSplashScreenImageUrl = (fileName: string) =>
`${STATIC_ASSETS_URL}/img/splash-screens/${fileName}`;

View File

@@ -8,6 +8,7 @@
"!app/db/seed/data/placements.json",
"!build/**/*",
"!app/features/cv/tests/fixtures",
"!public/cv/**/*",
"!test-results/**/*",
"!playwright-report/**/*",
"!.react-router/**/*",

View File

@@ -125,6 +125,7 @@
"i18next-locales-sync": "2.1.1",
"knip": "6.29.0",
"magic-string": "0.30.21",
"sharp": "^0.35.3",
"sql-formatter": "15.8.2",
"typescript": "7.0.2",
"vite": "8.1.5",

313
pnpm-lock.yaml generated
View File

@@ -242,6 +242,9 @@ importers:
magic-string:
specifier: 0.30.21
version: 0.30.21
sharp:
specifier: ^0.35.3
version: 0.35.3(@types/node@26.1.1)
sql-formatter:
specifier: 15.8.2
version: 15.8.2
@@ -639,6 +642,168 @@ packages:
'@floating-ui/utils@0.2.11':
resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==}
'@img/colour@1.1.0':
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
'@img/sharp-darwin-arm64@0.35.3':
resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.35.3':
resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [darwin]
'@img/sharp-freebsd-wasm32@0.35.3':
resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==}
engines: {node: '>=20.9.0'}
os: [freebsd]
'@img/sharp-libvips-darwin-arm64@1.3.2':
resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.3.2':
resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.3.2':
resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.3.2':
resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.3.2':
resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.3.2':
resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.3.2':
resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.3.2':
resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.3.2':
resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.3.2':
resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.35.3':
resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.35.3':
resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==}
engines: {node: '>=20.9.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.35.3':
resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==}
engines: {node: '>=20.9.0'}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.35.3':
resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==}
engines: {node: '>=20.9.0'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.35.3':
resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==}
engines: {node: '>=20.9.0'}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.35.3':
resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.35.3':
resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.35.3':
resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.35.3':
resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==}
engines: {node: '>=20.9.0'}
'@img/sharp-webcontainers-wasm32@0.35.3':
resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==}
engines: {node: '>=20.9.0'}
cpu: [wasm32]
'@img/sharp-win32-arm64@0.35.3':
resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-ia32@0.35.3':
resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==}
engines: {node: ^20.9.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.35.3':
resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [win32]
'@internationalized/date@3.12.2':
resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==}
@@ -4280,6 +4445,15 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
sharp@0.35.3:
resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==}
engines: {node: '>=20.9.0'}
peerDependencies:
'@types/node': '*'
peerDependenciesMeta:
'@types/node':
optional: true
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -5254,6 +5428,112 @@ snapshots:
'@floating-ui/utils@0.2.11': {}
'@img/colour@1.1.0': {}
'@img/sharp-darwin-arm64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.3.2
optional: true
'@img/sharp-darwin-x64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.3.2
optional: true
'@img/sharp-freebsd-wasm32@0.35.3':
dependencies:
'@img/sharp-wasm32': 0.35.3
optional: true
'@img/sharp-libvips-darwin-arm64@1.3.2':
optional: true
'@img/sharp-libvips-darwin-x64@1.3.2':
optional: true
'@img/sharp-libvips-linux-arm64@1.3.2':
optional: true
'@img/sharp-libvips-linux-arm@1.3.2':
optional: true
'@img/sharp-libvips-linux-ppc64@1.3.2':
optional: true
'@img/sharp-libvips-linux-riscv64@1.3.2':
optional: true
'@img/sharp-libvips-linux-s390x@1.3.2':
optional: true
'@img/sharp-libvips-linux-x64@1.3.2':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.3.2':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.3.2':
optional: true
'@img/sharp-linux-arm64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.3.2
optional: true
'@img/sharp-linux-arm@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.3.2
optional: true
'@img/sharp-linux-ppc64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.3.2
optional: true
'@img/sharp-linux-riscv64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.3.2
optional: true
'@img/sharp-linux-s390x@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.3.2
optional: true
'@img/sharp-linux-x64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.3.2
optional: true
'@img/sharp-linuxmusl-arm64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.3.2
optional: true
'@img/sharp-linuxmusl-x64@0.35.3':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.3.2
optional: true
'@img/sharp-wasm32@0.35.3':
dependencies:
'@emnapi/runtime': 1.11.2
optional: true
'@img/sharp-webcontainers-wasm32@0.35.3':
dependencies:
'@img/sharp-wasm32': 0.35.3
optional: true
'@img/sharp-win32-arm64@0.35.3':
optional: true
'@img/sharp-win32-ia32@0.35.3':
optional: true
'@img/sharp-win32-x64@0.35.3':
optional: true
'@internationalized/date@3.12.2':
dependencies:
'@swc/helpers': 0.5.23
@@ -8885,6 +9165,39 @@ snapshots:
setprototypeof@1.2.0: {}
sharp@0.35.3(@types/node@26.1.1):
dependencies:
'@img/colour': 1.1.0
detect-libc: 2.1.2
semver: 7.8.5
optionalDependencies:
'@img/sharp-darwin-arm64': 0.35.3
'@img/sharp-darwin-x64': 0.35.3
'@img/sharp-freebsd-wasm32': 0.35.3
'@img/sharp-libvips-darwin-arm64': 1.3.2
'@img/sharp-libvips-darwin-x64': 1.3.2
'@img/sharp-libvips-linux-arm': 1.3.2
'@img/sharp-libvips-linux-arm64': 1.3.2
'@img/sharp-libvips-linux-ppc64': 1.3.2
'@img/sharp-libvips-linux-riscv64': 1.3.2
'@img/sharp-libvips-linux-s390x': 1.3.2
'@img/sharp-libvips-linux-x64': 1.3.2
'@img/sharp-libvips-linuxmusl-arm64': 1.3.2
'@img/sharp-libvips-linuxmusl-x64': 1.3.2
'@img/sharp-linux-arm': 0.35.3
'@img/sharp-linux-arm64': 0.35.3
'@img/sharp-linux-ppc64': 0.35.3
'@img/sharp-linux-riscv64': 0.35.3
'@img/sharp-linux-s390x': 0.35.3
'@img/sharp-linux-x64': 0.35.3
'@img/sharp-linuxmusl-arm64': 0.35.3
'@img/sharp-linuxmusl-x64': 0.35.3
'@img/sharp-webcontainers-wasm32': 0.35.3
'@img/sharp-win32-arm64': 0.35.3
'@img/sharp-win32-ia32': 0.35.3
'@img/sharp-win32-x64': 0.35.3
'@types/node': 26.1.1
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

View File

@@ -0,0 +1,597 @@
{
"height": 19,
"glyphs": [
{
"char": "a",
"x": 2,
"y": 2,
"w": 12,
"h": 17,
"source": "fixture"
},
{
"char": "B",
"x": 16,
"y": 2,
"w": 12,
"h": 20,
"source": "fixture"
},
{
"char": "e",
"x": 30,
"y": 2,
"w": 12,
"h": 16,
"source": "fixture"
},
{
"char": "l",
"x": 44,
"y": 2,
"w": 6,
"h": 20,
"source": "fixture"
},
{
"char": "t",
"x": 52,
"y": 2,
"w": 10,
"h": 18,
"source": "fixture"
},
{
"char": "X",
"x": 64,
"y": 2,
"w": 11,
"h": 20,
"source": "fixture"
},
{
"char": "X",
"x": 77,
"y": 2,
"w": 11,
"h": 21,
"source": "font"
},
{
"char": "X",
"x": 90,
"y": 2,
"w": 12,
"h": 22,
"source": "font"
},
{
"char": "-",
"x": 104,
"y": 2,
"w": 12,
"h": 7,
"source": "font"
},
{
"char": "-",
"x": 118,
"y": 2,
"w": 12,
"h": 8,
"source": "font"
},
{
"char": "K",
"x": 132,
"y": 2,
"w": 12,
"h": 21,
"source": "font"
},
{
"char": "K",
"x": 146,
"y": 2,
"w": 12,
"h": 22,
"source": "font"
},
{
"char": "a",
"x": 160,
"y": 2,
"w": 12,
"h": 17,
"source": "font"
},
{
"char": "a",
"x": 174,
"y": 2,
"w": 13,
"h": 18,
"source": "font"
},
{
"char": "m",
"x": 189,
"y": 2,
"w": 16,
"h": 16,
"source": "font"
},
{
"char": "m",
"x": 207,
"y": 2,
"w": 17,
"h": 17,
"source": "font"
},
{
"char": "p",
"x": 226,
"y": 2,
"w": 12,
"h": 21,
"source": "font"
},
{
"char": "p",
"x": 240,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "f",
"x": 255,
"y": 2,
"w": 12,
"h": 21,
"source": "font"
},
{
"char": "f",
"x": 269,
"y": 2,
"w": 11,
"h": 22,
"source": "font"
},
{
"char": "A",
"x": 282,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "A",
"x": 297,
"y": 2,
"w": 14,
"h": 22,
"source": "font"
},
{
"char": "n",
"x": 313,
"y": 2,
"w": 11,
"h": 16,
"source": "font"
},
{
"char": "n",
"x": 326,
"y": 2,
"w": 12,
"h": 17,
"source": "font"
},
{
"char": "r",
"x": 340,
"y": 2,
"w": 10,
"h": 16,
"source": "font"
},
{
"char": "r",
"x": 352,
"y": 2,
"w": 10,
"h": 17,
"source": "font"
},
{
"char": "c",
"x": 364,
"y": 2,
"w": 13,
"h": 16,
"source": "font"
},
{
"char": "c",
"x": 379,
"y": 2,
"w": 12,
"h": 18,
"source": "font"
},
{
"char": "h",
"x": 393,
"y": 2,
"w": 12,
"h": 21,
"source": "font"
},
{
"char": "h",
"x": 407,
"y": 2,
"w": 12,
"h": 22,
"source": "font"
},
{
"char": "i",
"x": 421,
"y": 2,
"w": 6,
"h": 21,
"source": "font"
},
{
"char": "i",
"x": 429,
"y": 2,
"w": 6,
"h": 22,
"source": "font"
},
{
"char": "e",
"x": 437,
"y": 2,
"w": 13,
"h": 16,
"source": "font"
},
{
"char": "e",
"x": 452,
"y": 2,
"w": 13,
"h": 17,
"source": "font"
},
{
"char": "(",
"x": 467,
"y": 2,
"w": 10,
"h": 25,
"source": "font"
},
{
"char": "(",
"x": 479,
"y": 2,
"w": 11,
"h": 26,
"source": "font"
},
{
"char": "S",
"x": 492,
"y": 2,
"w": 14,
"h": 21,
"source": "font"
},
{
"char": "S",
"x": 508,
"y": 2,
"w": 14,
"h": 22,
"source": "font"
},
{
"char": ")",
"x": 524,
"y": 2,
"w": 11,
"h": 25,
"source": "font"
},
{
"char": ")",
"x": 537,
"y": 2,
"w": 11,
"h": 25,
"source": "font"
},
{
"char": "O",
"x": 550,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "O",
"x": 565,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "P",
"x": 580,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "P",
"x": 595,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "v",
"x": 610,
"y": 2,
"w": 12,
"h": 16,
"source": "font"
},
{
"char": "v",
"x": 624,
"y": 2,
"w": 13,
"h": 17,
"source": "font"
},
{
"char": "t",
"x": 639,
"y": 2,
"w": 10,
"h": 19,
"source": "font"
},
{
"char": "t",
"x": 651,
"y": 2,
"w": 11,
"h": 20,
"source": "font"
},
{
"char": "B",
"x": 664,
"y": 2,
"w": 12,
"h": 21,
"source": "font"
},
{
"char": "B",
"x": 678,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "l",
"x": 693,
"y": 2,
"w": 6,
"h": 21,
"source": "font"
},
{
"char": "l",
"x": 701,
"y": 2,
"w": 6,
"h": 22,
"source": "font"
},
{
"char": "y",
"x": 709,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "y",
"x": 724,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "s",
"x": 739,
"y": 2,
"w": 12,
"h": 16,
"source": "font"
},
{
"char": "s",
"x": 753,
"y": 2,
"w": 12,
"h": 17,
"source": "font"
},
{
"char": "C",
"x": 767,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "C",
"x": 782,
"y": 2,
"w": 14,
"h": 22,
"source": "font"
},
{
"char": "o",
"x": 798,
"y": 2,
"w": 12,
"h": 16,
"source": "font"
},
{
"char": "o",
"x": 812,
"y": 2,
"w": 13,
"h": 17,
"source": "font"
},
{
"char": "b",
"x": 827,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "b",
"x": 842,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "ó",
"x": 857,
"y": 2,
"w": 12,
"h": 22,
"source": "font"
},
{
"char": "ó",
"x": 871,
"y": 2,
"w": 13,
"h": 23,
"source": "font"
},
{
"char": "d",
"x": 886,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "d",
"x": 901,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "M",
"x": 916,
"y": 2,
"w": 15,
"h": 21,
"source": "font"
},
{
"char": "M",
"x": 933,
"y": 2,
"w": 16,
"h": 22,
"source": "font"
},
{
"char": "é",
"x": 951,
"y": 2,
"w": 13,
"h": 22,
"source": "font"
},
{
"char": "é",
"x": 966,
"y": 2,
"w": 13,
"h": 23,
"source": "font"
},
{
"char": "u",
"x": 981,
"y": 2,
"w": 13,
"h": 16,
"source": "font"
},
{
"char": "u",
"x": 996,
"y": 2,
"w": 13,
"h": 17,
"source": "font"
},
{
"char": "g",
"x": 1011,
"y": 2,
"w": 13,
"h": 21,
"source": "font"
},
{
"char": "g",
"x": 1026,
"y": 2,
"w": 12,
"h": 22,
"source": "font"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

View File

@@ -0,0 +1,381 @@
{
"height": 29,
"glyphs": [
{
"char": "0",
"x": 2,
"y": 2,
"w": 19,
"h": 31,
"source": "fixture"
},
{
"char": "0",
"x": 23,
"y": 2,
"w": 19,
"h": 30,
"source": "fixture"
},
{
"char": "0",
"x": 44,
"y": 2,
"w": 20,
"h": 30,
"source": "fixture"
},
{
"char": "0",
"x": 66,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "1",
"x": 86,
"y": 2,
"w": 10,
"h": 30,
"source": "fixture"
},
{
"char": "1",
"x": 98,
"y": 2,
"w": 10,
"h": 30,
"source": "fixture"
},
{
"char": "1",
"x": 110,
"y": 2,
"w": 10,
"h": 30,
"source": "fixture"
},
{
"char": "1",
"x": 122,
"y": 2,
"w": 10,
"h": 30,
"source": "fixture"
},
{
"char": "2",
"x": 134,
"y": 2,
"w": 18,
"h": 31,
"source": "fixture"
},
{
"char": "2",
"x": 154,
"y": 2,
"w": 18,
"h": 31,
"source": "fixture"
},
{
"char": "2",
"x": 174,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "2",
"x": 194,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "3",
"x": 214,
"y": 2,
"w": 19,
"h": 30,
"source": "fixture"
},
{
"char": "3",
"x": 235,
"y": 2,
"w": 19,
"h": 30,
"source": "fixture"
},
{
"char": "3",
"x": 256,
"y": 2,
"w": 19,
"h": 30,
"source": "fixture"
},
{
"char": "3",
"x": 277,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "4",
"x": 297,
"y": 2,
"w": 19,
"h": 30,
"source": "fixture"
},
{
"char": "4",
"x": 318,
"y": 2,
"w": 19,
"h": 31,
"source": "fixture"
},
{
"char": "4",
"x": 339,
"y": 2,
"w": 19,
"h": 30,
"source": "fixture"
},
{
"char": "4",
"x": 360,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "5",
"x": 380,
"y": 2,
"w": 18,
"h": 31,
"source": "fixture"
},
{
"char": "5",
"x": 400,
"y": 2,
"w": 17,
"h": 31,
"source": "fixture"
},
{
"char": "5",
"x": 419,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "5",
"x": 439,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "6",
"x": 459,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "6",
"x": 479,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "6",
"x": 499,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "6",
"x": 519,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "7",
"x": 539,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "7",
"x": 559,
"y": 2,
"w": 17,
"h": 30,
"source": "fixture"
},
{
"char": "7",
"x": 578,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "7",
"x": 598,
"y": 2,
"w": 17,
"h": 29,
"source": "fixture"
},
{
"char": "8",
"x": 617,
"y": 2,
"w": 18,
"h": 31,
"source": "fixture"
},
{
"char": "8",
"x": 637,
"y": 2,
"w": 19,
"h": 31,
"source": "fixture"
},
{
"char": "9",
"x": 658,
"y": 2,
"w": 18,
"h": 31,
"source": "fixture"
},
{
"char": "9",
"x": 678,
"y": 2,
"w": 18,
"h": 31,
"source": "fixture"
},
{
"char": "9",
"x": 698,
"y": 2,
"w": 18,
"h": 30,
"source": "fixture"
},
{
"char": "0",
"x": 718,
"y": 2,
"w": 20,
"h": 31,
"source": "font"
},
{
"char": "1",
"x": 740,
"y": 2,
"w": 11,
"h": 31,
"source": "font"
},
{
"char": "2",
"x": 753,
"y": 2,
"w": 18,
"h": 31,
"source": "font"
},
{
"char": "3",
"x": 773,
"y": 2,
"w": 19,
"h": 31,
"source": "font"
},
{
"char": "4",
"x": 794,
"y": 2,
"w": 19,
"h": 31,
"source": "font"
},
{
"char": "5",
"x": 815,
"y": 2,
"w": 17,
"h": 31,
"source": "font"
},
{
"char": "6",
"x": 834,
"y": 2,
"w": 19,
"h": 31,
"source": "font"
},
{
"char": "7",
"x": 855,
"y": 2,
"w": 17,
"h": 31,
"source": "font"
},
{
"char": "8",
"x": 874,
"y": 2,
"w": 19,
"h": 31,
"source": "font"
},
{
"char": "9",
"x": 895,
"y": 2,
"w": 19,
"h": 31,
"source": "font"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -0,0 +1,517 @@
{
"height": 30,
"glyphs": [
{
"char": "W",
"x": 2,
"y": 2,
"w": 39,
"h": 31,
"source": "font"
},
{
"char": "W",
"x": 43,
"y": 2,
"w": 40,
"h": 32,
"source": "font"
},
{
"char": "I",
"x": 85,
"y": 2,
"w": 12,
"h": 31,
"source": "font"
},
{
"char": "I",
"x": 99,
"y": 2,
"w": 13,
"h": 32,
"source": "font"
},
{
"char": "N",
"x": 114,
"y": 2,
"w": 27,
"h": 31,
"source": "font"
},
{
"char": "N",
"x": 143,
"y": 2,
"w": 28,
"h": 32,
"source": "font"
},
{
"char": "L",
"x": 173,
"y": 2,
"w": 23,
"h": 31,
"source": "font"
},
{
"char": "L",
"x": 198,
"y": 2,
"w": 24,
"h": 32,
"source": "font"
},
{
"char": "O",
"x": 224,
"y": 2,
"w": 31,
"h": 31,
"source": "font"
},
{
"char": "O",
"x": 257,
"y": 2,
"w": 32,
"h": 32,
"source": "font"
},
{
"char": "S",
"x": 291,
"y": 2,
"w": 27,
"h": 31,
"source": "font"
},
{
"char": "S",
"x": 320,
"y": 2,
"w": 28,
"h": 32,
"source": "font"
},
{
"char": "E",
"x": 350,
"y": 2,
"w": 25,
"h": 31,
"source": "font"
},
{
"char": "E",
"x": 377,
"y": 2,
"w": 25,
"h": 32,
"source": "font"
},
{
"char": ".",
"x": 404,
"y": 2,
"w": 11,
"h": 9,
"source": "font"
},
{
"char": ".",
"x": 417,
"y": 2,
"w": 12,
"h": 9,
"source": "font"
},
{
"char": "i",
"x": 431,
"y": 2,
"w": 11,
"h": 31,
"source": "font"
},
{
"char": "i",
"x": 444,
"y": 2,
"w": 11,
"h": 32,
"source": "font"
},
{
"char": "e",
"x": 457,
"y": 2,
"w": 24,
"h": 23,
"source": "font"
},
{
"char": "e",
"x": 483,
"y": 2,
"w": 25,
"h": 23,
"source": "font"
},
{
"char": "g",
"x": 510,
"y": 2,
"w": 24,
"h": 31,
"source": "font"
},
{
"char": "g",
"x": 536,
"y": 2,
"w": 25,
"h": 31,
"source": "font"
},
{
"char": "!",
"x": 563,
"y": 2,
"w": 14,
"h": 31,
"source": "font"
},
{
"char": "!",
"x": 579,
"y": 2,
"w": 15,
"h": 32,
"source": "font"
},
{
"char": "V",
"x": 596,
"y": 2,
"w": 30,
"h": 31,
"source": "font"
},
{
"char": "V",
"x": 628,
"y": 2,
"w": 31,
"h": 32,
"source": "font"
},
{
"char": "r",
"x": 661,
"y": 2,
"w": 17,
"h": 23,
"source": "font"
},
{
"char": "r",
"x": 680,
"y": 2,
"w": 17,
"h": 24,
"source": "font"
},
{
"char": "l",
"x": 699,
"y": 2,
"w": 11,
"h": 31,
"source": "font"
},
{
"char": "l",
"x": 712,
"y": 2,
"w": 11,
"h": 32,
"source": "font"
},
{
"char": "o",
"x": 725,
"y": 2,
"w": 25,
"h": 23,
"source": "font"
},
{
"char": "o",
"x": 752,
"y": 2,
"w": 26,
"h": 23,
"source": "font"
},
{
"char": "n",
"x": 780,
"y": 2,
"w": 23,
"h": 23,
"source": "font"
},
{
"char": "n",
"x": 805,
"y": 2,
"w": 24,
"h": 24,
"source": "font"
},
{
"char": "C",
"x": 831,
"y": 2,
"w": 30,
"h": 31,
"source": "font"
},
{
"char": "C",
"x": 863,
"y": 2,
"w": 30,
"h": 32,
"source": "font"
},
{
"char": "T",
"x": 895,
"y": 2,
"w": 26,
"h": 31,
"source": "font"
},
{
"char": "T",
"x": 923,
"y": 2,
"w": 27,
"h": 32,
"source": "font"
},
{
"char": "R",
"x": 952,
"y": 2,
"w": 27,
"h": 31,
"source": "font"
},
{
"char": "R",
"x": 981,
"y": 2,
"w": 28,
"h": 32,
"source": "font"
},
{
"char": "Y",
"x": 1011,
"y": 2,
"w": 29,
"h": 31,
"source": "font"
},
{
"char": "Y",
"x": 1042,
"y": 2,
"w": 30,
"h": 32,
"source": "font"
},
{
"char": "D",
"x": 1074,
"y": 2,
"w": 29,
"h": 31,
"source": "font"
},
{
"char": "D",
"x": 1105,
"y": 2,
"w": 30,
"h": 32,
"source": "font"
},
{
"char": "F",
"x": 1137,
"y": 2,
"w": 23,
"h": 31,
"source": "font"
},
{
"char": "F",
"x": 1162,
"y": 2,
"w": 23,
"h": 32,
"source": "font"
},
{
"char": "A",
"x": 1187,
"y": 2,
"w": 32,
"h": 31,
"source": "font"
},
{
"char": "A",
"x": 1221,
"y": 2,
"w": 33,
"h": 32,
"source": "font"
},
{
"char": "¡",
"x": 1256,
"y": 2,
"w": 14,
"h": 32,
"source": "font"
},
{
"char": "¡",
"x": 1272,
"y": 2,
"w": 15,
"h": 32,
"source": "font"
},
{
"char": "c",
"x": 1289,
"y": 2,
"w": 23,
"h": 23,
"source": "font"
},
{
"char": "c",
"x": 1314,
"y": 2,
"w": 24,
"h": 23,
"source": "font"
},
{
"char": "t",
"x": 1340,
"y": 2,
"w": 16,
"h": 30,
"source": "font"
},
{
"char": "t",
"x": 1358,
"y": 2,
"w": 17,
"h": 30,
"source": "font"
},
{
"char": "a",
"x": 1377,
"y": 2,
"w": 24,
"h": 23,
"source": "font"
},
{
"char": "a",
"x": 1403,
"y": 2,
"w": 25,
"h": 23,
"source": "font"
},
{
"char": "é",
"x": 1430,
"y": 2,
"w": 24,
"h": 33,
"source": "font"
},
{
"char": "é",
"x": 1456,
"y": 2,
"w": 25,
"h": 33,
"source": "font"
},
{
"char": "f",
"x": 1483,
"y": 2,
"w": 17,
"h": 31,
"source": "font"
},
{
"char": "f",
"x": 1502,
"y": 2,
"w": 17,
"h": 32,
"source": "font"
},
{
"char": "G",
"x": 1521,
"y": 2,
"w": 30,
"h": 31,
"source": "font"
},
{
"char": "G",
"x": 1553,
"y": 2,
"w": 31,
"h": 32,
"source": "font"
},
{
"char": "w",
"x": 2,
"y": 37,
"w": 34,
"h": 23,
"source": "font"
},
{
"char": "w",
"x": 38,
"y": 37,
"w": 35,
"h": 23,
"source": "font"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,373 @@
{
"height": 17,
"glyphs": [
{
"char": "0",
"x": 2,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "0",
"x": 16,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "0",
"x": 30,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "0",
"x": 44,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "1",
"x": 58,
"y": 2,
"w": 7,
"h": 19,
"source": "fixture"
},
{
"char": "1",
"x": 67,
"y": 2,
"w": 8,
"h": 19,
"source": "fixture"
},
{
"char": "1",
"x": 77,
"y": 2,
"w": 7,
"h": 19,
"source": "fixture"
},
{
"char": "1",
"x": 86,
"y": 2,
"w": 7,
"h": 18,
"source": "fixture"
},
{
"char": "2",
"x": 95,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "2",
"x": 109,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "2",
"x": 122,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "2",
"x": 136,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "3",
"x": 150,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "3",
"x": 164,
"y": 2,
"w": 11,
"h": 18,
"source": "fixture"
},
{
"char": "3",
"x": 177,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "3",
"x": 191,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "4",
"x": 205,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "4",
"x": 219,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "4",
"x": 232,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "4",
"x": 246,
"y": 2,
"w": 11,
"h": 18,
"source": "fixture"
},
{
"char": "5",
"x": 259,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "5",
"x": 272,
"y": 2,
"w": 10,
"h": 19,
"source": "fixture"
},
{
"char": "5",
"x": 284,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "5",
"x": 297,
"y": 2,
"w": 11,
"h": 18,
"source": "fixture"
},
{
"char": "6",
"x": 310,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "6",
"x": 324,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "6",
"x": 338,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "6",
"x": 352,
"y": 2,
"w": 11,
"h": 17,
"source": "fixture"
},
{
"char": "7",
"x": 365,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "7",
"x": 378,
"y": 2,
"w": 10,
"h": 19,
"source": "fixture"
},
{
"char": "8",
"x": 390,
"y": 2,
"w": 12,
"h": 19,
"source": "fixture"
},
{
"char": "8",
"x": 404,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "8",
"x": 417,
"y": 2,
"w": 12,
"h": 18,
"source": "fixture"
},
{
"char": "8",
"x": 431,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "9",
"x": 444,
"y": 2,
"w": 11,
"h": 19,
"source": "fixture"
},
{
"char": "9",
"x": 457,
"y": 2,
"w": 10,
"h": 18,
"source": "fixture"
},
{
"char": "0",
"x": 469,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
},
{
"char": "1",
"x": 483,
"y": 2,
"w": 7,
"h": 19,
"source": "font"
},
{
"char": "2",
"x": 492,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
},
{
"char": "3",
"x": 506,
"y": 2,
"w": 13,
"h": 19,
"source": "font"
},
{
"char": "4",
"x": 521,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
},
{
"char": "5",
"x": 535,
"y": 2,
"w": 11,
"h": 19,
"source": "font"
},
{
"char": "6",
"x": 548,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
},
{
"char": "7",
"x": 562,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
},
{
"char": "8",
"x": 576,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
},
{
"char": "9",
"x": 590,
"y": 2,
"w": 12,
"h": 19,
"source": "font"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,149 @@
{
"height": 28,
"glyphs": [
{
"char": "0",
"x": 2,
"y": 2,
"w": 24,
"h": 28,
"source": "fixture"
},
{
"char": "0",
"x": 28,
"y": 2,
"w": 24,
"h": 28,
"source": "fixture"
},
{
"char": "0",
"x": 54,
"y": 2,
"w": 24,
"h": 28,
"source": "fixture"
},
{
"char": "0",
"x": 80,
"y": 2,
"w": 24,
"h": 28,
"source": "fixture"
},
{
"char": "5",
"x": 106,
"y": 2,
"w": 24,
"h": 29,
"source": "fixture"
},
{
"char": "5",
"x": 132,
"y": 2,
"w": 23,
"h": 29,
"source": "fixture"
},
{
"char": "5",
"x": 157,
"y": 2,
"w": 24,
"h": 29,
"source": "fixture"
},
{
"char": "5",
"x": 183,
"y": 2,
"w": 24,
"h": 29,
"source": "fixture"
},
{
"char": "0",
"x": 209,
"y": 2,
"w": 25,
"h": 30,
"source": "font"
},
{
"char": "1",
"x": 236,
"y": 2,
"w": 16,
"h": 30,
"source": "font"
},
{
"char": "2",
"x": 254,
"y": 2,
"w": 25,
"h": 30,
"source": "font"
},
{
"char": "3",
"x": 281,
"y": 2,
"w": 24,
"h": 30,
"source": "font"
},
{
"char": "4",
"x": 307,
"y": 2,
"w": 25,
"h": 30,
"source": "font"
},
{
"char": "5",
"x": 334,
"y": 2,
"w": 25,
"h": 30,
"source": "font"
},
{
"char": "6",
"x": 361,
"y": 2,
"w": 26,
"h": 30,
"source": "font"
},
{
"char": "7",
"x": 389,
"y": 2,
"w": 24,
"h": 30,
"source": "font"
},
{
"char": "8",
"x": 415,
"y": 2,
"w": 24,
"h": 30,
"source": "font"
},
{
"char": "9",
"x": 441,
"y": 2,
"w": 27,
"h": 30,
"source": "font"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,132 @@
{
"width": 120,
"height": 68,
"cols": 5,
"keys": [
"0-CB",
"0-RM",
"0-SZ",
"0-TC",
"0-TW",
"1-CB",
"1-RM",
"1-SZ",
"1-TC",
"1-TW",
"2-CB",
"2-RM",
"2-SZ",
"2-TC",
"2-TW",
"3-CB",
"3-RM",
"3-SZ",
"3-TC",
"3-TW",
"4-CB",
"4-RM",
"4-SZ",
"4-TC",
"4-TW",
"5-CB",
"5-RM",
"5-SZ",
"5-TC",
"5-TW",
"6-CB",
"6-RM",
"6-SZ",
"6-TC",
"6-TW",
"7-CB",
"7-RM",
"7-SZ",
"7-TC",
"7-TW",
"8-CB",
"8-RM",
"8-SZ",
"8-TC",
"8-TW",
"9-CB",
"9-RM",
"9-SZ",
"9-TC",
"9-TW",
"10-CB",
"10-RM",
"10-SZ",
"10-TC",
"10-TW",
"11-CB",
"11-RM",
"11-SZ",
"11-TC",
"11-TW",
"12-CB",
"12-RM",
"12-SZ",
"12-TC",
"12-TW",
"13-CB",
"13-RM",
"13-SZ",
"13-TC",
"13-TW",
"14-CB",
"14-RM",
"14-SZ",
"14-TC",
"14-TW",
"15-CB",
"15-RM",
"15-SZ",
"15-TC",
"15-TW",
"16-CB",
"16-RM",
"16-SZ",
"16-TC",
"16-TW",
"17-CB",
"17-RM",
"17-SZ",
"17-TC",
"17-TW",
"18-CB",
"18-RM",
"18-SZ",
"18-TC",
"18-TW",
"19-CB",
"19-RM",
"19-SZ",
"19-TC",
"19-TW",
"20-CB",
"20-RM",
"20-SZ",
"20-TC",
"20-TW",
"21-CB",
"21-RM",
"21-SZ",
"21-TC",
"21-TW",
"22-CB",
"22-RM",
"22-SZ",
"22-TC",
"22-TW",
"23-CB",
"23-RM",
"23-SZ",
"23-TC",
"23-TW",
"24-CB",
"24-RM",
"24-SZ",
"24-TC",
"24-TW"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB