@shotstack/shotstack-studio 1.7.0 → 1.8.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.
@@ -0,0 +1,301 @@
1
+ import * as e from "zod";
2
+ const y = e.enum(["linear", "bezier", "constant"]), A = e.enum([
3
+ "ease",
4
+ "easeIn",
5
+ "easeOut",
6
+ "easeInOut",
7
+ "easeInQuad",
8
+ "easeInCubic",
9
+ "easeInQuart",
10
+ "easeInQuint",
11
+ "easeInSine",
12
+ "easeInExpo",
13
+ "easeInCirc",
14
+ "easeInBack",
15
+ "easeOutQuad",
16
+ "easeOutCubic",
17
+ "easeOutQuart",
18
+ "easeOutQuint",
19
+ "easeOutSine",
20
+ "easeOutExpo",
21
+ "easeOutCirc",
22
+ "easeOutBack",
23
+ "easeInOutQuad",
24
+ "easeInOutCubic",
25
+ "easeInOutQuart",
26
+ "easeInOutQuint",
27
+ "easeInOutSine",
28
+ "easeInOutExpo",
29
+ "easeInOutCirc",
30
+ "easeInOutBack"
31
+ ]), o = e.object({
32
+ from: e.number(),
33
+ to: e.number(),
34
+ start: e.number().min(0),
35
+ length: e.number().positive(),
36
+ interpolation: y.optional(),
37
+ easing: A.optional()
38
+ }), j = e.string().url("Invalid audio url format."), v = o.extend({
39
+ from: e.number().min(0).max(1),
40
+ to: e.number().min(0).max(1)
41
+ }).array().or(e.number().min(0).max(1)), r = e.object({
42
+ type: e.literal("audio"),
43
+ src: j,
44
+ trim: e.number().optional(),
45
+ volume: v.optional()
46
+ }), I = e.enum(["top", "topRight", "right", "bottomRight", "bottom", "bottomLeft", "left", "topLeft", "center"]), m = e.object({
47
+ type: e.literal("html"),
48
+ html: e.string(),
49
+ css: e.string(),
50
+ width: e.number().positive().optional(),
51
+ height: e.number().positive().optional(),
52
+ position: I.optional()
53
+ }), C = e.string().url("Invalid image url format."), O = e.object({
54
+ top: e.number().min(0).optional(),
55
+ right: e.number().min(0).optional(),
56
+ bottom: e.number().min(0).optional(),
57
+ left: e.number().min(0).optional()
58
+ }), s = e.object({
59
+ type: e.literal("image"),
60
+ src: C,
61
+ crop: O.optional()
62
+ }), T = e.string().url("Invalid luma url format."), l = e.object({
63
+ type: e.literal("luma"),
64
+ src: T
65
+ }), a = e.string().regex(/^#[A-Fa-f0-9]{6}$/, "Invalid hex color format"), w = e.object({
66
+ offset: e.number().min(0).max(1),
67
+ color: a
68
+ }).strict(), R = e.object({
69
+ type: e.enum(["linear", "radial"]).default("linear"),
70
+ angle: e.number().min(0).max(360).default(0),
71
+ stops: e.array(w).min(2)
72
+ }).strict(), k = e.object({
73
+ family: e.string().default("Roboto"),
74
+ size: e.number().min(8).max(500).default(48),
75
+ weight: e.union([e.string(), e.number()]).default("400"),
76
+ style: e.enum(["normal", "italic", "oblique"]).default("normal"),
77
+ color: a.default("#000000"),
78
+ opacity: e.number().min(0).max(1).default(1)
79
+ }).strict(), F = e.object({
80
+ letterSpacing: e.number().default(0),
81
+ lineHeight: e.number().min(0.1).max(10).default(1.2),
82
+ textTransform: e.enum(["none", "uppercase", "lowercase", "capitalize"]).default("none"),
83
+ textDecoration: e.enum(["none", "underline", "line-through"]).default("none"),
84
+ gradient: R.optional()
85
+ }).strict(), P = e.object({
86
+ width: e.number().min(0).default(0),
87
+ color: a.default("#000000"),
88
+ opacity: e.number().min(0).max(1).default(1)
89
+ }).strict(), L = e.object({
90
+ offsetX: e.number().default(0),
91
+ offsetY: e.number().default(0),
92
+ blur: e.number().min(0).default(0),
93
+ color: a.default("#000000"),
94
+ opacity: e.number().min(0).max(1).default(0.5)
95
+ }).strict(), Q = e.object({
96
+ color: a.optional(),
97
+ opacity: e.number().min(0).max(1).default(1),
98
+ borderRadius: e.number().min(0).default(0)
99
+ }).strict(), z = e.object({
100
+ horizontal: e.enum(["left", "center", "right"]).default("left"),
101
+ vertical: e.enum(["top", "middle", "bottom"]).default("middle")
102
+ }).strict(), V = e.object({
103
+ preset: e.enum(["fadeIn", "slideIn", "typewriter", "shift", "ascend", "movingLetters", "bounce", "elastic", "pulse"]),
104
+ speed: e.number().min(0.1).max(10).default(1),
105
+ duration: e.number().min(0.1).max(60).optional(),
106
+ style: e.enum(["character", "word"]).optional(),
107
+ direction: e.enum(["left", "right", "up", "down"]).optional()
108
+ }).strict(), E = e.object({
109
+ src: e.string().url("Invalid font URL"),
110
+ family: e.string(),
111
+ weight: e.union([e.string(), e.number()]).default("400"),
112
+ style: e.enum(["normal", "italic", "oblique"]).default("normal")
113
+ }).strict(), c = e.object({
114
+ type: e.literal("rich-text"),
115
+ text: e.string().max(1e4).default(""),
116
+ width: e.number().min(1).max(8192).optional(),
117
+ height: e.number().min(1).max(8192).optional(),
118
+ font: k.optional(),
119
+ style: F.optional(),
120
+ stroke: P.optional(),
121
+ shadow: L.optional(),
122
+ background: Q.optional(),
123
+ align: z.optional(),
124
+ animation: V.optional(),
125
+ cacheEnabled: e.boolean().default(!0),
126
+ pixelRatio: e.number().min(1).max(4).default(2),
127
+ customFonts: e.array(E).optional()
128
+ }).strict(), g = e.string().regex(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})|transparent$/, "Invalid color format."), u = e.object({
129
+ width: e.number().positive(),
130
+ height: e.number().positive()
131
+ }), p = e.object({
132
+ radius: e.number().positive()
133
+ }), f = e.object({
134
+ length: e.number().positive(),
135
+ thickness: e.number().positive()
136
+ }), U = e.object({
137
+ color: g,
138
+ opacity: e.number().min(0).max(1)
139
+ }), B = e.object({
140
+ color: g,
141
+ width: e.number().positive()
142
+ }), b = e.object({
143
+ type: e.literal("shape"),
144
+ width: e.number().positive().optional(),
145
+ height: e.number().positive().optional(),
146
+ shape: e.enum(["rectangle", "circle", "line"]),
147
+ fill: U.optional(),
148
+ stroke: B.optional(),
149
+ rectangle: u.optional(),
150
+ circle: p.optional(),
151
+ line: f.optional()
152
+ }).refine((t) => t.shape === "rectangle" ? u.safeParse(t.rectangle) : t.shape === "circle" ? p.safeParse(t.circle) : t.shape === "line" ? f.safeParse(t.line) : !1), i = e.string().regex(/^#([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})|transparent$/, "Invalid color format."), H = e.object({
153
+ color: i.optional(),
154
+ family: e.string().optional(),
155
+ size: e.number().positive().optional(),
156
+ weight: e.number().optional(),
157
+ lineHeight: e.number().optional()
158
+ }), K = e.object({
159
+ horizontal: e.enum(["left", "center", "right"]).optional(),
160
+ vertical: e.enum(["top", "center", "bottom"]).optional()
161
+ }), $ = e.object({
162
+ color: i,
163
+ opacity: e.number().min(0).max(1)
164
+ }), q = e.object({
165
+ width: e.number().positive(),
166
+ color: i
167
+ }), h = e.object({
168
+ type: e.literal("text"),
169
+ text: e.string(),
170
+ width: e.number().positive().optional(),
171
+ height: e.number().positive().optional(),
172
+ font: H.optional(),
173
+ alignment: K.optional(),
174
+ background: $.optional(),
175
+ stroke: q.optional()
176
+ }), G = e.string().url("Invalid video url format."), X = e.object({
177
+ top: e.number().min(0).optional(),
178
+ right: e.number().min(0).optional(),
179
+ bottom: e.number().min(0).optional(),
180
+ left: e.number().min(0).optional()
181
+ }), Y = o.extend({
182
+ from: e.number().min(0).max(1),
183
+ to: e.number().min(0).max(1)
184
+ }).array().or(e.number().min(0).max(1)), d = e.object({
185
+ type: e.literal("video"),
186
+ src: G,
187
+ trim: e.number().optional(),
188
+ crop: X.optional(),
189
+ volume: Y.optional()
190
+ }), D = e.union([
191
+ h,
192
+ c,
193
+ b,
194
+ m,
195
+ s,
196
+ d,
197
+ l,
198
+ r
199
+ ]).refine((t) => t.type === "text" ? h.safeParse(t) : t.type === "rich-text" ? c.safeParse(t) : t.type === "shape" ? b.safeParse(t) : t.type === "html" ? m.safeParse(t) : t.type === "image" ? s.safeParse(t) : t.type === "video" ? d.safeParse(t) : t.type === "luma" ? l.safeParse(t) : t.type === "audio" ? r.safeParse(t) : !1), J = e.enum(["topLeft", "top", "topRight", "left", "center", "right", "bottomLeft", "bottom", "bottomRight"]), M = e.enum(["crop", "cover", "contain", "none"]), n = e.number().min(-10).max(10).default(0), N = o.extend({
200
+ from: n,
201
+ to: n
202
+ }).array().or(n), W = o.extend({
203
+ from: n,
204
+ to: n
205
+ }).array().or(n), Z = e.object({
206
+ x: N.default(0),
207
+ y: W.default(0)
208
+ }), _ = o.extend({
209
+ from: e.number().min(0).max(1),
210
+ to: e.number().min(0).max(1)
211
+ }).array().or(e.number().min(0).max(1)), ee = o.extend({
212
+ from: e.number().min(0),
213
+ to: e.number().min(0)
214
+ }).array().or(e.number().min(0)), te = e.object({
215
+ angle: o.extend({
216
+ from: e.number(),
217
+ to: e.number()
218
+ }).array().or(e.number())
219
+ }), oe = e.string(), S = e.string(), ne = e.object({
220
+ in: S.optional(),
221
+ out: S.optional()
222
+ }), ae = e.object({
223
+ rotate: te.default({ angle: 0 })
224
+ }), ie = e.object({
225
+ asset: D,
226
+ start: e.number().min(0),
227
+ length: e.number().positive(),
228
+ position: J.default("center").optional(),
229
+ fit: M.optional(),
230
+ offset: Z.default({ x: 0, y: 0 }).optional(),
231
+ opacity: _.default(1).optional(),
232
+ scale: ee.default(1).optional(),
233
+ transform: ae.default({ rotate: { angle: 0 } }).optional(),
234
+ effect: oe.optional(),
235
+ transition: ne.optional(),
236
+ width: e.number().min(1).max(3840).optional(),
237
+ height: e.number().min(1).max(2160).optional()
238
+ }).transform((t) => {
239
+ if (t.fit !== void 0)
240
+ return t;
241
+ const x = t.asset.type === "rich-text" ? "none" : "crop";
242
+ return { ...t, fit: x };
243
+ }), re = e.object({
244
+ clips: ie.array()
245
+ }), me = e.string().url("Invalid image url format."), se = e.object({
246
+ src: me
247
+ }), le = e.object({
248
+ background: e.string().optional(),
249
+ fonts: se.array().optional(),
250
+ tracks: re.array()
251
+ }), ce = e.object({
252
+ size: e.object({
253
+ width: e.number().positive(),
254
+ height: e.number().positive()
255
+ }),
256
+ fps: e.number().positive().optional(),
257
+ format: e.string()
258
+ }), ue = e.object({
259
+ timeline: le,
260
+ output: ce
261
+ });
262
+ export {
263
+ D as AssetSchema,
264
+ r as AudioAssetSchema,
265
+ j as AudioAssetUrlSchema,
266
+ v as AudioAssetVolumeSchema,
267
+ ie as ClipSchema,
268
+ ue as EditSchema,
269
+ se as FontSourceSchema,
270
+ me as FontSourceUrlSchema,
271
+ m as HtmlAssetSchema,
272
+ O as ImageAssetCropSchema,
273
+ s as ImageAssetSchema,
274
+ C as ImageAssetUrlSchema,
275
+ A as KeyframeEasingSchema,
276
+ y as KeyframeInterpolationSchema,
277
+ o as KeyframeSchema,
278
+ l as LumaAssetSchema,
279
+ T as LumaAssetUrlSchema,
280
+ ce as OutputSchema,
281
+ c as RichTextAssetSchema,
282
+ p as ShapeAssetCircleSchema,
283
+ g as ShapeAssetColorSchema,
284
+ U as ShapeAssetFillSchema,
285
+ f as ShapeAssetLineSchema,
286
+ u as ShapeAssetRectangleSchema,
287
+ b as ShapeAssetSchema,
288
+ B as ShapeAssetStrokeSchema,
289
+ K as TextAssetAlignmentSchema,
290
+ $ as TextAssetBackgroundSchema,
291
+ i as TextAssetColorSchema,
292
+ H as TextAssetFontSchema,
293
+ h as TextAssetSchema,
294
+ q as TextAssetStrokeSchema,
295
+ le as TimelineSchema,
296
+ re as TrackSchema,
297
+ X as VideoAssetCropSchema,
298
+ d as VideoAssetSchema,
299
+ G as VideoAssetUrlSchema,
300
+ Y as VideoAssetVolumeSchema
301
+ };
package/package.json CHANGED
@@ -5,15 +5,30 @@
5
5
  "cpuccino",
