@clypra/engine 1.3.0 → 1.3.1

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/index.cjs CHANGED
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/index.ts
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
+ AnimationSchema: () => AnimationSchema,
22
23
  COMPOSITION_PRESETS: () => COMPOSITION_PRESETS,
23
24
  CUSTOM_ENGINE_IDS: () => CUSTOM_ENGINE_IDS,
24
25
  CanvasDevice: () => CanvasDevice,
@@ -32,8 +33,19 @@ __export(index_exports, {
32
33
  ENGINE_ID_TO_LEGACY: () => ENGINE_ID_TO_LEGACY,
33
34
  ENTRANCE_PRESETS: () => ENTRANCE_PRESETS,
34
35
  EXIT_PRESETS: () => EXIT_PRESETS,
36
+ EffectBevelSchema: () => EffectBevelSchema,
37
+ EffectFillSchema: () => EffectFillSchema,
38
+ EffectFullDefinitionSchema: () => EffectFullDefinitionSchema,
39
+ EffectGlowSchema: () => EffectGlowSchema,
40
+ EffectIndexItemSchema: () => EffectIndexItemSchema,
41
+ EffectPanelSchema: () => EffectPanelSchema,
42
+ EffectShadowSchema: () => EffectShadowSchema,
43
+ EffectStackSchema: () => EffectStackSchema,
44
+ EffectStrokeSchema: () => EffectStrokeSchema,
35
45
  FONT_WEIGHT_OPTIONS: () => FONT_WEIGHT_OPTIONS,
36
46
  FontLoader: () => FontLoader,
47
+ FontSchema: () => FontSchema,
48
+ GradientStopSchema: () => GradientStopSchema,
37
49
  InkBrushEngine: () => InkBrushEngine,
38
50
  LEGACY_RENDERER_MAP: () => LEGACY_RENDERER_MAP,
39
51
  LOOP_PRESETS: () => LOOP_PRESETS,
@@ -43,6 +55,7 @@ __export(index_exports, {
43
55
  SUPPORTED_FONT_FAMILIES: () => SUPPORTED_FONT_FAMILIES,
44
56
  TEMPLATE_CATEGORIES: () => TEMPLATE_CATEGORIES,
45
57
  TextEffectBuilder: () => TextEffectBuilder,
58
+ TextEffectDefinitionSchema: () => TextEffectDefinitionSchema,
46
59
  TextEffectRenderer: () => TextEffectRenderer,
47
60
  WEBM_EXPORT_MAX_FRAMES: () => WEBM_EXPORT_MAX_FRAMES,
48
61
  WebGLCompositor: () => WebGLCompositor,
@@ -108,6 +121,7 @@ __export(index_exports, {
108
121
  evaluateConfig: () => evaluateConfig,
109
122
  evaluateScene: () => evaluateScene,
110
123
  findTrackIndex: () => findTrackIndex,
124
+ formatValidationErrors: () => formatValidationErrors,
111
125
  getAnimPreset: () => getAnimPreset,
112
126
  getAnimatableParamDef: () => getAnimatableParamDef,
113
127
  getAnimatableParamsForLayer: () => getAnimatableParamsForLayer,
@@ -194,11 +208,183 @@ __export(index_exports, {
194
208
  updateTrack: () => updateTrack,
195
209
  updateTrackMatte: () => updateTrackMatte,
196
210
  upsertKeyframe: () => upsertKeyframe,
211
+ validateEffectDefinition: () => validateEffectDefinition,
212
+ validateEffectDefinitionStrict: () => validateEffectDefinitionStrict,
213
+ validateTextEffectDefinition: () => validateTextEffectDefinition,
214
+ validateTextEffectDefinitionStrict: () => validateTextEffectDefinitionStrict,
197
215
  waitForFontsReady: () => waitForFontsReady,
198
216
  wrapTextToWidth: () => wrapTextToWidth
199
217
  });
200
218
  module.exports = __toCommonJS(index_exports);
201
219
 
220
+ // src/validation.ts
221
+ var import_zod = require("zod");
222
+ var GradientStopSchema = import_zod.z.object({
223
+ color: import_zod.z.string(),
224
+ offset: import_zod.z.number().min(0).max(100)
225
+ });
226
+ var EffectFillSchema = import_zod.z.object({
227
+ type: import_zod.z.enum(["solid", "linear", "radial", "pattern", "none"]),
228
+ color: import_zod.z.string().optional(),
229
+ gradient: import_zod.z.object({
230
+ angle: import_zod.z.number(),
231
+ stops: import_zod.z.array(GradientStopSchema)
232
+ }).optional(),
233
+ patternType: import_zod.z.string().optional(),
234
+ perCharFillEnabled: import_zod.z.boolean().optional(),
235
+ charFillColors: import_zod.z.array(import_zod.z.string()).optional()
236
+ });
237
+ var EffectStrokeSchema = import_zod.z.object({
238
+ color: import_zod.z.string(),
239
+ width: import_zod.z.number().min(0),
240
+ position: import_zod.z.enum(["outside", "center", "inside"]).optional(),
241
+ opacity: import_zod.z.number().min(0).max(100).optional(),
242
+ lineJoin: import_zod.z.enum(["round", "miter", "bevel"]).optional(),
243
+ blur: import_zod.z.number().min(0).optional(),
244
+ type: import_zod.z.enum(["solid", "gradient"]).optional(),
245
+ colorSecondary: import_zod.z.string().optional(),
246
+ widthSecondary: import_zod.z.number().min(0).optional(),
247
+ fadeRange: import_zod.z.tuple([import_zod.z.number(), import_zod.z.number()]).optional()
248
+ });
249
+ var EffectShadowSchema = import_zod.z.object({
250
+ type: import_zod.z.enum(["drop", "inner"]).optional(),
251
+ color: import_zod.z.string(),
252
+ blur: import_zod.z.number().min(0),
253
+ offset: import_zod.z.object({
254
+ x: import_zod.z.number(),
255
+ y: import_zod.z.number()
256
+ }).optional(),
257
+ offsetX: import_zod.z.number().optional(),
258
+ // Legacy flat format
259
+ offsetY: import_zod.z.number().optional(),
260
+ // Legacy flat format
261
+ opacity: import_zod.z.number().min(0).max(100).optional()
262
+ }).refine((data) => data.offset !== void 0 || data.offsetX !== void 0 && data.offsetY !== void 0, { message: "Shadow must have either nested 'offset' or flat 'offsetX/offsetY'" });
263
+ var EffectBevelSchema = import_zod.z.object({
264
+ depth: import_zod.z.number().min(0),
265
+ highlight: import_zod.z.string().optional(),
266
+ // Current Studio output
267
+ highlightColor: import_zod.z.string().optional(),
268
+ // Legacy format
269
+ shadow: import_zod.z.string().optional(),
270
+ // Current Studio output
271
+ shadowColor: import_zod.z.string().optional(),
272
+ // Legacy format
273
+ direction: import_zod.z.enum(["bottom-right", "bottom", "right"]).optional(),
274
+ coreColor: import_zod.z.string().optional(),
275
+ edgeColor: import_zod.z.string().optional(),
276
+ edgeWidth: import_zod.z.number().min(0).optional(),
277
+ blur: import_zod.z.number().min(0).optional(),
278
+ blurColor: import_zod.z.string().optional(),
279
+ perspectiveEnabled: import_zod.z.boolean().optional(),
280
+ vanishingPointX: import_zod.z.number().optional(),
281
+ vanishingPointY: import_zod.z.number().optional(),
282
+ focalLength: import_zod.z.number().optional()
283
+ }).refine((data) => data.highlight !== void 0 || data.highlightColor !== void 0, { message: "Bevel must have either 'highlight' or 'highlightColor'" }).refine((data) => data.shadow !== void 0 || data.shadowColor !== void 0, { message: "Bevel must have either 'shadow' or 'shadowColor'" });
284
+ var EffectGlowSchema = import_zod.z.object({
285
+ color: import_zod.z.string(),
286
+ blur: import_zod.z.number().min(0),
287
+ opacity: import_zod.z.number().min(0).max(100),
288
+ type: import_zod.z.enum(["outer", "inner"]).optional(),
289
+ strength: import_zod.z.number().min(0).optional(),
290
+ spread: import_zod.z.number().min(0).optional()
291
+ });
292
+ var EffectPanelSchema = import_zod.z.object({
293
+ color: import_zod.z.string(),
294
+ opacity: import_zod.z.number().min(0).max(100),
295
+ radius: import_zod.z.number().min(0),
296
+ padding: import_zod.z.object({
297
+ x: import_zod.z.number().min(0),
298
+ y: import_zod.z.number().min(0)
299
+ }).optional(),
300
+ paddingX: import_zod.z.number().min(0).optional(),
301
+ // Legacy flat format
302
+ paddingY: import_zod.z.number().min(0).optional(),
303
+ // Legacy flat format
304
+ stroke: import_zod.z.object({
305
+ color: import_zod.z.string(),
306
+ width: import_zod.z.number().min(0)
307
+ }).nullable().optional()
308
+ }).refine((data) => data.padding !== void 0 || data.paddingX !== void 0 && data.paddingY !== void 0, { message: "Panel must have either nested 'padding' or flat 'paddingX/paddingY'" });
309
+ var EffectStackSchema = import_zod.z.object({
310
+ count: import_zod.z.number().int().min(1).max(100),
311
+ offsetX: import_zod.z.number(),
312
+ offsetY: import_zod.z.number(),
313
+ opacityDecay: import_zod.z.number().min(0).max(1),
314
+ color1: import_zod.z.string().optional(),
315
+ color2: import_zod.z.string().optional(),
316
+ color3: import_zod.z.string().optional(),
317
+ color4: import_zod.z.string().optional()
318
+ });
319
+ var FontSchema = import_zod.z.object({
320
+ family: import_zod.z.string().min(1),
321
+ weight: import_zod.z.number().int().min(100).max(900),
322
+ style: import_zod.z.enum(["normal", "italic"]),
323
+ letterSpacing: import_zod.z.number(),
324
+ lineHeight: import_zod.z.number().positive()
325
+ });
326
+ var AnimationSchema = import_zod.z.object({
327
+ type: import_zod.z.enum(["none", "typewriter", "wave", "fade", "glitch"]),
328
+ speed: import_zod.z.number().positive().optional(),
329
+ amplitude: import_zod.z.number().optional(),
330
+ frequency: import_zod.z.number().positive().optional()
331
+ });
332
+ var EffectIndexItemSchema = import_zod.z.object({
333
+ id: import_zod.z.string().min(1),
334
+ name: import_zod.z.string().min(1),
335
+ category: import_zod.z.string().min(1),
336
+ description: import_zod.z.string().optional(),
337
+ tags: import_zod.z.array(import_zod.z.string()).optional(),
338
+ isPremium: import_zod.z.boolean().optional(),
339
+ previewType: import_zod.z.enum(["static", "video", "lottie"]).optional(),
340
+ thumbnailUrl: import_zod.z.string().url().optional(),
341
+ thumbnail: import_zod.z.string().optional(),
342
+ previewUrl: import_zod.z.string().url().optional(),
343
+ durationMs: import_zod.z.number().positive().optional()
344
+ });
345
+ var EffectFullDefinitionSchema = EffectIndexItemSchema.extend({
346
+ version: import_zod.z.string().optional(),
347
+ description: import_zod.z.string(),
348
+ tags: import_zod.z.array(import_zod.z.string()),
349
+ font: FontSchema,
350
+ fills: import_zod.z.array(EffectFillSchema),
351
+ strokes: import_zod.z.array(EffectStrokeSchema),
352
+ shadows: import_zod.z.array(EffectShadowSchema),
353
+ bevel: EffectBevelSchema.optional(),
354
+ glow: EffectGlowSchema.optional(),
355
+ // Legacy single glow
356
+ glows: import_zod.z.array(EffectGlowSchema).optional(),
357
+ // Current multi-layer glows
358
+ panel: EffectPanelSchema.optional(),
359
+ glitch: import_zod.z.any().optional(),
360
+ // TODO: Define proper schema when glitch effects are implemented
361
+ animation: AnimationSchema.optional(),
362
+ background: import_zod.z.any().optional(),
363
+ // DEPRECATED: kept for backward compatibility
364
+ stack: EffectStackSchema.optional()
365
+ });
366
+ var TextEffectDefinitionSchema = EffectFullDefinitionSchema.extend({
367
+ text: import_zod.z.string().optional()
368
+ });
369
+ function validateEffectDefinition(data) {
370
+ return EffectFullDefinitionSchema.safeParse(data);
371
+ }
372
+ function validateEffectDefinitionStrict(data) {
373
+ return EffectFullDefinitionSchema.parse(data);
374
+ }
375
+ function validateTextEffectDefinition(data) {
376
+ return TextEffectDefinitionSchema.safeParse(data);
377
+ }
378
+ function validateTextEffectDefinitionStrict(data) {
379
+ return TextEffectDefinitionSchema.parse(data);
380
+ }
381
+ function formatValidationErrors(error) {
382
+ return error.issues.map((err) => {
383
+ const path = err.path.join(".");
384
+ return `${path ? `${path}: ` : ""}${err.message}`;
385
+ });
386
+ }
387
+
202
388
  // src/engine/schema.ts
203
389
  var SCENE_VERSION = 1;
204
390
  var DEFAULT_CANVAS_WIDTH = 800;
@@ -3230,16 +3416,20 @@ function _buildConfig(effect, text, fontSize, canvasWidth, canvasHeight, time, c
3230
3416
  if (shadow) {
3231
3417
  if (shadow.color !== void 0) config.shadowColor = shadow.color;
3232
3418
  if (shadow.blur !== void 0) config.shadowBlur = shadow.blur * ratio;
3233
- if (shadow.offsetX !== void 0) config.shadowOffsetX = shadow.offsetX * ratio;
3234
- if (shadow.offsetY !== void 0) config.shadowOffsetY = shadow.offsetY * ratio;
3419
+ if (shadow.offset?.x !== void 0) config.shadowOffsetX = shadow.offset.x * ratio;
3420
+ else if (shadow.offsetX !== void 0) config.shadowOffsetX = shadow.offsetX * ratio;
3421
+ if (shadow.offset?.y !== void 0) config.shadowOffsetY = shadow.offset.y * ratio;
3422
+ else if (shadow.offsetY !== void 0) config.shadowOffsetY = shadow.offsetY * ratio;
3235
3423
  if (shadow.opacity !== void 0) config.shadowOpacity = shadow.opacity;
3236
3424
  if (shadow.type !== void 0) config.shadowType = shadow.type;
3237
3425
  }
3238
3426
  config.bevelEnabled = !!bevel;
3239
3427
  if (bevel) {
3240
3428
  if (bevel.depth !== void 0) config.bevelDepth = Math.round(bevel.depth * ratio);
3241
- if (bevel.highlightColor !== void 0) config.bevelHighlight = bevel.highlightColor;
3242
- if (bevel.shadowColor !== void 0) config.bevelShadow = bevel.shadowColor;
3429
+ if (bevel.highlight !== void 0) config.bevelHighlight = bevel.highlight;
3430
+ else if (bevel.highlightColor !== void 0) config.bevelHighlight = bevel.highlightColor;
3431
+ if (bevel.shadow !== void 0) config.bevelShadow = bevel.shadow;
3432
+ else if (bevel.shadowColor !== void 0) config.bevelShadow = bevel.shadowColor;
3243
3433
  if (bevel.direction !== void 0) config.bevelDirection = bevel.direction;
3244
3434
  if (bevel.coreColor !== void 0) config.bevelCoreColor = bevel.coreColor;
3245
3435
  if (bevel.edgeColor !== void 0) config.bevelEdgeColor = bevel.edgeColor;
@@ -3267,8 +3457,10 @@ function _buildConfig(effect, text, fontSize, canvasWidth, canvasHeight, time, c
3267
3457
  if (panel.color !== void 0) config.panelColor = panel.color;
3268
3458
  if (panel.opacity !== void 0) config.panelOpacity = panel.opacity;
3269
3459
  if (panel.radius !== void 0) config.panelRadius = panel.radius;
3270
- if (panel.paddingX !== void 0) config.panelPaddingX = panel.paddingX * ratio;
3271
- if (panel.paddingY !== void 0) config.panelPaddingY = panel.paddingY * ratio;
3460
+ if (panel.padding?.x !== void 0) config.panelPaddingX = panel.padding.x * ratio;
3461
+ else if (panel.paddingX !== void 0) config.panelPaddingX = panel.paddingX * ratio;
3462
+ if (panel.padding?.y !== void 0) config.panelPaddingY = panel.padding.y * ratio;
3463
+ else if (panel.paddingY !== void 0) config.panelPaddingY = panel.paddingY * ratio;
3272
3464
  if (panel.stroke !== void 0) {
3273
3465
  config.panelStrokeEnabled = !!panel.stroke;
3274
3466
  if (panel.stroke.color !== void 0) config.panelStrokeColor = panel.stroke.color;
@@ -3289,23 +3481,7 @@ function _buildConfig(effect, text, fontSize, canvasWidth, canvasHeight, time, c
3289
3481
  return mappedGlow;
3290
3482
  });
3291
3483
  }
3292
- const standardKeys = /* @__PURE__ */ new Set([
3293
- "id",
3294
- "name",
3295
- "category",
3296
- "description",
3297
- "tags",
3298
- "font",
3299
- "fills",
3300
- "strokes",
3301
- "shadows",
3302
- "glows",
3303
- "bevel",
3304
- "panel",
3305
- "text",
3306
- "animation",
3307
- "stack"
3308
- ]);
3484
+ const standardKeys = /* @__PURE__ */ new Set(["id", "name", "category", "description", "tags", "font", "fills", "strokes", "shadows", "glows", "bevel", "panel", "text", "animation", "stack"]);
3309
3485
  for (const key of Object.keys(effect)) {
3310
3486
  if (!standardKeys.has(key)) {
3311
3487
  config[key] = effect[key];
@@ -4358,8 +4534,8 @@ function applyToLayerTransform(layer2, override) {
4358
4534
  ks.s.k = [override.scaleX, override.scaleY, 100];
4359
4535
  }
4360
4536
  if (override.posX !== void 0 && override.posY !== void 0 && ks.p?.a === 0) {
4361
- const z = Array.isArray(ks.p.k) ? ks.p.k[2] ?? 0 : 0;
4362
- ks.p.k = [override.posX, override.posY, z];
4537
+ const z2 = Array.isArray(ks.p.k) ? ks.p.k[2] ?? 0 : 0;
4538
+ ks.p.k = [override.posX, override.posY, z2];
4363
4539
  }
4364
4540
  }
4365
4541
  function mutateTextLayer(layer2, override) {
@@ -7460,6 +7636,7 @@ var TextEffectBuilder = class _TextEffectBuilder {
7460
7636
  };
7461
7637
  // Annotate the CommonJS export names for ESM import in node:
7462
7638
  0 && (module.exports = {
7639
+ AnimationSchema,
7463
7640
  COMPOSITION_PRESETS,
7464
7641
  CUSTOM_ENGINE_IDS,
7465
7642
  CanvasDevice,
@@ -7473,8 +7650,19 @@ var TextEffectBuilder = class _TextEffectBuilder {
7473
7650
  ENGINE_ID_TO_LEGACY,
7474
7651
  ENTRANCE_PRESETS,
7475
7652
  EXIT_PRESETS,
7653
+ EffectBevelSchema,
7654
+ EffectFillSchema,
7655
+ EffectFullDefinitionSchema,
7656
+ EffectGlowSchema,
7657
+ EffectIndexItemSchema,
7658
+ EffectPanelSchema,
7659
+ EffectShadowSchema,
7660
+ EffectStackSchema,
7661
+ EffectStrokeSchema,
7476
7662
  FONT_WEIGHT_OPTIONS,
7477
7663
  FontLoader,
7664
+ FontSchema,
7665
+ GradientStopSchema,
7478
7666
  InkBrushEngine,
7479
7667
  LEGACY_RENDERER_MAP,
7480
7668
  LOOP_PRESETS,
@@ -7484,6 +7672,7 @@ var TextEffectBuilder = class _TextEffectBuilder {
7484
7672
  SUPPORTED_FONT_FAMILIES,
7485
7673
  TEMPLATE_CATEGORIES,
7486
7674
  TextEffectBuilder,
7675
+ TextEffectDefinitionSchema,
7487
7676
  TextEffectRenderer,
7488
7677
  WEBM_EXPORT_MAX_FRAMES,
7489
7678
  WebGLCompositor,
@@ -7549,6 +7738,7 @@ var TextEffectBuilder = class _TextEffectBuilder {
7549
7738
  evaluateConfig,
7550
7739
  evaluateScene,
7551
7740
  findTrackIndex,
7741
+ formatValidationErrors,
7552
7742
  getAnimPreset,
7553
7743
  getAnimatableParamDef,
7554
7744
  getAnimatableParamsForLayer,
@@ -7635,6 +7825,10 @@ var TextEffectBuilder = class _TextEffectBuilder {
7635
7825
  updateTrack,
7636
7826
  updateTrackMatte,
7637
7827
  upsertKeyframe,
7828
+ validateEffectDefinition,
7829
+ validateEffectDefinitionStrict,
7830
+ validateTextEffectDefinition,
7831
+ validateTextEffectDefinitionStrict,
7638
7832
  waitForFontsReady,
7639
7833
  wrapTextToWidth
7640
7834
  });