@pixerate/editor 0.238.0 → 0.240.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.
@@ -1,756 +1,42 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
2
 
3
- var _chunkFJ2MSI43cjs = require('../chunk-FJ2MSI43.cjs');
4
3
 
5
4
 
6
5
 
7
6
 
8
7
 
9
8
 
10
- var _chunk7PT4RJL5cjs = require('../chunk-7PT4RJL5.cjs');
11
9
 
12
10
 
13
11
 
14
- var _chunkPPFJHTW5cjs = require('../chunk-PPFJHTW5.cjs');
15
12
 
16
13
 
17
14
 
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+ var _chunkS6OT6CTScjs = require('../chunk-S6OT6CTS.cjs');
29
+
30
+
31
+ var _chunkFJ2MSI43cjs = require('../chunk-FJ2MSI43.cjs');
32
+ require('../chunk-7PT4RJL5.cjs');
33
+ require('../chunk-PPFJHTW5.cjs');
34
+
35
+
18
36
  var _chunkRD7RX52Gcjs = require('../chunk-RD7RX52G.cjs');
19
37
  require('../chunk-7AHBW2CQ.cjs');
20
38
  require('../chunk-IPJ3TEYR.cjs');
21
39
 
22
- // src/gradient/model.ts
23
- function clamp(v, min, max) {
24
- return Math.max(min, Math.min(max, v));
25
- }
26
- function gradientMidpointRemap(u, m) {
27
- const mid = clamp(m, 0.05, 0.95);
28
- if (Math.abs(mid - 0.5) < 1e-6) return u;
29
- return u <= mid ? 0.5 * u / mid : 0.5 + 0.5 * (u - mid) / (1 - mid);
30
- }
31
- function normalizeGradient(input) {
32
- if (typeof input === "string") {
33
- const parsed = parseCssGradient(input);
34
- if (!parsed) {
35
- return { error: `Could not parse CSS gradient: "${input}".` };
36
- }
37
- return { gradient: parsed };
38
- }
39
- if (!input || typeof input !== "object") {
40
- return { error: "Gradient must be an object." };
41
- }
42
- const obj = input;
43
- const type = obj.type === "radial" || obj.type === "conic" ? obj.type : "linear";
44
- if (!Array.isArray(obj.stops)) {
45
- return { error: "Gradient stops must be an array." };
46
- }
47
- if (obj.stops.length < 2) {
48
- return { error: `A gradient needs at least 2 stops; it has ${obj.stops.length}.` };
49
- }
50
- const usedIds = /* @__PURE__ */ new Set();
51
- for (const s of obj.stops) {
52
- if (s && typeof s === "object" && typeof s.id === "string" && s.id.trim()) {
53
- usedIds.add(s.id.trim());
54
- }
55
- }
56
- let nextIdIndex = 1;
57
- const getNextId = () => {
58
- while (usedIds.has(`s${nextIdIndex}`)) {
59
- nextIdIndex++;
60
- }
61
- const id = `s${nextIdIndex}`;
62
- usedIds.add(id);
63
- nextIdIndex++;
64
- return id;
65
- };
66
- const stops = [];
67
- for (let i = 0; i < obj.stops.length; i++) {
68
- const s = obj.stops[i];
69
- if (!s || typeof s !== "object") {
70
- return { error: `Stop at index ${i} must be an object.` };
71
- }
72
- const rawStop = s;
73
- let normColor = null;
74
- if (typeof rawStop.color === "string" || rawStop.color && typeof rawStop.color === "object") {
75
- const dual = _chunk7PT4RJL5cjs.normalizeDualColor.call(void 0, rawStop.color);
76
- if (dual) {
77
- normColor = _chunk7PT4RJL5cjs.formatDualColor.call(void 0, dual);
78
- }
79
- }
80
- if (!normColor) {
81
- return { error: `Stop at index ${i} has an invalid colour.` };
82
- }
83
- let offset = Number(rawStop.offset);
84
- if (!Number.isFinite(offset)) {
85
- offset = obj.stops.length === 1 ? 0 : i / (obj.stops.length - 1);
86
- }
87
- offset = clamp(offset, 0, 1);
88
- const id = typeof rawStop.id === "string" && rawStop.id.trim() ? rawStop.id.trim() : getNextId();
89
- const stop = {
90
- id,
91
- offset,
92
- color: normColor
93
- };
94
- if (rawStop.midpoint !== void 0 && rawStop.midpoint !== null) {
95
- const m = Number(rawStop.midpoint);
96
- if (Number.isFinite(m)) {
97
- stop.midpoint = clamp(m, 0.05, 0.95);
98
- }
99
- }
100
- stops.push(stop);
101
- }
102
- stops.sort((a, b) => a.offset - b.offset);
103
- const gradient = {
104
- type,
105
- stops
106
- };
107
- if (type === "linear" || type === "conic") {
108
- if (obj.angle !== void 0 && Number.isFinite(Number(obj.angle))) {
109
- gradient.angle = (Number(obj.angle) % 360 + 360) % 360;
110
- } else {
111
- gradient.angle = type === "linear" ? 180 : 0;
112
- }
113
- }
114
- if (type === "radial" || type === "conic") {
115
- if (obj.center && typeof obj.center === "object") {
116
- const cx = Number(obj.center.x);
117
- const cy = Number(obj.center.y);
118
- gradient.center = {
119
- x: Number.isFinite(cx) ? clamp(cx, 0, 1) : 0.5,
120
- y: Number.isFinite(cy) ? clamp(cy, 0, 1) : 0.5
121
- };
122
- } else {
123
- gradient.center = { x: 0.5, y: 0.5 };
124
- }
125
- }
126
- if (type === "radial") {
127
- gradient.shape = obj.shape === "circle" ? "circle" : "ellipse";
128
- }
129
- const allowedInterp = ["srgb", "srgb-linear", "oklab", "oklch"];
130
- if (obj.interpolation && allowedInterp.includes(obj.interpolation)) {
131
- gradient.interpolation = obj.interpolation;
132
- } else {
133
- gradient.interpolation = "oklab";
134
- }
135
- if (obj.repeating) {
136
- gradient.repeating = true;
137
- }
138
- return { gradient };
139
- }
140
- function createGradient(colorsOrOptions, options) {
141
- if (Array.isArray(colorsOrOptions)) {
142
- const colors = colorsOrOptions;
143
- const resolvedColors = colors.length >= 2 ? colors : colors.length === 1 ? [colors[0], colors[0]] : ["#000000", "#ffffff"];
144
- const stops = resolvedColors.map((color, i) => ({
145
- id: `s${i + 1}`,
146
- offset: i / (resolvedColors.length - 1),
147
- color: _chunk7PT4RJL5cjs.formatDualColor.call(void 0, _nullishCoalesce(_chunk7PT4RJL5cjs.normalizeDualColor.call(void 0, color), () => ( { light: "#000000", dark: "#000000" })))
148
- }));
149
- const res2 = normalizeGradient({
150
- ...options,
151
- stops
152
- });
153
- if ("error" in res2) {
154
- throw new Error(res2.error);
155
- }
156
- return res2.gradient;
157
- }
158
- if (colorsOrOptions && typeof colorsOrOptions === "object" && !Array.isArray(colorsOrOptions)) {
159
- const raw = colorsOrOptions;
160
- let stops = raw.stops;
161
- if (!stops || stops.length === 0) {
162
- stops = [
163
- { id: "s1", offset: 0, color: "#000000" },
164
- { id: "s2", offset: 1, color: "#ffffff" }
165
- ];
166
- } else if (typeof stops[0] === "string") {
167
- const strStops = stops;
168
- stops = strStops.map((color, i) => ({
169
- id: `s${i + 1}`,
170
- offset: i / (strStops.length - 1),
171
- color
172
- }));
173
- }
174
- const res2 = normalizeGradient({
175
- ...raw,
176
- stops
177
- });
178
- if ("error" in res2) {
179
- throw new Error(res2.error);
180
- }
181
- return res2.gradient;
182
- }
183
- const res = normalizeGradient({
184
- ...options,
185
- stops: [
186
- { id: "s1", offset: 0, color: "#000000" },
187
- { id: "s2", offset: 1, color: "#ffffff" }
188
- ]
189
- });
190
- if ("error" in res) {
191
- throw new Error(res.error);
192
- }
193
- return res.gradient;
194
- }
195
- function isDualColorValue(val) {
196
- if (typeof val === "string") {
197
- return val.trim().toLowerCase().startsWith("light-dark(");
198
- }
199
- if (_chunk7PT4RJL5cjs.isDualColor.call(void 0, val)) {
200
- return val.light !== val.dark;
201
- }
202
- return false;
203
- }
204
- function isDualGradient(g) {
205
- return g.stops.some((s) => isDualColorValue(s.color));
206
- }
207
- function resolveGradient(g, scheme) {
208
- return {
209
- ...g,
210
- center: g.center ? { ...g.center } : void 0,
211
- stops: g.stops.map((s) => ({
212
- ...s,
213
- color: _nullishCoalesce(_chunk7PT4RJL5cjs.resolveColor.call(void 0, s.color, scheme), () => ( s.color))
214
- }))
215
- };
216
- }
217
- function sampleGradient(g, t, scheme = "light") {
218
- const resolved = resolveGradient(g, scheme);
219
- const clampedT = clamp(t, 0, 1);
220
- const stops = resolved.stops;
221
- if (clampedT <= stops[0].offset) {
222
- return stops[0].color;
223
- }
224
- if (clampedT >= stops[stops.length - 1].offset) {
225
- return stops[stops.length - 1].color;
226
- }
227
- let i = 0;
228
- while (i < stops.length - 1 && stops[i + 1].offset < clampedT) {
229
- i++;
230
- }
231
- const s0 = stops[i];
232
- const s1 = stops[i + 1];
233
- const span = s1.offset - s0.offset;
234
- if (span <= 1e-6) {
235
- return s1.color;
236
- }
237
- let u = (clampedT - s0.offset) / span;
238
- if (s0.midpoint !== void 0) {
239
- u = gradientMidpointRemap(u, s0.midpoint);
240
- }
241
- const mixed = _chunk7PT4RJL5cjs.mixColors.call(void 0, s0.color, s1.color, u, { space: _nullishCoalesce(g.interpolation, () => ( "oklab")) });
242
- return _nullishCoalesce(mixed, () => ( s0.color));
243
- }
244
- function insertGradientStop(g, offset, color) {
245
- const clampedOffset = clamp(offset, 0, 1);
246
- const stopColor = color !== void 0 ? color : sampleGradient(g, clampedOffset, "light");
247
- const used = new Set(g.stops.map((s) => s.id));
248
- let idx = 1;
249
- while (used.has(`s${idx}`)) idx++;
250
- const id = `s${idx}`;
251
- const newStop = {
252
- id,
253
- offset: clampedOffset,
254
- color: stopColor
255
- };
256
- const stops = [...g.stops, newStop];
257
- const res = normalizeGradient({ ...g, stops });
258
- if ("error" in res) throw new Error(res.error);
259
- const added = res.gradient.stops.find((s) => s.id === id);
260
- return { gradient: res.gradient, stop: added };
261
- }
262
- function listValidIds(g) {
263
- return g.stops.map((s) => `"${s.id}"`).join(", ");
264
- }
265
- function updateGradientStop(g, id, patch) {
266
- const index = g.stops.findIndex((s) => s.id === id);
267
- if (index === -1) {
268
- throw new Error(`Stop "${id}" not found. Valid stop IDs are: ${listValidIds(g)}.`);
269
- }
270
- const stops = g.stops.map((s, i) => i === index ? { ...s, ...patch } : s);
271
- const res = normalizeGradient({ ...g, stops });
272
- if ("error" in res) throw new Error(res.error);
273
- return res.gradient;
274
- }
275
- function removeGradientStop(g, id) {
276
- if (g.stops.length <= 2) {
277
- throw new Error("A gradient needs at least 2 stops; add one with add_gradient_stop before removing this one.");
278
- }
279
- const index = g.stops.findIndex((s) => s.id === id);
280
- if (index === -1) {
281
- throw new Error(`Stop "${id}" not found. Valid stop IDs are: ${listValidIds(g)}.`);
282
- }
283
- const stops = g.stops.filter((s) => s.id !== id);
284
- const res = normalizeGradient({ ...g, stops });
285
- if ("error" in res) throw new Error(res.error);
286
- return res.gradient;
287
- }
288
- function reverseGradient(g) {
289
- const reversedStops = g.stops.map((s) => ({
290
- ...s,
291
- offset: Math.round((1 - s.offset) * 1e4) / 1e4,
292
- midpoint: s.midpoint !== void 0 ? Math.round((1 - s.midpoint) * 1e4) / 1e4 : void 0
293
- }));
294
- reversedStops.sort((a, b) => a.offset - b.offset);
295
- return {
296
- ...g,
297
- center: g.center ? { ...g.center } : void 0,
298
- stops: reversedStops
299
- };
300
- }
301
- function distributeGradientStops(g) {
302
- const n = g.stops.length;
303
- const distributed = g.stops.map((s, i) => ({
304
- ...s,
305
- offset: Math.round(i / (n - 1) * 1e4) / 1e4
306
- }));
307
- return {
308
- ...g,
309
- center: g.center ? { ...g.center } : void 0,
310
- stops: distributed
311
- };
312
- }
313
- function formatPercent(fraction) {
314
- const pct = Math.round(fraction * 1e3) / 10;
315
- return `${pct}%`;
316
- }
317
- function gradientToCss(g) {
318
- const prefix = g.repeating ? `repeating-${g.type}-gradient` : `${g.type}-gradient`;
319
- const parts = [];
320
- const interpPrefix = g.interpolation && g.interpolation !== "srgb" ? `in ${g.interpolation} ` : "";
321
- if (g.type === "linear") {
322
- const angle = g.angle !== void 0 ? `${g.angle}deg` : "180deg";
323
- parts.push(`${interpPrefix}${angle}`.trim());
324
- } else if (g.type === "radial") {
325
- const shape = _nullishCoalesce(g.shape, () => ( "ellipse"));
326
- const cx = g.center ? formatPercent(g.center.x) : "50%";
327
- const cy = g.center ? formatPercent(g.center.y) : "50%";
328
- parts.push(`${interpPrefix}${shape} at ${cx} ${cy}`.trim());
329
- } else if (g.type === "conic") {
330
- const angle = g.angle !== void 0 ? `from ${g.angle}deg` : "from 0deg";
331
- const cx = g.center ? formatPercent(g.center.x) : "50%";
332
- const cy = g.center ? formatPercent(g.center.y) : "50%";
333
- parts.push(`${interpPrefix}${angle} at ${cx} ${cy}`.trim());
334
- }
335
- for (let i = 0; i < g.stops.length; i++) {
336
- const s = g.stops[i];
337
- parts.push(`${s.color} ${formatPercent(s.offset)}`);
338
- if (s.midpoint !== void 0 && i < g.stops.length - 1) {
339
- const next = g.stops[i + 1];
340
- const midOffset = s.offset + (next.offset - s.offset) * s.midpoint;
341
- parts.push(formatPercent(midOffset));
342
- }
343
- }
344
- return `${prefix}(${parts.join(", ")})`;
345
- }
346
- function gradientToCssFallback(g, steps = 16, scheme = "light") {
347
- const prefix = g.repeating ? `repeating-${g.type}-gradient` : `${g.type}-gradient`;
348
- const parts = [];
349
- if (g.type === "linear") {
350
- const angle = g.angle !== void 0 ? `${g.angle}deg` : "180deg";
351
- parts.push(angle);
352
- } else if (g.type === "radial") {
353
- const shape = _nullishCoalesce(g.shape, () => ( "ellipse"));
354
- const cx = g.center ? formatPercent(g.center.x) : "50%";
355
- const cy = g.center ? formatPercent(g.center.y) : "50%";
356
- parts.push(`${shape} at ${cx} ${cy}`);
357
- } else if (g.type === "conic") {
358
- const angle = g.angle !== void 0 ? `from ${g.angle}deg` : "from 0deg";
359
- const cx = g.center ? formatPercent(g.center.x) : "50%";
360
- const cy = g.center ? formatPercent(g.center.y) : "50%";
361
- parts.push(`${angle} at ${cx} ${cy}`);
362
- }
363
- for (let i = 0; i <= steps; i++) {
364
- const t = i / steps;
365
- const color = sampleGradient(g, t, scheme);
366
- parts.push(`${color} ${formatPercent(t)}`);
367
- }
368
- return `${prefix}(${parts.join(", ")})`;
369
- }
370
- function splitCssArgs(str) {
371
- const parts = [];
372
- let depth = 0;
373
- let current = "";
374
- for (let i = 0; i < str.length; i++) {
375
- const char = str[i];
376
- if (char === "(") depth++;
377
- else if (char === ")") depth = Math.max(0, depth - 1);
378
- if (char === "," && depth === 0) {
379
- if (current.trim()) parts.push(current.trim());
380
- current = "";
381
- } else {
382
- current += char;
383
- }
384
- }
385
- if (current.trim()) parts.push(current.trim());
386
- return parts;
387
- }
388
- function parseCssAngle(val) {
389
- const trimmed = val.trim().toLowerCase();
390
- if (trimmed.endsWith("deg")) return parseFloat(trimmed);
391
- if (trimmed.endsWith("turn")) return parseFloat(trimmed) * 360;
392
- if (trimmed.endsWith("rad")) return parseFloat(trimmed) * 180 / Math.PI;
393
- if (trimmed.startsWith("to ")) {
394
- const dir = trimmed.slice(3).trim();
395
- if (dir === "bottom") return 180;
396
- if (dir === "top") return 0;
397
- if (dir === "right") return 90;
398
- if (dir === "left") return 270;
399
- if (dir === "bottom right" || dir === "right bottom") return 135;
400
- if (dir === "bottom left" || dir === "left bottom") return 225;
401
- if (dir === "top right" || dir === "right top") return 45;
402
- if (dir === "top left" || dir === "left top") return 315;
403
- }
404
- const n = parseFloat(trimmed);
405
- return Number.isFinite(n) ? n : 180;
406
- }
407
- function parseCssGradient(css) {
408
- if (typeof css !== "string") return null;
409
- const trimmed = css.trim();
410
- const match = trimmed.match(/^(repeating-)?(linear|radial|conic)-gradient\s*\(([\s\S]*)\)$/i);
411
- if (!match) return null;
412
- const repeating = Boolean(match[1]);
413
- const type = match[2].toLowerCase();
414
- const body = match[3];
415
- const rawArgs = splitCssArgs(body);
416
- if (rawArgs.length === 0) return null;
417
- let angle = type === "linear" ? 180 : 0;
418
- let center = type !== "linear" ? { x: 0.5, y: 0.5 } : void 0;
419
- let shape = type === "radial" ? "ellipse" : void 0;
420
- let interpolation = "srgb";
421
- let stopStartIndex = 0;
422
- let first = rawArgs[0].trim();
423
- const inMatch = first.match(/^in\s+([a-z0-9-]+)\s*(.*)$/i);
424
- if (inMatch) {
425
- const space = inMatch[1].toLowerCase();
426
- if (space === "oklab" || space === "oklch" || space === "srgb" || space === "srgb-linear") {
427
- interpolation = space;
428
- }
429
- first = inMatch[2].trim();
430
- if (!first) {
431
- stopStartIndex = 1;
432
- first = _nullishCoalesce(_optionalChain([rawArgs, 'access', _ => _[1], 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]), () => ( ""));
433
- }
434
- }
435
- if (first) {
436
- if (type === "linear") {
437
- if (first.endsWith("deg") || first.endsWith("turn") || first.endsWith("rad") || first.startsWith("to ")) {
438
- angle = parseCssAngle(first);
439
- stopStartIndex = stopStartIndex === 0 ? 1 : 2;
440
- }
441
- } else if (type === "radial") {
442
- if (first.includes("circle") || first.includes("ellipse") || first.includes("at ")) {
443
- if (first.includes("circle")) shape = "circle";
444
- if (first.includes("ellipse")) shape = "ellipse";
445
- const atIdx = first.indexOf("at ");
446
- if (atIdx !== -1) {
447
- const posParts = first.slice(atIdx + 3).trim().split(/\s+/);
448
- if (posParts[0]) center = { x: parsePos(posParts[0]), y: posParts[1] ? parsePos(posParts[1]) : parsePos(posParts[0]) };
449
- }
450
- stopStartIndex = stopStartIndex === 0 ? 1 : 2;
451
- }
452
- } else if (type === "conic") {
453
- if (first.includes("from ") || first.includes("at ")) {
454
- const fromMatch = first.match(/from\s+([0-9.]+(?:deg|turn|rad)?)/i);
455
- if (fromMatch) angle = parseCssAngle(fromMatch[1]);
456
- const atIdx = first.indexOf("at ");
457
- if (atIdx !== -1) {
458
- const posParts = first.slice(atIdx + 3).trim().split(/\s+/);
459
- if (posParts[0]) center = { x: parsePos(posParts[0]), y: posParts[1] ? parsePos(posParts[1]) : parsePos(posParts[0]) };
460
- }
461
- stopStartIndex = stopStartIndex === 0 ? 1 : 2;
462
- }
463
- }
464
- }
465
- const stopArgs = rawArgs.slice(stopStartIndex);
466
- if (stopArgs.length === 0) return null;
467
- const items = [];
468
- for (const str of stopArgs) {
469
- const s = str.trim();
470
- if (/^([0-9.]+)%$/.test(s)) {
471
- items.push({ kind: "hint", offset: parseFloat(s) / 100 });
472
- continue;
473
- }
474
- const posMatch = s.match(/\s+([0-9.]+)%$/);
475
- if (posMatch) {
476
- const colorStr = s.slice(0, posMatch.index).trim();
477
- const offset = parseFloat(posMatch[1]) / 100;
478
- items.push({ kind: "stop", color: colorStr, offset });
479
- } else {
480
- items.push({ kind: "stop", color: s });
481
- }
482
- }
483
- const rawStops = items.filter((it) => it.kind === "stop");
484
- if (rawStops.length < 2) return null;
485
- for (let i = 0; i < rawStops.length; i++) {
486
- if (rawStops[i].offset === void 0) {
487
- if (i === 0) rawStops[i].offset = 0;
488
- else if (i === rawStops.length - 1) rawStops[i].offset = 1;
489
- else {
490
- let nextIdx = i + 1;
491
- while (nextIdx < rawStops.length && rawStops[nextIdx].offset === void 0) {
492
- nextIdx++;
493
- }
494
- const nextOffset = nextIdx < rawStops.length ? rawStops[nextIdx].offset : 1;
495
- const prevOffset = _nullishCoalesce(rawStops[i - 1].offset, () => ( 0));
496
- const count = nextIdx - (i - 1);
497
- for (let k = i; k < nextIdx; k++) {
498
- rawStops[k].offset = prevOffset + (k - (i - 1)) / count * (nextOffset - prevOffset);
499
- }
500
- i = nextIdx - 1;
501
- }
502
- }
503
- }
504
- const finalStops = [];
505
- let stopIdx = 0;
506
- for (let i = 0; i < items.length; i++) {
507
- const it = items[i];
508
- if (it.kind === "stop") {
509
- const stop = {
510
- id: `s${stopIdx + 1}`,
511
- offset: clamp(_nullishCoalesce(it.offset, () => ( 0)), 0, 1),
512
- color: it.color
513
- };
514
- if (i + 1 < items.length && items[i + 1].kind === "hint") {
515
- const hintOffset = items[i + 1].offset;
516
- const nextStopItem = items.slice(i + 2).find((x) => x.kind === "stop");
517
- if (nextStopItem && nextStopItem.offset !== void 0) {
518
- const span = nextStopItem.offset - stop.offset;
519
- if (span > 0) {
520
- stop.midpoint = clamp((hintOffset - stop.offset) / span, 0.05, 0.95);
521
- }
522
- }
523
- }
524
- finalStops.push(stop);
525
- stopIdx++;
526
- }
527
- }
528
- const res = normalizeGradient({
529
- type,
530
- angle,
531
- center,
532
- shape,
533
- interpolation,
534
- repeating,
535
- stops: finalStops
536
- });
537
- return "gradient" in res ? res.gradient : null;
538
- }
539
- function parsePos(str) {
540
- const s = str.trim();
541
- if (s.endsWith("%")) return clamp(parseFloat(s) / 100, 0, 1);
542
- const val = parseFloat(s);
543
- return Number.isFinite(val) ? clamp(val, 0, 1) : 0.5;
544
- }
545
- function gradientToSvgDef(g, id, scheme = "light") {
546
- const steps = 32;
547
- const stopsXml = [];
548
- for (let i = 0; i <= steps; i++) {
549
- const t = i / steps;
550
- const color = sampleGradient(g, t, scheme);
551
- stopsXml.push(` <stop offset="${formatPercent(t)}" stop-color="${color}" />`);
552
- }
553
- if (g.type === "radial") {
554
- const cx = g.center ? formatPercent(g.center.x) : "50%";
555
- const cy = g.center ? formatPercent(g.center.y) : "50%";
556
- const r = g.shape === "circle" ? "50%" : "70.7%";
557
- return `<radialGradient id="${id}" cx="${cx}" cy="${cy}" r="${r}">
558
- ${stopsXml.join("\n")}
559
- </radialGradient>`;
560
- }
561
- if (g.type === "conic") {
562
- return `<!-- Note: SVG does not natively support conic gradients; rendering as linear fallback -->
563
- <linearGradient id="${id}" x1="0%" y1="0%" x2="100%" y2="0%">
564
- ${stopsXml.join("\n")}
565
- </linearGradient>`;
566
- }
567
- const angle = _nullishCoalesce(g.angle, () => ( 180));
568
- const rad = (90 - angle) * Math.PI / 180;
569
- const dx = Math.cos(rad);
570
- const dy = -Math.sin(rad);
571
- const x1 = formatPercent(0.5 - dx * 0.5);
572
- const y1 = formatPercent(0.5 - dy * 0.5);
573
- const x2 = formatPercent(0.5 + dx * 0.5);
574
- const y2 = formatPercent(0.5 + dy * 0.5);
575
- return `<linearGradient id="${id}" x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}">
576
- ${stopsXml.join("\n")}
577
- </linearGradient>`;
578
- }
579
- function checkGradient(g, options = {}) {
580
- const dual = isDualGradient(g) || _chunk7PT4RJL5cjs.isDualColor.call(void 0, options.surface) || _chunk7PT4RJL5cjs.isDualColor.call(void 0, options.text);
581
- if (dual) {
582
- const lightG = resolveGradient(g, "light");
583
- const darkG = resolveGradient(g, "dark");
584
- const lightText = options.text ? _nullishCoalesce(_chunk7PT4RJL5cjs.resolveColor.call(void 0, options.text, "light"), () => ( void 0)) : void 0;
585
- const darkText = options.text ? _nullishCoalesce(_chunk7PT4RJL5cjs.resolveColor.call(void 0, options.text, "dark"), () => ( void 0)) : void 0;
586
- const checkScheme = (grad, txt, schemeName) => {
587
- const issues2 = [];
588
- for (let i = 0; i < grad.stops.length - 1; i++) {
589
- const gap = grad.stops[i + 1].offset - grad.stops[i].offset;
590
- if (gap < 5e-3) {
591
- issues2.push(`[${schemeName}] Stops "${grad.stops[i].id}" and "${grad.stops[i + 1].id}" are too close (gap < 0.005).`);
592
- }
593
- const c1 = _chunkRD7RX52Gcjs.parseColor.call(void 0, grad.stops[i].color);
594
- const c2 = _chunkRD7RX52Gcjs.parseColor.call(void 0, grad.stops[i + 1].color);
595
- if (c1 && c2 && gap < 0.05) {
596
- const dE = _chunkPPFJHTW5cjs.deltaEOK.call(void 0, c1, c2);
597
- if (dE > 0.5) {
598
- issues2.push(
599
- `[${schemeName}] Banding risk between stops "${grad.stops[i].id}" and "${grad.stops[i + 1].id}" (\u0394EOK = ${dE.toFixed(2)} across gap ${gap.toFixed(3)}).`
600
- );
601
- }
602
- }
603
- }
604
- for (const s of grad.stops) {
605
- const c = _chunkRD7RX52Gcjs.parseColor.call(void 0, s.color);
606
- if (c && !_chunkPPFJHTW5cjs.inSrgbGamut.call(void 0, c)) {
607
- issues2.push(`[${schemeName}] Stop "${s.id}" colour ${s.color} is outside standard sRGB gamut.`);
608
- }
609
- }
610
- let lowest;
611
- if (txt) {
612
- const parsedTxt = _chunkRD7RX52Gcjs.parseColor.call(void 0, txt);
613
- if (parsedTxt) {
614
- let minRatio = Infinity;
615
- for (let step = 0; step <= 32; step++) {
616
- const sample = _chunkRD7RX52Gcjs.parseColor.call(void 0, sampleGradient(grad, step / 32, schemeName));
617
- if (sample) {
618
- const r = _chunkRD7RX52Gcjs.contrastRatio.call(void 0, parsedTxt, sample);
619
- if (r < minRatio) minRatio = r;
620
- }
621
- }
622
- lowest = Math.round(minRatio * 100) / 100;
623
- }
624
- }
625
- return { issues: issues2, lowest };
626
- };
627
- const lRes = checkScheme(lightG, lightText, "light");
628
- const dRes = checkScheme(darkG, darkText, "dark");
629
- return {
630
- issues: [...lRes.issues, ...dRes.issues],
631
- lowestTextContrast: lRes.lowest !== void 0 && dRes.lowest !== void 0 ? { light: lRes.lowest, dark: dRes.lowest } : void 0
632
- };
633
- }
634
- const issues = [];
635
- for (let i = 0; i < g.stops.length - 1; i++) {
636
- const gap = g.stops[i + 1].offset - g.stops[i].offset;
637
- if (gap < 5e-3) {
638
- issues.push(`Stops "${g.stops[i].id}" and "${g.stops[i + 1].id}" are too close (gap < 0.005).`);
639
- }
640
- const c1 = _chunkRD7RX52Gcjs.parseColor.call(void 0, g.stops[i].color);
641
- const c2 = _chunkRD7RX52Gcjs.parseColor.call(void 0, g.stops[i + 1].color);
642
- if (c1 && c2 && gap < 0.05) {
643
- const dE = _chunkPPFJHTW5cjs.deltaEOK.call(void 0, c1, c2);
644
- if (dE > 0.5) {
645
- issues.push(
646
- `Banding risk between stops "${g.stops[i].id}" and "${g.stops[i + 1].id}" (\u0394EOK = ${dE.toFixed(2)} across gap ${gap.toFixed(3)}).`
647
- );
648
- }
649
- }
650
- }
651
- for (const s of g.stops) {
652
- const c = _chunkRD7RX52Gcjs.parseColor.call(void 0, s.color);
653
- if (c && !_chunkPPFJHTW5cjs.inSrgbGamut.call(void 0, c)) {
654
- issues.push(`Stop "${s.id}" colour ${s.color} is outside standard sRGB gamut.`);
655
- }
656
- }
657
- let lowestTextContrast;
658
- if (options.text) {
659
- const parsedTxt = _chunkRD7RX52Gcjs.parseColor.call(void 0, options.text);
660
- if (parsedTxt) {
661
- let minRatio = Infinity;
662
- for (let step = 0; step <= 32; step++) {
663
- const sample = _chunkRD7RX52Gcjs.parseColor.call(void 0, sampleGradient(g, step / 32, "light"));
664
- if (sample) {
665
- const r = _chunkRD7RX52Gcjs.contrastRatio.call(void 0, parsedTxt, sample);
666
- if (r < minRatio) minRatio = r;
667
- }
668
- }
669
- lowestTextContrast = Math.round(minRatio * 100) / 100;
670
- }
671
- }
672
- return { issues, lowestTextContrast };
673
- }
674
- var GRADIENT_PRESETS = Object.freeze({
675
- sunset: {
676
- type: "linear",
677
- angle: 90,
678
- interpolation: "oklab",
679
- stops: [
680
- { id: "s1", offset: 0, color: "#f97316" },
681
- { id: "s2", offset: 1, color: "#ec4899" }
682
- ]
683
- },
684
- ocean: {
685
- type: "linear",
686
- angle: 180,
687
- interpolation: "oklab",
688
- stops: [
689
- { id: "s1", offset: 0, color: "#06b6d4" },
690
- { id: "s2", offset: 1, color: "#3b82f6" }
691
- ]
692
- },
693
- "mono-fade": {
694
- type: "linear",
695
- angle: 180,
696
- interpolation: "oklab",
697
- stops: [
698
- { id: "s1", offset: 0, color: "#ffffff" },
699
- { id: "s2", offset: 1, color: "#000000" }
700
- ]
701
- },
702
- "transparent-fade": {
703
- type: "linear",
704
- angle: 180,
705
- interpolation: "oklab",
706
- stops: [
707
- { id: "s1", offset: 0, color: "#000000ff" },
708
- { id: "s2", offset: 1, color: "#00000000" }
709
- ]
710
- },
711
- aurora: {
712
- type: "linear",
713
- angle: 135,
714
- interpolation: "oklab",
715
- stops: [
716
- { id: "s1", offset: 0, color: "#10b981" },
717
- { id: "s2", offset: 0.5, color: "#06b6d4" },
718
- { id: "s3", offset: 1, color: "#8b5cf6" }
719
- ]
720
- },
721
- neon: {
722
- type: "linear",
723
- angle: 90,
724
- interpolation: "oklab",
725
- stops: [
726
- { id: "s1", offset: 0, color: "#f43f5e" },
727
- { id: "s2", offset: 1, color: "#8b5cf6" }
728
- ]
729
- },
730
- radial: {
731
- type: "radial",
732
- shape: "ellipse",
733
- center: { x: 0.5, y: 0.5 },
734
- interpolation: "oklab",
735
- stops: [
736
- { id: "s1", offset: 0, color: "#ffffff" },
737
- { id: "s2", offset: 1, color: "#000000" }
738
- ]
739
- },
740
- conic: {
741
- type: "conic",
742
- angle: 0,
743
- center: { x: 0.5, y: 0.5 },
744
- interpolation: "oklab",
745
- stops: [
746
- { id: "s1", offset: 0, color: "#f43f5e" },
747
- { id: "s2", offset: 0.33, color: "#eab308" },
748
- { id: "s3", offset: 0.66, color: "#06b6d4" },
749
- { id: "s4", offset: 1, color: "#f43f5e" }
750
- ]
751
- }
752
- });
753
-
754
40
  // src/gradient/controller.ts