6
6
  "dazzatron"
7
7
  ],
8
- "version": "1.7.0",
8
+ "version": "1.8.0",
9
9
  "description": "A video editing library for creating and editing videos with Shotstack",
10
10
  "type": "module",
11
11
  "main": "dist/shotstack-studio.umd.js",
12
12
  "module": "dist/shotstack-studio.es.js",
13
13
  "types": "dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/shotstack-studio.es.js",
18
+ "require": "./dist/shotstack-studio.umd.js",
19
+ "default": "./dist/shotstack-studio.es.js"
20
+ },
21
+ "./schema": {
22
+ "types": "./dist/schema/index.d.ts",
23
+ "import": "./dist/schema/index.mjs",
24
+ "require": "./dist/schema/index.cjs",
25
+ "default": "./dist/schema/index.mjs"
26
+ }
27
+ },
14
28
  "files": [
15
29
  "dist/shotstack-studio.umd.js",
16
30
  "dist/shotstack-studio.es.js",
31
+ "dist/schema/**",
17
32
  "dist/**/*.d.ts"
18
33
  ],
19
34
  "keywords": [
@@ -28,11 +43,22 @@
28
43
  "url": "https://github.com/shotstack/shotstack-studio-sdk"
29
44
  },
30
45
  "scripts": {
31
- "build": "vite build",
32
- "prepublishOnly": "npm run build"
46
+ "dev": "vite",
47
+ "start": "npm run build && vite preview",
48
+ "build": "npm run build:main && npm run build:schema",
49
+ "build:main": "vite build",
50
+ "build:schema": "vite build --config vite.config.schema.ts",
51
+ "test": "npm run build && jest",
52
+ "test:watch": "jest --watch",
53
+ "lint": "eslint --ignore-path .gitignore .",
54
+ "lint:fix": "eslint --ignore-path .gitignore --fix .",
55
+ "format": "prettier --ignore-path .gitignore --write .",
56
+ "prepublishOnly": "npm run build && npm run test"
33
57
  },
