@glissade/core 0.11.0 → 0.12.0-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clips.d.ts +114 -0
- package/dist/clips.js +208 -0
- package/dist/cmap.js +112 -0
- package/dist/font-ingest.d.ts +154 -0
- package/dist/font-ingest.js +282 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +3 -112
- package/dist/sidecar.d.ts +5 -470
- package/dist/sidecar.js +2 -967
- package/dist/studioHost.d.ts +4 -1
- package/dist/studioHost.js +2 -1
- package/dist/targetRef.d.ts +33 -0
- package/dist/targetRef.js +992 -0
- package/dist/timeline.d.ts +138 -0
- package/dist/track.d.ts +329 -0
- package/package.json +12 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { t as parseCmap } from "./cmap.js";
|
|
2
|
+
//#region src/fontIngest.ts
|
|
3
|
+
/**
|
|
4
|
+
* @glissade/core/font-ingest — the font INGESTION front door (DESIGN.md §3.6).
|
|
5
|
+
*
|
|
6
|
+
* This is the heavy, EXPORT/prepare-path-only sibling of the light, embed-safe
|
|
7
|
+
* `fontRegistry.ts`. It owns the byte work that must never reach the runtime
|
|
8
|
+
* document or the browser bundle:
|
|
9
|
+
*
|
|
10
|
+
* - magic-byte sniffing (ttf / otf / ttc → straight to Skia; woff/woff2 →
|
|
11
|
+
* decoded in-process to a plain sfnt),
|
|
12
|
+
* - STATIC variable-axis instancing (a fixed axis tuple → ONE static sfnt,
|
|
13
|
+
* content-hashed so identical inputs yield byte-identical output), and
|
|
14
|
+
* - eager `parseCmap` so `registerFont` returns the covered code points and a
|
|
15
|
+
* build-time `covers(text)` predicate.
|
|
16
|
+
*
|
|
17
|
+
* The single heavy dependency — `subset-font` (a wasm hb-subset wrapper that
|
|
18
|
+
* also decodes woff2 via `fontverter`) — is reached ONLY through a dynamic
|
|
19
|
+
* `import()` inside this module, which lives on its own tsdown entry
|
|
20
|
+
* (`@glissade/core/font-ingest`). `core/index.ts` never imports it, so the
|
|
21
|
+
* decoder + instancer tree-shake completely out of the embed (asserted by the
|
|
22
|
+
* §4.4 leak-guard in scripts/check-size.mjs).
|
|
23
|
+
*
|
|
24
|
+
* Determinism: woff2 → sfnt and axis instancing run ONCE here, at ingest time,
|
|
25
|
+
* never inside evaluate(); the result is a content-hashed static sfnt with a
|
|
26
|
+
* stable identity. No new field flows through FontSpec/DisplayList, so the
|
|
27
|
+
* evaluate() byte output is unchanged.
|
|
28
|
+
*/
|
|
29
|
+
const MAGIC = {
|
|
30
|
+
/** 0x00010000 (TrueType) */ ttf1: 65536,
|
|
31
|
+
/** 'true' */ trueTag: 1953658213,
|
|
32
|
+
/** 'OTTO' */ otto: 1330926671,
|
|
33
|
+
/** 'ttcf' */ ttcf: 1953784678,
|
|
34
|
+
/** 'wOFF' */ woff: 2001684038,
|
|
35
|
+
/** 'wOF2' */ woff2: 2001684018
|
|
36
|
+
};
|
|
37
|
+
function asUint8(src) {
|
|
38
|
+
return src instanceof Uint8Array ? src : new Uint8Array(src);
|
|
39
|
+
}
|
|
40
|
+
/** Sniff the leading 4 bytes; throws on input that is not a recognized font. */
|
|
41
|
+
function sniffFontFormat(src) {
|
|
42
|
+
const u8 = asUint8(src);
|
|
43
|
+
if (u8.byteLength < 4) throw new FontIngestError("input is too short to be a font (need ≥ 4 magic bytes)");
|
|
44
|
+
const t = (u8[0] << 24 | u8[1] << 16 | u8[2] << 8 | u8[3]) >>> 0;
|
|
45
|
+
switch (t) {
|
|
46
|
+
case MAGIC.ttf1:
|
|
47
|
+
case MAGIC.trueTag: return "truetype";
|
|
48
|
+
case MAGIC.otto: return "opentype";
|
|
49
|
+
case MAGIC.ttcf: return "collection";
|
|
50
|
+
case MAGIC.woff: return "woff";
|
|
51
|
+
case MAGIC.woff2: return "woff2";
|
|
52
|
+
default: throw new FontIngestError(`unrecognized font magic 0x${t.toString(16).padStart(8, "0")} — expected ttf/otf/ttc/woff/woff2`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
var FontIngestError = class extends Error {
|
|
56
|
+
constructor(message) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "FontIngestError";
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/** FNV-1a (32-bit) over the bytes → 8-hex-char content hash (DESIGN §3.5 family). */
|
|
62
|
+
function fnv1a(bytes) {
|
|
63
|
+
let h = 2166136261;
|
|
64
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
65
|
+
h ^= bytes[i];
|
|
66
|
+
h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
|
|
67
|
+
}
|
|
68
|
+
return h.toString(16).padStart(8, "0");
|
|
69
|
+
}
|
|
70
|
+
function codePointsOf(text) {
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const ch of text) {
|
|
73
|
+
const cp = ch.codePointAt(0);
|
|
74
|
+
if (cp !== void 0) out.push(cp);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
let subsetFontPromise;
|
|
79
|
+
async function loadSubsetFont() {
|
|
80
|
+
subsetFontPromise ??= import("subset-font").then((m) => m.default ?? m, (err) => {
|
|
81
|
+
throw new FontIngestError(`font ingestion needs the optional 'subset-font' dependency for woff2 decode / axis instancing — install it. (${String(err?.message ?? err)})`);
|
|
82
|
+
});
|
|
83
|
+
return subsetFontPromise;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build the "retain every code point" string from a font's own cmap — used to
|
|
87
|
+
* run hb-subset purely as a woff2 DECODER / variable-axis INSTANCER without
|
|
88
|
+
* dropping a single glyph (subsetting is the DEFERRED feature). We feed back
|
|
89
|
+
* exactly the code points the source already covers.
|
|
90
|
+
*/
|
|
91
|
+
function retainAllText(coverage) {
|
|
92
|
+
let s = "";
|
|
93
|
+
for (const cp of coverage) s += String.fromCodePoint(cp);
|
|
94
|
+
return s;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Ingest one face: sniff → (woff2 decode) → (instance at `axes`) → a static
|
|
98
|
+
* sfnt + content hash + coverage. The heavy path (woff2 / instancing) runs
|
|
99
|
+
* hb-subset; a plain ttf/otf with no axes skips it entirely (zero-dep fast
|
|
100
|
+
* path), keeping the common case off the wasm boundary.
|
|
101
|
+
*/
|
|
102
|
+
async function ingestFont(init) {
|
|
103
|
+
const sourceFormat = sniffFontFormat(init.src);
|
|
104
|
+
const input = asUint8(init.src);
|
|
105
|
+
const weight = init.weight ?? 400;
|
|
106
|
+
const style = init.style ?? "normal";
|
|
107
|
+
const fallback = init.fallback ? [...init.fallback] : [];
|
|
108
|
+
const needsDecode = sourceFormat === "woff" || sourceFormat === "woff2";
|
|
109
|
+
const needsInstance = init.axes !== void 0 && Object.keys(init.axes).length > 0;
|
|
110
|
+
let bytes;
|
|
111
|
+
if (!needsDecode && !needsInstance) bytes = input;
|
|
112
|
+
else {
|
|
113
|
+
const subsetFont = await loadSubsetFont();
|
|
114
|
+
let decoded = input;
|
|
115
|
+
if (needsDecode) decoded = await subsetFont(input, retainAllText(parseCmap(input)), { targetFormat: "sfnt" });
|
|
116
|
+
if (needsInstance) {
|
|
117
|
+
const coverage = parseCmap(decoded);
|
|
118
|
+
decoded = await subsetFont(decoded, retainAllText(coverage), {
|
|
119
|
+
targetFormat: "sfnt",
|
|
120
|
+
variationAxes: init.axes
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
bytes = decoded;
|
|
124
|
+
}
|
|
125
|
+
const coverage = parseCmap(bytes);
|
|
126
|
+
const hash = fnv1a(bytes);
|
|
127
|
+
return {
|
|
128
|
+
family: init.family,
|
|
129
|
+
weight,
|
|
130
|
+
style,
|
|
131
|
+
bytes,
|
|
132
|
+
sourceFormat,
|
|
133
|
+
hash,
|
|
134
|
+
coverage,
|
|
135
|
+
fallback,
|
|
136
|
+
covers(text) {
|
|
137
|
+
return codePointsOf(text).every((cp) => coverage.has(cp));
|
|
138
|
+
},
|
|
139
|
+
missing(text) {
|
|
140
|
+
const miss = /* @__PURE__ */ new Set();
|
|
141
|
+
for (const cp of codePointsOf(text)) if (!coverage.has(cp)) miss.add(cp);
|
|
142
|
+
return [...miss].sort((a, b) => a - b);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* `registerFont(...)` — the merged front door. Ingests `init` and records it in
|
|
148
|
+
* `store` (the in-process registry the render path consumes). Returns the
|
|
149
|
+
* ingested face (bytes + coverage + `covers()`); the caller decides whether to
|
|
150
|
+
* persist the static sfnt to disk (content-hashed url) and/or feed Skia.
|
|
151
|
+
*/
|
|
152
|
+
async function registerFont(init, store) {
|
|
153
|
+
const result = await ingestFont(init);
|
|
154
|
+
const face = {
|
|
155
|
+
...result,
|
|
156
|
+
faceRef(url) {
|
|
157
|
+
return {
|
|
158
|
+
url,
|
|
159
|
+
...result.weight !== 400 ? { weight: result.weight } : {},
|
|
160
|
+
...result.style !== "normal" ? { style: result.style } : {}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
store?.add(face);
|
|
165
|
+
return face;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* A tiny in-process store the export/prepare path fills via `registerFont` and
|
|
169
|
+
* the renderer drains. Groups faces by family and remembers each family's
|
|
170
|
+
* declared fallback chain — the same shape `buildFontRegistry` consumes, so the
|
|
171
|
+
* programmatic and document-driven paths converge on one registry.
|
|
172
|
+
*/
|
|
173
|
+
var FontStore = class {
|
|
174
|
+
byFamily = /* @__PURE__ */ new Map();
|
|
175
|
+
fallbacks = /* @__PURE__ */ new Map();
|
|
176
|
+
add(face) {
|
|
177
|
+
const list = this.byFamily.get(face.family) ?? [];
|
|
178
|
+
list.push(face);
|
|
179
|
+
this.byFamily.set(face.family, list);
|
|
180
|
+
if (face.fallback.length > 0) this.fallbacks.set(face.family, [...face.fallback]);
|
|
181
|
+
}
|
|
182
|
+
families() {
|
|
183
|
+
return [...this.byFamily.keys()];
|
|
184
|
+
}
|
|
185
|
+
facesOf(family) {
|
|
186
|
+
return this.byFamily.get(family) ?? [];
|
|
187
|
+
}
|
|
188
|
+
fallbackOf(family) {
|
|
189
|
+
return this.fallbacks.get(family) ?? [];
|
|
190
|
+
}
|
|
191
|
+
/** Every ingested face across every family (the renderer registers these). */
|
|
192
|
+
all() {
|
|
193
|
+
const out = [];
|
|
194
|
+
for (const list of this.byFamily.values()) out.push(...list);
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
function font(family) {
|
|
199
|
+
const sources = [];
|
|
200
|
+
const fallback = [];
|
|
201
|
+
let weightList;
|
|
202
|
+
let axes;
|
|
203
|
+
let variable = false;
|
|
204
|
+
const api = {
|
|
205
|
+
src(path, opts) {
|
|
206
|
+
sources.push({
|
|
207
|
+
path,
|
|
208
|
+
...opts?.weight !== void 0 ? { weight: opts.weight } : {},
|
|
209
|
+
...opts?.style !== void 0 ? { style: opts.style } : {}
|
|
210
|
+
});
|
|
211
|
+
return api;
|
|
212
|
+
},
|
|
213
|
+
bytes(data, opts) {
|
|
214
|
+
sources.push({
|
|
215
|
+
bytes: data,
|
|
216
|
+
...opts?.weight !== void 0 ? { weight: opts.weight } : {},
|
|
217
|
+
...opts?.style !== void 0 ? { style: opts.style } : {}
|
|
218
|
+
});
|
|
219
|
+
return api;
|
|
220
|
+
},
|
|
221
|
+
weights(ws) {
|
|
222
|
+
weightList = [...ws];
|
|
223
|
+
return api;
|
|
224
|
+
},
|
|
225
|
+
fallback(families) {
|
|
226
|
+
fallback.push(...families);
|
|
227
|
+
return api;
|
|
228
|
+
},
|
|
229
|
+
variable() {
|
|
230
|
+
variable = true;
|
|
231
|
+
return api;
|
|
232
|
+
},
|
|
233
|
+
axis(tag, value) {
|
|
234
|
+
(axes ??= {})[tag] = value;
|
|
235
|
+
return api;
|
|
236
|
+
},
|
|
237
|
+
build() {
|
|
238
|
+
let resolved = sources;
|
|
239
|
+
if (weightList && sources.length === 1 && sources[0].weight === void 0) {
|
|
240
|
+
const base = sources[0];
|
|
241
|
+
resolved = weightList.map((w) => ({
|
|
242
|
+
...base,
|
|
243
|
+
weight: w
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
family,
|
|
248
|
+
sources: resolved,
|
|
249
|
+
fallback,
|
|
250
|
+
variable,
|
|
251
|
+
...axes !== void 0 ? { axes } : {}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
return api;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Ingest a whole `FontPlan` into `store`. `read` resolves a source path to bytes
|
|
259
|
+
* (the I/O seam — the CLI reads files, a bundler plugin could fetch). Every
|
|
260
|
+
* source becomes one ingested face under the plan's family; the family fallback
|
|
261
|
+
* is attached to the first face so the store records it once.
|
|
262
|
+
*/
|
|
263
|
+
async function buildFontPlan(plan, read, store) {
|
|
264
|
+
const out = [];
|
|
265
|
+
for (let i = 0; i < plan.sources.length; i++) {
|
|
266
|
+
const entry = plan.sources[i];
|
|
267
|
+
const src = entry.bytes ?? (entry.path !== void 0 ? await read(entry.path) : void 0);
|
|
268
|
+
if (src === void 0) throw new FontIngestError(`font('${plan.family}') source #${i} has neither a path nor bytes`);
|
|
269
|
+
const face = await registerFont({
|
|
270
|
+
family: plan.family,
|
|
271
|
+
src,
|
|
272
|
+
...entry.weight !== void 0 ? { weight: entry.weight } : {},
|
|
273
|
+
...entry.style !== void 0 ? { style: entry.style } : {},
|
|
274
|
+
...plan.axes !== void 0 ? { axes: plan.axes } : {},
|
|
275
|
+
...i === 0 && plan.fallback.length > 0 ? { fallback: plan.fallback } : {}
|
|
276
|
+
}, store);
|
|
277
|
+
out.push(face);
|
|
278
|
+
}
|
|
279
|
+
return out;
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
export { FontIngestError, FontStore, buildFontPlan, font, ingestFont, registerFont, sniffFontFormat };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as easingDerivatives, A as getValueType, B as RetargetSpring, C as UnknownValueTypeError, D as Vec2, E as ValueTypeInferenceError, F as registerValueType, G as springEasingDerivative, H as SpringEase, I as stringType, J as EaseSpec, K as springPresets, L as vec2ArcType, M as numberType, N as paintType, O as booleanType, P as pathType, Q as cubicBezierDerivative, R as vec2Equals, S as PathValue, T as ValueTypeId, U as spring, V as SpringConfig, W as springEasing, X as UnknownEasingError, Y as EasingFn, Z as cubicBezier, _ as MeshInterpolation, a as key, b as Paint, c as sampleTrack, d as track, et as easings, f as validateTrack, g as HandoffKind, h as GradientInterpolation, i as TrackValidationError, j as inferValueType, k as colorType, l as springTo, m as ColorStop, n as KeyOpts, o as resolveEase, p as velocityAt, q as DEFAULT_EASE, r as Track, s as resolveEaseDerivative, t as Key, tt as namedEasing, u as stagger, v as MeshPaint, w as ValueType, x as PathContour, y as MeshPoint, z as vec2Type } from "./track.js";
|
|
2
|
+
import { a as isEditableNodeId, i as UnresolvableTargetError, n as TargetCarrier, o as resolveTweenTarget, r as TweenTarget, s as targetNodeId, t as TARGET_PATH } from "./targetRef.js";
|
|
3
|
+
import { _ as setDevWarning, a as FontFaceRef, c as Marker, d as TimelineValidationError, f as audioOffsetSamples, g as emitDevWarning, h as DevWarning, i as CompiledTimeline, l as Timeline, m as isDurationEditable, n as AudioClip, o as GainEnvelope, p as compileTimeline, r as ChildEntry, s as Json, t as AssetRef, u as TimelineInit } from "./timeline.js";
|
|
4
|
+
import { _ as setSidecarTrack, a as SidecarOrphan, c as SidecarVersionError, d as emptySidecar, f as hashKeys, g as normalizeEditedKeys, h as migrateSidecar, i as SidecarDocV1, l as assignKeyIds, m as mergeSidecarDetailed, n as OrphanReason, o as SidecarTimelineEntry, p as mergeSidecar, r as SidecarDoc, s as SidecarTrackEntry, t as MergeResult, u as deleteSidecarTrack } from "./sidecar.js";
|
|
2
5
|
|
|
3
6
|
//#region src/signal.d.ts
|
|
4
7
|
|
|
@@ -357,4 +360,4 @@ interface CheckpointedSim {
|
|
|
357
360
|
}
|
|
358
361
|
declare function bakeCheckpointed<W, S = W>(cfg: CheckpointedBakeConfig<W, S>): CheckpointedSim;
|
|
359
362
|
//#endregion
|
|
360
|
-
export { type AssetRef, type AudioClip, type BakeConfig, BakeError, type BindTarget, type BindableSignal, type BoundTimeline, type CheckpointedBakeConfig, type CheckpointedSim, type ChildEntry, CircularDependencyError, ColorParseError, type ColorStop, type CompiledTimeline, type CoverageReport, type CurveSampler, DEFAULT_EASE, type DevWarning, type EaseSpec, type EasingFn, type Equals, type FontFaceRef, type FontMode, type FontRegistry, type FontUsage, FontValidationError, type GainEnvelope, type GradientInterpolation, type HandoffKind, type Json, type Key, type KeyOpts, type Marker, type MergeResult, type MissingGlyphs, type OkLab, type OrphanReason, type Paint, type PathContour, type PathValue, type Playhead, type Position, PositionError, type ReadonlySignal, type ResolvedFace, type RetargetSpring, type Rgba, type Rng, type Scheduler, type SidecarDoc, type SidecarDocV1, type SidecarOrphan, type SidecarTimelineEntry, type SidecarTrackEntry, SidecarVersionError, type Signal, type SignalOptions, type SpringConfig, type SpringEase, TARGET_PATH, type TargetCarrier, type Timeline, type TimelineBuilder, type TimelineInit, TimelineValidationError, type Track, TrackValidationError, type TweenOpts, type TweenTarget, UnboundTargetError, UnknownEasingError, UnknownValueTypeError, UnresolvableTargetError, type ValidateFontsOptions, type ValueType, type ValueTypeId, ValueTypeInferenceError, type Vec2, type Vec2Signal, WriteDuringEvaluationError, assignKeyIds, audioOffsetSamples, bake, bakeCheckpointed, batch, beginReadPhase, bindTimeline, booleanType, buildFontRegistry, buildTimeline, colorType, compileTimeline, computed, createPlayhead, cubicBezier, cubicBezierDerivative, deleteSidecarTrack, easingDerivatives, easings, emitDevWarning, emptySidecar, endReadPhase, evaluateAt, formatColor, getTimelineCallbacks, getValueType, hashKeys, inReadPhase, inferValueType, isDurationEditable, isEditableNodeId, isExemptFamily, key, lerpColor, mergeSidecar, mergeSidecarDetailed, migrateSidecar, namedEasing, normalizeEditedKeys, numberType, oklabToRgba, paintType, parseCmap, parseColor, pathType, random, registerValueType, resolveEase, resolveEaseDerivative, resolveTweenTarget, rgbaToOklab, sampleTrack, setDevWarning, setScheduler, setSidecarTrack, signal, spring, springEasing, springEasingDerivative, springPresets, springTo, stagger, stringType, synchronousScheduler, targetNodeId, timeline, track, untracked, validateFonts, validateTrack, vec2ArcType, vec2Equals, vec2Signal, vec2Type, velocityAt };
|
|
363
|
+
export { type AssetRef, type AudioClip, type BakeConfig, BakeError, type BindTarget, type BindableSignal, type BoundTimeline, type CheckpointedBakeConfig, type CheckpointedSim, type ChildEntry, CircularDependencyError, ColorParseError, type ColorStop, type CompiledTimeline, type CoverageReport, type CurveSampler, DEFAULT_EASE, type DevWarning, type EaseSpec, type EasingFn, type Equals, type FontFaceRef, type FontMode, type FontRegistry, type FontUsage, FontValidationError, type GainEnvelope, type GradientInterpolation, type HandoffKind, type Json, type Key, type KeyOpts, type Marker, type MergeResult, type MeshInterpolation, type MeshPaint, type MeshPoint, type MissingGlyphs, type OkLab, type OrphanReason, type Paint, type PathContour, type PathValue, type Playhead, type Position, PositionError, type ReadonlySignal, type ResolvedFace, type RetargetSpring, type Rgba, type Rng, type Scheduler, type SidecarDoc, type SidecarDocV1, type SidecarOrphan, type SidecarTimelineEntry, type SidecarTrackEntry, SidecarVersionError, type Signal, type SignalOptions, type SpringConfig, type SpringEase, TARGET_PATH, type TargetCarrier, type Timeline, type TimelineBuilder, type TimelineInit, TimelineValidationError, type Track, TrackValidationError, type TweenOpts, type TweenTarget, UnboundTargetError, UnknownEasingError, UnknownValueTypeError, UnresolvableTargetError, type ValidateFontsOptions, type ValueType, type ValueTypeId, ValueTypeInferenceError, type Vec2, type Vec2Signal, WriteDuringEvaluationError, assignKeyIds, audioOffsetSamples, bake, bakeCheckpointed, batch, beginReadPhase, bindTimeline, booleanType, buildFontRegistry, buildTimeline, colorType, compileTimeline, computed, createPlayhead, cubicBezier, cubicBezierDerivative, deleteSidecarTrack, easingDerivatives, easings, emitDevWarning, emptySidecar, endReadPhase, evaluateAt, formatColor, getTimelineCallbacks, getValueType, hashKeys, inReadPhase, inferValueType, isDurationEditable, isEditableNodeId, isExemptFamily, key, lerpColor, mergeSidecar, mergeSidecarDetailed, migrateSidecar, namedEasing, normalizeEditedKeys, numberType, oklabToRgba, paintType, parseCmap, parseColor, pathType, random, registerValueType, resolveEase, resolveEaseDerivative, resolveTweenTarget, rgbaToOklab, sampleTrack, setDevWarning, setScheduler, setSidecarTrack, signal, spring, springEasing, springEasingDerivative, springPresets, springTo, stagger, stringType, synchronousScheduler, targetNodeId, timeline, track, untracked, validateFonts, validateTrack, vec2ArcType, vec2Equals, vec2Signal, vec2Type, velocityAt };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ColorParseError, B as springEasingDerivative, C as paintType, D as vec2ArcType, E as stringType, F as rgbaToOklab, G as cubicBezierDerivative, H as DEFAULT_EASE, I as emitDevWarning, J as namedEasing, K as easingDerivatives, L as setDevWarning, M as lerpColor, N as oklabToRgba, O as vec2Equals, P as parseColor, R as spring, S as numberType, T as registerValueType, U as UnknownEasingError, V as springPresets, W as cubicBezier, _ as ValueTypeInferenceError, a as targetNodeId, b as getValueType, c as resolveEase, d as springTo, f as stagger, g as UnknownValueTypeError, h as velocityAt, i as resolveTweenTarget, j as formatColor, k as vec2Type, l as resolveEaseDerivative, m as validateTrack, n as UnresolvableTargetError, o as TrackValidationError, p as track, q as easings, r as isEditableNodeId, s as key, t as TARGET_PATH, u as sampleTrack, v as booleanType, w as pathType, x as inferValueType, y as colorType, z as springEasing } from "./targetRef.js";
|
|
2
|
+
import { t as parseCmap } from "./cmap.js";
|
|
3
|
+
import { a as hashKeys, c as migrateSidecar, d as TimelineValidationError, f as audioOffsetSamples, h as timeline$1, i as emptySidecar, l as normalizeEditedKeys, m as isDurationEditable, n as assignKeyIds, o as mergeSidecar, p as compileTimeline, r as deleteSidecarTrack, s as mergeSidecarDetailed, t as SidecarVersionError, u as setSidecarTrack } from "./sidecar.js";
|
|
2
4
|
//#region src/ticker.ts
|
|
3
5
|
/** The default scheduler: flush synchronously, preserving pre-ticker timing. */
|
|
4
6
|
const synchronousScheduler = (flush) => flush();
|
|
@@ -448,117 +450,6 @@ function buildFontRegistry(assets) {
|
|
|
448
450
|
};
|
|
449
451
|
}
|
|
450
452
|
//#endregion
|
|
451
|
-
//#region src/cmap.ts
|
|
452
|
-
/**
|
|
453
|
-
* Minimal sfnt `cmap` reader (DESIGN.md §3.6 glyph-coverage check) — pure,
|
|
454
|
-
* byte-only, ZERO DOM / ZERO Node. Given the raw bytes of a TrueType/OpenType
|
|
455
|
-
* (or the first table of a TTC) font, returns the set of Unicode code points
|
|
456
|
-
* the font claims to cover. I/O (reading the file / fetching the URL) happens
|
|
457
|
-
* at the call site; this function only parses bytes.
|
|
458
|
-
*
|
|
459
|
-
* Hand-rolled (no font-parsing dependency) to keep core zero-dep and in budget.
|
|
460
|
-
* It covers the two cmap subtable formats that matter in practice:
|
|
461
|
-
* - format 4 (segment mapping to delta values — the BMP workhorse)
|
|
462
|
-
* - format 12 (segmented coverage — astral planes, e.g. emoji)
|
|
463
|
-
* Malformed or unsupported input yields an empty set; it never throws or hangs.
|
|
464
|
-
*/
|
|
465
|
-
const PLATFORM_UNICODE = 0;
|
|
466
|
-
const PLATFORM_WINDOWS = 3;
|
|
467
|
-
/** Read a big-endian u16 with bounds checking (0 when out of range). */
|
|
468
|
-
function u16(dv, off) {
|
|
469
|
-
return off + 2 <= dv.byteLength ? dv.getUint16(off) : 0;
|
|
470
|
-
}
|
|
471
|
-
function u32(dv, off) {
|
|
472
|
-
return off + 4 <= dv.byteLength ? dv.getUint32(off) : 0;
|
|
473
|
-
}
|
|
474
|
-
/** Rank a (platform, encoding) cmap subtable: higher = better Unicode coverage. */
|
|
475
|
-
function encodingScore(platform, encoding) {
|
|
476
|
-
if (platform === PLATFORM_WINDOWS && encoding === 10) return 5;
|
|
477
|
-
if (platform === PLATFORM_UNICODE && encoding >= 4) return 5;
|
|
478
|
-
if (platform === PLATFORM_WINDOWS && encoding === 1) return 3;
|
|
479
|
-
if (platform === PLATFORM_UNICODE) return 3;
|
|
480
|
-
return 0;
|
|
481
|
-
}
|
|
482
|
-
function parseFormat4(dv, base, into) {
|
|
483
|
-
const segCountX2 = u16(dv, base + 6);
|
|
484
|
-
const segCount = segCountX2 >> 1;
|
|
485
|
-
const endOff = base + 14;
|
|
486
|
-
const startOff = endOff + segCountX2 + 2;
|
|
487
|
-
const deltaOff = startOff + segCountX2;
|
|
488
|
-
const rangeOff = deltaOff + segCountX2;
|
|
489
|
-
for (let i = 0; i < segCount; i++) {
|
|
490
|
-
const end = u16(dv, endOff + i * 2);
|
|
491
|
-
const start = u16(dv, startOff + i * 2);
|
|
492
|
-
if (start > end) continue;
|
|
493
|
-
const idDelta = u16(dv, deltaOff + i * 2);
|
|
494
|
-
const idRangeOffset = u16(dv, rangeOff + i * 2);
|
|
495
|
-
for (let c = start; c <= end; c++) {
|
|
496
|
-
if (c === 65535) continue;
|
|
497
|
-
let glyph;
|
|
498
|
-
if (idRangeOffset === 0) glyph = c + idDelta & 65535;
|
|
499
|
-
else {
|
|
500
|
-
const g = u16(dv, rangeOff + i * 2 + idRangeOffset + (c - start) * 2);
|
|
501
|
-
glyph = g === 0 ? 0 : g + idDelta & 65535;
|
|
502
|
-
}
|
|
503
|
-
if (glyph !== 0) into.add(c);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
function parseFormat12(dv, base, into) {
|
|
508
|
-
const declared = u32(dv, base + 12);
|
|
509
|
-
const maxGroups = Math.max(0, Math.floor((dv.byteLength - (base + 16)) / 12));
|
|
510
|
-
const nGroups = Math.min(declared, maxGroups);
|
|
511
|
-
let off = base + 16;
|
|
512
|
-
for (let i = 0; i < nGroups; i++, off += 12) {
|
|
513
|
-
const startChar = u32(dv, off);
|
|
514
|
-
const endChar = u32(dv, off + 4);
|
|
515
|
-
const startGlyph = u32(dv, off + 8);
|
|
516
|
-
if (startChar > endChar) continue;
|
|
517
|
-
if (endChar - startChar > 2097152) continue;
|
|
518
|
-
for (let c = startChar; c <= endChar; c++) if (startGlyph + (c - startChar) !== 0) into.add(c);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
function parseCmap(bytes) {
|
|
522
|
-
const out = /* @__PURE__ */ new Set();
|
|
523
|
-
try {
|
|
524
|
-
const dv = ArrayBuffer.isView(bytes) ? new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength) : new DataView(bytes);
|
|
525
|
-
if (dv.byteLength < 12) return out;
|
|
526
|
-
let sfntOff = 0;
|
|
527
|
-
if (u32(dv, 0) === 1953784678) sfntOff = u32(dv, 12);
|
|
528
|
-
const numTables = u16(dv, sfntOff + 4);
|
|
529
|
-
let cmapOff = 0;
|
|
530
|
-
for (let i = 0; i < numTables; i++) {
|
|
531
|
-
const rec = sfntOff + 12 + i * 16;
|
|
532
|
-
if (u32(dv, rec) === 1668112752) {
|
|
533
|
-
cmapOff = u32(dv, rec + 8);
|
|
534
|
-
break;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
if (cmapOff === 0) return out;
|
|
538
|
-
const nSub = u16(dv, cmapOff + 2);
|
|
539
|
-
let bestOff = 0;
|
|
540
|
-
let bestScore = -1;
|
|
541
|
-
for (let i = 0; i < nSub; i++) {
|
|
542
|
-
const rec = cmapOff + 4 + i * 8;
|
|
543
|
-
const platform = u16(dv, rec);
|
|
544
|
-
const encoding = u16(dv, rec + 2);
|
|
545
|
-
const subOff = cmapOff + u32(dv, rec + 4);
|
|
546
|
-
const score = encodingScore(platform, encoding);
|
|
547
|
-
if (score > bestScore && subOff + 2 <= dv.byteLength) {
|
|
548
|
-
bestScore = score;
|
|
549
|
-
bestOff = subOff;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
if (bestScore < 0) return out;
|
|
553
|
-
const format = u16(dv, bestOff);
|
|
554
|
-
if (format === 4) parseFormat4(dv, bestOff, out);
|
|
555
|
-
else if (format === 12) parseFormat12(dv, bestOff, out);
|
|
556
|
-
} catch {
|
|
557
|
-
return out;
|
|
558
|
-
}
|
|
559
|
-
return out;
|
|
560
|
-
}
|
|
561
|
-
//#endregion
|
|
562
453
|
//#region src/fontValidation.ts
|
|
563
454
|
/**
|
|
564
455
|
* Font validation (DESIGN.md §3.6) — the family-level + glyph-coverage check.
|