755
41
  var GRADIENT_TYPES = ["linear", "radial", "conic"];
756
42
  var GRADIENT_INTERPOLATIONS = ["oklab", "oklch", "srgb", "srgb-linear"];
@@ -809,11 +95,11 @@ var GradientController = (_class = class {
809
95
  __init6() {this._cachedState = null}
810
96
  constructor(options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);
811
97
  if (options.value !== void 0) {
812
- const norm = normalizeGradient(options.value);
98
+ const norm = _chunkS6OT6CTScjs.normalizeGradient.call(void 0, options.value);
813
99
  if ("error" in norm) throw new Error(norm.error);
814
100
  this._gradient = norm.gradient;
815
101
  } else {
816
- this._gradient = createGradient(["#000000", "#ffffff"]);
102
+ this._gradient = _chunkS6OT6CTScjs.createGradient.call(void 0, ["#000000", "#ffffff"]);
817
103
  }
818
104
  this._history = _nullishCoalesce(options.history, () => ( new (0, _chunkFJ2MSI43cjs.HistoryManager)()));
819
105
  this._readOnlyOption = options.readOnly;
@@ -920,7 +206,7 @@ var GradientController = (_class = class {
920
206
  const ro = this._checkReadOnly();
921
207
  if (ro) return ro;
922
208
  if (newStopId !== null && !newGradient.stops.some((s) => s.id === newStopId)) {
923
- newStopId = _nullishCoalesce(_optionalChain([newGradient, 'access', _4 => _4.stops, 'access', _5 => _5[0], 'optionalAccess', _6 => _6.id]), () => ( null));
209
+ newStopId = _nullishCoalesce(_optionalChain([newGradient, 'access', _ => _.stops, 'access', _2 => _2[0], 'optionalAccess', _3 => _3.id]), () => ( null));
924
210
  }
925
211
  if (sameGradientJson(this._gradient, newGradient) && this._selectedStopId === newStopId) {
926
212
  return { ok: true };
@@ -953,10 +239,10 @@ var GradientController = (_class = class {
953
239
  if (this._selectedStopId === null) return null;
954
240
  if (next.stops.some((s) => s.id === this._selectedStopId)) return this._selectedStopId;
955
241
  const index = this._gradient.stops.findIndex((s) => s.id === this._selectedStopId);
956
- return _nullishCoalesce(_optionalChain([next, 'access', _7 => _7.stops, 'access', _8 => _8[Math.min(Math.max(index, 0), next.stops.length - 1)], 'optionalAccess', _9 => _9.id]), () => ( null));
242
+ return _nullishCoalesce(_optionalChain([next, 'access', _4 => _4.stops, 'access', _5 => _5[Math.min(Math.max(index, 0), next.stops.length - 1)], 'optionalAccess', _6 => _6.id]), () => ( null));
957
243
  }
958
244
  setGradient(value, label = "Set Gradient") {
959
- const norm = normalizeGradient(value);
245
+ const norm = _chunkS6OT6CTScjs.normalizeGradient.call(void 0, value);
960
246
  if ("error" in norm) {
961
247
  return { ok: false, message: norm.error };
962
248
  }
@@ -967,7 +253,7 @@ var GradientController = (_class = class {
967
253
  * host). Keeps the selection when the stop survives. Notifies subscribers and `onChange`.
968
254
  */
969
255
  load(value, options = {}) {
970
- const norm = normalizeGradient(value);
256
+ const norm = _chunkS6OT6CTScjs.normalizeGradient.call(void 0, value);
971
257
  if ("error" in norm) {
972
258
  return { ok: false, message: norm.error };
973
259
  }
@@ -1004,7 +290,7 @@ var GradientController = (_class = class {
1004
290
  if (patch.angle !== void 0 && merged.type === "radial") {
1005
291
  return { ok: false, message: "The angle applies to linear and conic gradients only; this one is radial." };
1006
292
  }
1007
- const norm = normalizeGradient(merged);
293
+ const norm = _chunkS6OT6CTScjs.normalizeGradient.call(void 0, merged);
1008
294
  if ("error" in norm) return { ok: false, message: norm.error };
1009
295
  return this._apply(norm.gradient, label);
1010
296
  }
@@ -1030,7 +316,7 @@ var GradientController = (_class = class {
1030
316
  const ro = this._checkReadOnly();
1031
317
  if (ro) return { ok: false, message: ro.message };
1032
318
  try {
1033
- const { gradient: next, stop } = insertGradientStop(this._gradient, offset, color);
319
+ const { gradient: next, stop } = _chunkS6OT6CTScjs.insertGradientStop.call(void 0, this._gradient, offset, color);
1034
320
  const res = this._apply(next, "Add Gradient Stop", stop.id);
1035
321
  if (!res.ok) return res;
1036
322
  return { ok: true, stop };
@@ -1042,7 +328,7 @@ var GradientController = (_class = class {
1042
328
  const ro = this._checkReadOnly();
1043
329
  if (ro) return ro;
1044
330
  try {
1045
- const next = updateGradientStop(this._gradient, id, patch);
331
+ const next = _chunkS6OT6CTScjs.updateGradientStop.call(void 0, this._gradient, id, patch);
1046
332
  return this._apply(next, "Update Gradient Stop");
1047
333
  } catch (e) {
1048
334
  return { ok: false, message: e instanceof Error ? e.message : String(e) };
@@ -1052,7 +338,7 @@ var GradientController = (_class = class {
1052
338
  const ro = this._checkReadOnly();
1053
339
  if (ro) return ro;
1054
340
  try {
1055
- const next = removeGradientStop(this._gradient, id);
341
+ const next = _chunkS6OT6CTScjs.removeGradientStop.call(void 0, this._gradient, id);
1056
342
  return this._apply(next, "Remove Gradient Stop");
1057
343
  } catch (e) {
1058
344
  return { ok: false, message: e instanceof Error ? e.message : String(e) };
@@ -1061,13 +347,13 @@ var GradientController = (_class = class {
1061
347
  reverse() {
1062
348
  const ro = this._checkReadOnly();
1063
349
  if (ro) return ro;
1064
- const next = reverseGradient(this._gradient);
350
+ const next = _chunkS6OT6CTScjs.reverseGradient.call(void 0, this._gradient);
1065
351
  return this._apply(next, "Reverse Gradient");
1066
352
  }
1067
353
  distribute() {
1068
354
  const ro = this._checkReadOnly();
1069
355
  if (ro) return ro;
1070
- const next = distributeGradientStops(this._gradient);
356
+ const next = _chunkS6OT6CTScjs.distributeGradientStops.call(void 0, this._gradient);
1071
357
  return this._apply(next, "Distribute Gradient Stops");
1072
358
  }
1073
359
  undo() {
@@ -1115,9 +401,9 @@ function createGradientToolProvider(controller, options = {}) {
1115
401
  const ro = readOnlyReason();
1116
402
  return {
1117
403
  gradient: g,
1118
- css: gradientToCss(g),
1119
- svgDef: gradientToSvgDef(g, "gradient-preview"),
1120
- isDual: isDualGradient(g),
404
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, g),
405
+ svgDef: _chunkS6OT6CTScjs.gradientToSvgDef.call(void 0, g, "gradient-preview"),
406
+ isDual: _chunkS6OT6CTScjs.isDualGradient.call(void 0, g),
1121
407
  selectedStopId: controller.getSelectedStopId(),
1122
408
  readOnly: ro !== null,
1123
409
  ...ro ? { readOnlyReason: ro } : {}
@@ -1156,7 +442,7 @@ function createGradientToolProvider(controller, options = {}) {
1156
442
  if (args.text !== void 0) checkOpts.text = args.text;
1157
443
  if (typeof args.fontSize === "number") checkOpts.fontSize = args.fontSize;
1158
444
  if (typeof args.bold === "boolean") checkOpts.bold = args.bold;
1159
- const res = checkGradient(g, checkOpts);
445
+ const res = _chunkS6OT6CTScjs.checkGradient.call(void 0, g, checkOpts);
1160
446
  return {
1161
447
  pass: res.issues.length === 0,
1162
448
  issueCount: res.issues.length,
@@ -1176,10 +462,10 @@ function createGradientToolProvider(controller, options = {}) {
1176
462
  annotations: readTool("List Gradient Presets"),
1177
463
  handler: async () => {
1178
464
  return {
1179
- presets: Object.entries(GRADIENT_PRESETS).map(([id, gradient]) => ({
465
+ presets: Object.entries(_chunkS6OT6CTScjs.GRADIENT_PRESETS).map(([id, gradient]) => ({
1180
466
  id,
1181
467
  name: id,
1182
- css: gradientToCss(gradient),
468
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, gradient),
1183
469
  gradient
1184
470
  }))
1185
471
  };
@@ -1224,7 +510,7 @@ function createGradientToolProvider(controller, options = {}) {
1224
510
  const current = controller.getGradient();
1225
511
  return {
1226
512
  gradient: current,
1227
- css: gradientToCss(current)
513
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, current)
1228
514
  };
1229
515
  }
1230
516
  },
@@ -1292,7 +578,7 @@ function createGradientToolProvider(controller, options = {}) {
1292
578
  const current = controller.getGradient();
1293
579
  return {
1294
580
  gradient: current,
1295
- css: gradientToCss(current)
581
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, current)
1296
582
  };
1297
583
  }
1298
584
  },
@@ -1322,7 +608,7 @@ function createGradientToolProvider(controller, options = {}) {
1322
608
  return {
1323
609
  stop: res.stop,
1324
610
  gradient: controller.getGradient(),
1325
- css: gradientToCss(controller.getGradient())
611
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, controller.getGradient())
1326
612
  };
1327
613
  }
1328
614
  },
@@ -1364,7 +650,7 @@ function createGradientToolProvider(controller, options = {}) {
1364
650
  return {
1365
651
  stop: controller.getGradient().stops.find((s) => s.id === args.id),
1366
652
  gradient: controller.getGradient(),
1367
- css: gradientToCss(controller.getGradient())
653
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, controller.getGradient())
1368
654
  };
1369
655
  }
1370
656
  },
@@ -1390,7 +676,7 @@ function createGradientToolProvider(controller, options = {}) {
1390
676
  return {
1391
677
  removedId: args.id,
1392
678
  gradient: controller.getGradient(),
1393
- css: gradientToCss(controller.getGradient())
679
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, controller.getGradient())
1394
680
  };
1395
681
  }
1396
682
  },
@@ -1409,7 +695,7 @@ function createGradientToolProvider(controller, options = {}) {
1409
695
  if (!res.ok) fail(res.message);
1410
696
  return {
1411
697
  gradient: controller.getGradient(),
1412
- css: gradientToCss(controller.getGradient())
698
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, controller.getGradient())
1413
699
  };
1414
700
  }
1415
701
  },
@@ -1428,10 +714,61 @@ function createGradientToolProvider(controller, options = {}) {
1428
714
  if (!res.ok) fail(res.message);
1429
715
  return {
1430
716
  gradient: controller.getGradient(),
1431
- css: gradientToCss(controller.getGradient())
717
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, controller.getGradient())
1432
718
  };
1433
719
  }
1434
720
  },
721
+ {
722
+ name: "set_gradient_from_samples",
723
+ title: "Set Gradient From Sampled Colours",
724
+ description: "Fits the fewest stops that reproduce colours sampled along a line (from a photo, painting or shape, e.g. image sample_gradient or your own picks) and makes them the gradient's stops, keeping its type, angle and centre and switching interpolation to oklab. Noise is smoothed first; stops are added where the colour turns most until every sample is within tolerance. One undo step. Returns the gradient, its CSS, the stop count and the largest error left (OKLab \u0394E).",
725
+ inputSchema: {
726
+ type: "object",
727
+ properties: {
728
+ samples: {
729
+ type: "array",
730
+ minItems: 2,
731
+ maxItems: 1024,
732
+ description: "Colours in order along the line.",
733
+ items: {
734
+ type: "object",
735
+ properties: {
736
+ offset: { type: "number", minimum: 0, maximum: 1, description: "Position along the line, 0..1." },
737
+ color: { type: "string", description: "Any CSS colour (hex, rgb(), oklch(), named\u2026)." }
738
+ },
739
+ required: ["offset", "color"],
740
+ additionalProperties: false
741
+ }
742
+ },
743
+ tolerance: { type: "number", exclusiveMinimum: 0, maximum: 1, description: "Largest error left, OKLab \u0394E (default 0.06; 0.02 is a just-noticeable step, lower keeps more stops)." },
744
+ minStops: { type: "integer", minimum: 2, maximum: 64, description: "Fewest stops (default 2)." },
745
+ maxStops: { type: "integer", minimum: 2, maximum: 64, description: "Most stops (default 5)." },
746
+ smoothing: { type: "integer", minimum: 0, maximum: 16, description: "Samples on each side averaged into each one to ignore grain and texture (default 2; 0 fits the raw colours)." }
747
+ },
748
+ required: ["samples"],
749
+ additionalProperties: false
750
+ },
751
+ annotations: writeIdempotentTool("Set Gradient From Sampled Colours"),
752
+ handler: async (args) => {
753
+ const unreadable = args.samples.findIndex((x) => !_chunkRD7RX52Gcjs.isColor.call(void 0, x.color));
754
+ if (unreadable >= 0) fail(`samples[${unreadable}].color ${JSON.stringify(args.samples[unreadable].color)} is not a CSS colour.`);
755
+ let fit;
756
+ try {
757
+ fit = _chunkS6OT6CTScjs.fitGradientStops.call(void 0, args.samples, {
758
+ tolerance: args.tolerance,
759
+ minStops: args.minStops,
760
+ maxStops: args.maxStops,
761
+ smoothing: args.smoothing
762
+ });
763
+ } catch (e) {
764
+ return fail(e.message);
765
+ }
766
+ const res = controller.setGradient({ ...controller.getGradient(), stops: fit.stops, interpolation: "oklab" }, "Gradient From Samples");
767
+ if (!res.ok) fail(res.message);
768
+ const gradient = controller.getGradient();
769
+ return { gradient, css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, gradient), stopCount: gradient.stops.length, maxError: fit.maxError };
770
+ }
771
+ },
1435
772
  {
1436
773
  name: "apply_gradient_preset",
1437
774
  title: "Apply Gradient Preset",
@@ -1449,7 +786,7 @@ function createGradientToolProvider(controller, options = {}) {
1449
786
  },
1450
787
  annotations: writeIdempotentTool("Apply Gradient Preset"),
1451
788
  handler: async (args) => {
1452
- const gradient = GRADIENT_PRESETS[args.id];
789
+ const gradient = _chunkS6OT6CTScjs.GRADIENT_PRESETS[args.id];
1453
790
  if (!gradient) {
1454
791
  fail(`Unknown gradient preset "${args.id}". Use list_gradient_presets to inspect available presets.`);
1455
792
  }
@@ -1458,7 +795,7 @@ function createGradientToolProvider(controller, options = {}) {
1458
795
  return {
1459
796
  preset: args.id,
1460
797
  gradient: controller.getGradient(),
1461
- css: gradientToCss(controller.getGradient())
798
+ css: _chunkS6OT6CTScjs.gradientToCss.call(void 0, controller.getGradient())
1462
799
  };
1463
800
  }
1464
801
  }
@@ -1474,55 +811,6 @@ function createGradientToolProvider(controller, options = {}) {
1474
811
  };
1475
812
  }
1476
813
 
1477
- // src/gradient/editor.ts
1478
- function stopFromTrackPosition(g, x) {
1479
- return Math.max(0, Math.min(1, Math.round(x * 1e3) / 1e3));
1480
- }
1481
- function trackPositionFromStop(stop) {
1482
- return Math.max(0, Math.min(1, stop.offset));
1483
- }
1484
- function gradientKeyStep(g, id, key, shift = false) {
1485
- const stop = g.stops.find((s) => s.id === id);
1486
- if (!stop) return null;
1487
- const step = shift ? 0.1 : 0.01;
1488
- switch (key) {
1489
- case "ArrowLeft":
1490
- case "ArrowDown": {
1491
- const offset = Math.max(0, Math.round((stop.offset - step) * 1e3) / 1e3);
1492
- return { stopId: id, offset };
1493
- }
1494
- case "ArrowRight":
1495
- case "ArrowUp": {
1496
- const offset = Math.min(1, Math.round((stop.offset + step) * 1e3) / 1e3);
1497
- return { stopId: id, offset };
1498
- }
1499
- case "Delete":
1500
- case "Backspace": {
1501
- return { stopId: id, remove: true };
1502
- }
1503
- default:
1504
- return null;
1505
- }
1506
- }
1507
- function angleFromPointer(center, point, snapStep) {
1508
- const dx = point.x - center.x;
1509
- const dy = point.y - center.y;
1510
- let deg = Math.atan2(dx, -dy) * 180 / Math.PI;
1511
- if (deg < 0) deg += 360;
1512
- if (snapStep && snapStep > 0) {
1513
- deg = Math.round(deg / snapStep) * snapStep;
1514
- if (deg >= 360) deg -= 360;
1515
- }
1516
- return Math.round(deg);
1517
- }
1518
- function gradientTrackBackground(g) {
1519
- const linearCopy = {
1520
- ...g,
1521
- type: "linear",
1522
- angle: 90
1523
- };
1524
- return gradientToCss(linearCopy);
1525
- }
1526
814
 
1527
815
 
1528
816
 
@@ -1551,4 +839,6 @@ function gradientTrackBackground(g) {
1551
839
 
1552
840
 
1553
841
 
1554
- exports.GRADIENT_INTERPOLATIONS = GRADIENT_INTERPOLATIONS; exports.GRADIENT_PRESETS = GRADIENT_PRESETS; exports.GRADIENT_TYPES = GRADIENT_TYPES; exports.GradientController = GradientController; exports.angleFromPointer = angleFromPointer; exports.checkGradient = checkGradient; exports.createGradient = createGradient; exports.createGradientToolProvider = createGradientToolProvider; exports.distributeGradientStops = distributeGradientStops; exports.gradientKeyStep = gradientKeyStep; exports.gradientMidpointRemap = gradientMidpointRemap; exports.gradientToCss = gradientToCss; exports.gradientToCssFallback = gradientToCssFallback; exports.gradientToSvgDef = gradientToSvgDef; exports.gradientTrackBackground = gradientTrackBackground; exports.insertGradientStop = insertGradientStop; exports.isDualColorValue = isDualColorValue; exports.isDualGradient = isDualGradient; exports.normalizeGradient = normalizeGradient; exports.parseCssGradient = parseCssGradient; exports.removeGradientStop = removeGradientStop; exports.resolveGradient = resolveGradient; exports.reverseGradient = reverseGradient; exports.sampleGradient = sampleGradient; exports.stopFromTrackPosition = stopFromTrackPosition; exports.trackPositionFromStop = trackPositionFromStop; exports.updateGradientStop = updateGradientStop;
842
+
843
+
844
+ exports.GRADIENT_INTERPOLATIONS = GRADIENT_INTERPOLATIONS; exports.GRADIENT_PRESETS = _chunkS6OT6CTScjs.GRADIENT_PRESETS; exports.GRADIENT_TYPES = GRADIENT_TYPES; exports.GradientController = GradientController; exports.angleFromPointer = _chunkS6OT6CTScjs.angleFromPointer; exports.checkGradient = _chunkS6OT6CTScjs.checkGradient; exports.createGradient = _chunkS6OT6CTScjs.createGradient; exports.createGradientToolProvider = createGradientToolProvider; exports.distributeGradientStops = _chunkS6OT6CTScjs.distributeGradientStops; exports.fitGradientStops = _chunkS6OT6CTScjs.fitGradientStops; exports.gradientKeyStep = _chunkS6OT6CTScjs.gradientKeyStep; exports.gradientMidpointRemap = _chunkS6OT6CTScjs.gradientMidpointRemap; exports.gradientToCss = _chunkS6OT6CTScjs.gradientToCss; exports.gradientToCssFallback = _chunkS6OT6CTScjs.gradientToCssFallback; exports.gradientToSvgDef = _chunkS6OT6CTScjs.gradientToSvgDef; exports.gradientTrackBackground = _chunkS6OT6CTScjs.gradientTrackBackground; exports.insertGradientStop = _chunkS6OT6CTScjs.insertGradientStop; exports.isDualColorValue = _chunkS6OT6CTScjs.isDualColorValue; exports.isDualGradient = _chunkS6OT6CTScjs.isDualGradient; exports.normalizeGradient = _chunkS6OT6CTScjs.normalizeGradient; exports.parseCssGradient = _chunkS6OT6CTScjs.parseCssGradient; exports.removeGradientStop = _chunkS6OT6CTScjs.removeGradientStop; exports.resolveGradient = _chunkS6OT6CTScjs.resolveGradient; exports.reverseGradient = _chunkS6OT6CTScjs.reverseGradient; exports.sampleGradient = _chunkS6OT6CTScjs.sampleGradient; exports.sampleGradientFromLine = _chunkS6OT6CTScjs.sampleGradientFromLine; exports.simplifyGradientStops = _chunkS6OT6CTScjs.simplifyGradientStops; exports.stopFromTrackPosition = _chunkS6OT6CTScjs.stopFromTrackPosition; exports.trackPositionFromStop = _chunkS6OT6CTScjs.trackPositionFromStop; exports.updateGradientStop = _chunkS6OT6CTScjs.updateGradientStop;