34
58
  "devDependencies": {
59
+ "@jest/globals": "^30.2.0",
35
60
  "@types/howler": "^2.2.12",
61
+ "@types/jest": "^30.0.0",
36
62
  "@types/node": "^22.9.0",
37
63
  "@types/opentype.js": "^1.3.8",
38
64
  "@typescript-eslint/eslint-plugin": "^7.18.0",
@@ -42,12 +68,14 @@
42
68
  "eslint-config-airbnb-typescript": "^18.0.0",
43
69
  "eslint-config-prettier": "^9.1.0",
44
70
  "eslint-plugin-import": "^2.31.0",
71
+ "jest": "^30.2.0",
72
+ "ts-jest": "^29.4.5",
45
73
  "typescript": "^5.6.2",
46
74
  "vite": "^5.4.10",
47
75
  "vite-plugin-dts": "^4.5.4"
48
76
  },
49
77
  "dependencies": {
50
- "@shotstack/shotstack-canvas": "^1.1.2",
78
+ "@shotstack/shotstack-canvas": "^1.1.7",
51
79
  "fast-deep-equal": "^3.1.3",
52
80
  "howler": "^2.2.4",
53
81
  "mediabunny": "^1.11.2",
package/readme.md CHANGED
@@ -78,6 +78,10 @@ Your HTML should include containers for both the canvas and timeline:
78
78
  The Edit class represents a video project with its timeline, clips, and properties.
79
79
 
80
80
  ```typescript
81
+ import { Edit } from "@shotstack/shotstack-studio";
82
+
83
+ // For schema validation only (e.g., in tests):
84
+ import { EditSchema, ClipSchema } from "@shotstack/shotstack-studio/schema";
81
85
  // Create an edit with dimensions and background
82
86
  const edit = new Edit({ width: 1280, height: 720 }, "#000000");
83
87
  await edit.load();
@@ -293,6 +297,7 @@ const timeline = new Timeline(edit, { width: 1280, height: 300 }, { theme: custo
293
297
  Themes are organized by component, making it intuitive to customize specific parts of the interface:
294
298
 
295
299
  - **Timeline**: Controls the appearance of the timeline interface
300
+
296
301
  - `toolbar`: Playback controls and buttons
297
302
  - `ruler`: Time markers and labels
298
303
  - `tracks`: Track backgrounds and borders