@rogieking/figui3 8.9.19 → 8.9.20

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/fig-lab.js CHANGED
@@ -724,6 +724,121 @@ function figLabSolidFillValue(raw) {
724
724
  return JSON.stringify({ type: "solid", color, alpha });
725
725
  }
726
726
 
727
+ function figLabCssUrl(url) {
728
+ return `url("${String(url).replace(/\\/g, "\\\\").replace(/"/g, '\\"')}")`;
729
+ }
730
+
731
+ function figLabParseFillValue(raw) {
732
+ if (raw && typeof raw === "object") return raw;
733
+ const text = String(raw ?? "").trim();
734
+ if (!text) return { type: "solid", color: "#D9D9D9", alpha: 1 };
735
+ if (text.startsWith("{")) {
736
+ try {
737
+ const parsed = JSON.parse(text);
738
+ if (parsed && typeof parsed === "object") return parsed;
739
+ } catch {}
740
+ }
741
+ if (text.startsWith("#")) {
742
+ const { color, alpha } = figLabParseSolidColor(text);
743
+ return { type: "solid", color, alpha };
744
+ }
745
+ return { type: "solid", color: "#D9D9D9", alpha: 1 };
746
+ }
747
+
748
+ function figLabSerializeFillValue(fill) {
749
+ if (fill == null) return "";
750
+ return typeof fill === "string" ? fill : JSON.stringify(fill);
751
+ }
752
+
753
+ function figLabGradientCss(gradient) {
754
+ if (!gradient) return "";
755
+ const stops = [...(gradient.stops || [])]
756
+ .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))
757
+ .map((stop) => `${stop.color} ${stop.position ?? 0}%`)
758
+ .join(", ");
759
+ if (!stops) return "";
760
+ switch (gradient.type) {
761
+ case "radial":
762
+ return `radial-gradient(circle, ${stops})`;
763
+ case "angular":
764
+ return `conic-gradient(from ${gradient.angle || 0}deg, ${stops})`;
765
+ default:
766
+ return `linear-gradient(${gradient.angle ?? 180}deg, ${stops})`;
767
+ }
768
+ }
769
+
770
+ function figLabFillSwatchBackground(fill, slot) {
771
+ switch (fill?.type) {
772
+ case "solid":
773
+ return fill.color || "";
774
+ case "gradient":
775
+ return fill.css || figLabGradientCss(fill.gradient) || "";
776
+ case "image":
777
+ return fill.image?.url ? figLabCssUrl(fill.image.url) : "";
778
+ case "video":
779
+ return fill.video?.poster ? figLabCssUrl(fill.video.poster) : "";
780
+ case "webcam":
781
+ return fill.webcam?.snapshot ? figLabCssUrl(fill.webcam.snapshot) : "";
782
+ default:
783
+ return (
784
+ fill?.swatchBackground ||
785
+ fill?.background ||
786
+ fill?.css ||
787
+ slot?.getAttribute("swatch-background") ||
788
+ ""
789
+ );
790
+ }
791
+ }
792
+
793
+ function figLabFillSwatchMedia(fill) {
794
+ if (!fill || typeof fill !== "object") return null;
795
+ if (fill.type === "image") return fill.image;
796
+ if (fill.type === "video") return fill.video;
797
+ if (fill.type === "webcam") return fill.webcam;
798
+ return null;
799
+ }
800
+
801
+ function figLabFillSwatchSizing(fill) {
802
+ const media = figLabFillSwatchMedia(fill);
803
+ const scale = Number(media?.scale);
804
+ switch (media?.scaleMode) {
805
+ case "fit":
806
+ return { size: "contain", position: "center" };
807
+ case "tile":
808
+ return {
809
+ size: `${Number.isFinite(scale) ? scale : 50}%`,
810
+ position: "top left",
811
+ };
812
+ case "fill":
813
+ case "crop":
814
+ return { size: "cover", position: "center" };
815
+ default:
816
+ return fill?.type === "webcam" || fill?.type === "image" || fill?.type === "video"
817
+ ? { size: "cover", position: "center" }
818
+ : null;
819
+ }
820
+ }
821
+
822
+ function figLabFillSwatchAlpha(fill) {
823
+ switch (fill?.type) {
824
+ case "solid": {
825
+ if (Number.isFinite(fill.alpha)) return fill.alpha;
826
+ if (Number.isFinite(fill.opacity)) return fill.opacity / 100;
827
+ return 1;
828
+ }
829
+ case "gradient":
830
+ return fill.gradient?.opacity ?? fill.opacity ?? 1;
831
+ case "image":
832
+ return fill.image?.opacity ?? 1;
833
+ case "video":
834
+ return fill.video?.opacity ?? 1;
835
+ case "webcam":
836
+ return fill.webcam?.opacity ?? 1;
837
+ default:
838
+ return fill?.opacity ?? fill?.alpha ?? 1;
839
+ }
840
+ }
841
+
727
842
  /* Field + Color wrapper */
728
843
  class PropskitColor extends HTMLElement {
729
844
  #field = null;
@@ -996,13 +1111,17 @@ class PropskitColor extends HTMLElement {
996
1111
 
997
1112
  #handleClick(event) {
998
1113
  if (figLabBooleanAttribute(this, "disabled")) return;
1114
+ if (event.target instanceof Element && event.target.closest("fig-menu")) {
1115
+ return;
1116
+ }
999
1117
  if (
1000
1118
  event.target instanceof Element &&
1001
- event.target.closest("fig-fill-picker, fig-swatch, fig-menu")
1119
+ event.target.closest("fig-fill-picker, fig-swatch")
1002
1120
  ) {
1003
1121
  return;
1004
1122
  }
1005
1123
  this.focus();
1124
+ this.#input?.open?.();
1006
1125
  }
1007
1126
 
1008
1127
  get value() {
@@ -1076,6 +1195,374 @@ class PropskitColor extends HTMLElement {
1076
1195
  }
1077
1196
  figLabDefineElement("propskit-color", PropskitColor);
1078
1197
 
1198
+ /* Field + Fill wrapper — color-style swatch that opens the full fill picker */
1199
+ class PropskitFill extends HTMLElement {
1200
+ #field = null;
1201
+ #label = null;
1202
+ #input = null;
1203
+ #swatch = null;
1204
+ #hasCustomLabel = false;
1205
+ #observer = null;
1206
+ #managedInputAttrs = new Set();
1207
+ #boundHandleInput = null;
1208
+ #boundHandleChange = null;
1209
+ #boundHandleClick = this.#handleClick.bind(this);
1210
+ #initialValue = null;
1211
+
1212
+ static get observedAttributes() {
1213
+ return ["label", "direction", "aria-label"];
1214
+ }
1215
+
1216
+ connectedCallback() {
1217
+ if (!this.#field) this.#initialize();
1218
+ this.#syncField();
1219
+ this.#syncInputAttributes();
1220
+ this.#bindInputEvents();
1221
+ if (this.#initialValue === null) {
1222
+ this.#initialValue =
1223
+ this.getAttribute("default") ?? this.getAttribute("value") ?? "";
1224
+ }
1225
+ this.removeEventListener("click", this.#boundHandleClick);
1226
+ this.addEventListener("click", this.#boundHandleClick);
1227
+ figLabConnectPropskitResetMenu(this);
1228
+
1229
+ if (!this.#observer) {
1230
+ this.#observer = new MutationObserver((mutations) => {
1231
+ let syncField = false;
1232
+ let syncInput = false;
1233
+
1234
+ for (const mutation of mutations) {
1235
+ if (mutation.type !== "attributes") continue;
1236
+ if (
1237
+ mutation.attributeName === "label" ||
1238
+ mutation.attributeName === "direction" ||
1239
+ mutation.attributeName === "aria-label"
1240
+ ) {
1241
+ syncField = true;
1242
+ } else {
1243
+ syncInput = true;
1244
+ }
1245
+ }
1246
+
1247
+ if (syncField) this.#syncField();
1248
+ if (syncInput) this.#syncInputAttributes();
1249
+ });
1250
+ }
1251
+
1252
+ this.#observer.observe(this, { attributes: true });
1253
+ }
1254
+
1255
+ disconnectedCallback() {
1256
+ this.#observer?.disconnect();
1257
+ this.#unbindInputEvents();
1258
+ this.removeEventListener("click", this.#boundHandleClick);
1259
+ figLabDisconnectPropskitResetMenu(this);
1260
+ }
1261
+
1262
+ attributeChangedCallback(name, oldValue, newValue) {
1263
+ if (oldValue === newValue || !this.#field) return;
1264
+ if (name === "label" || name === "direction" || name === "aria-label") {
1265
+ this.#syncField();
1266
+ }
1267
+ }
1268
+
1269
+ #initialize() {
1270
+ const initialChildren = Array.from(this.childNodes).filter(
1271
+ (node) =>
1272
+ node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim()),
1273
+ );
1274
+ const customLabel = initialChildren.find(
1275
+ (node) => node.nodeType === Node.ELEMENT_NODE && node.matches("label"),
1276
+ );
1277
+ const field = document.createElement("fig-field");
1278
+ const label = customLabel || document.createElement("label");
1279
+ const picker = document.createElement("fig-fill-picker");
1280
+ const swatch = document.createElement("fig-swatch");
1281
+ picker.append(swatch);
1282
+ swatch.setAttribute("tabindex", "0");
1283
+ for (const node of initialChildren) {
1284
+ if (node !== customLabel) picker.appendChild(node);
1285
+ }
1286
+ field.append(label, picker);
1287
+ this.#field = field;
1288
+ this.#label = label;
1289
+ this.#input = picker;
1290
+ this.#swatch = swatch;
1291
+ this.#hasCustomLabel = Boolean(customLabel);
1292
+ this.replaceChildren(field);
1293
+ }
1294
+
1295
+ #syncField() {
1296
+ if (!this.#field || !this.#label || !this.#input) return;
1297
+ const hasLabelAttr = this.hasAttribute("label");
1298
+ const rawLabel = this.getAttribute("label");
1299
+ const isBlankLabel = hasLabelAttr && (rawLabel ?? "").trim() === "";
1300
+
1301
+ if (isBlankLabel) {
1302
+ this.#label.remove();
1303
+ } else {
1304
+ if (!this.#hasCustomLabel) {
1305
+ this.#label.textContent = hasLabelAttr ? (rawLabel ?? "") : "Label";
1306
+ }
1307
+ if (this.#label.parentElement !== this.#field) {
1308
+ this.#field.prepend(this.#label);
1309
+ }
1310
+ }
1311
+
1312
+ this.#field.setAttribute(
1313
+ "direction",
1314
+ this.getAttribute("direction") || "horizontal",
1315
+ );
1316
+ this.#input.setAttribute(
1317
+ "aria-label",
1318
+ this.getAttribute("aria-label") ||
1319
+ this.#label.textContent?.trim() ||
1320
+ "Fill",
1321
+ );
1322
+ }
1323
+
1324
+ #parsedHostFill() {
1325
+ return figLabParseFillValue(this.getAttribute("value"));
1326
+ }
1327
+
1328
+ #pickerValueFromHost() {
1329
+ return figLabSerializeFillValue(this.#parsedHostFill());
1330
+ }
1331
+
1332
+ #customModeSlot() {
1333
+ const type = this.#parsedHostFill()?.type;
1334
+ if (!type) return null;
1335
+ return this.querySelector(`[slot="mode-${type}"]`);
1336
+ }
1337
+
1338
+ #livePickerFill() {
1339
+ const live = this.#input?.value;
1340
+ if (live && typeof live === "object") return live;
1341
+ if (typeof live === "string" && live) return figLabParseFillValue(live);
1342
+ return null;
1343
+ }
1344
+
1345
+ #captureFill(fill) {
1346
+ const live = this.#livePickerFill();
1347
+ if (!fill || typeof fill !== "object") return fill;
1348
+ if (fill.type !== "webcam") return fill;
1349
+ const snapshot = fill.webcam?.snapshot || live?.webcam?.snapshot;
1350
+ if (!snapshot || fill.webcam?.snapshot) return fill;
1351
+ return {
1352
+ ...fill,
1353
+ webcam: {
1354
+ ...(live?.webcam && typeof live.webcam === "object" ? live.webcam : {}),
1355
+ ...fill.webcam,
1356
+ snapshot,
1357
+ },
1358
+ };
1359
+ }
1360
+
1361
+ #syncSwatch(fill = this.#parsedHostFill()) {
1362
+ if (!this.#swatch) return;
1363
+ const next = this.#captureFill(fill);
1364
+ let background = figLabFillSwatchBackground(next, this.#customModeSlot());
1365
+ if (!background) {
1366
+ background = figLabFillSwatchBackground(
1367
+ this.#livePickerFill(),
1368
+ this.#customModeSlot(),
1369
+ );
1370
+ }
1371
+ if (!background) {
1372
+ const painted = this.#swatch.getAttribute("background") ?? "";
1373
+ if (painted.startsWith("url(")) background = painted;
1374
+ }
1375
+ this.#swatch.setAttribute("background", background);
1376
+ this.#swatch.setAttribute("alpha", String(figLabFillSwatchAlpha(next)));
1377
+ const sizing = figLabFillSwatchSizing(next || fill);
1378
+ if (sizing) {
1379
+ this.#swatch.style.setProperty("--swatch-bg-size", sizing.size);
1380
+ this.#swatch.style.setProperty("--swatch-bg-position", sizing.position);
1381
+ }
1382
+ }
1383
+
1384
+ #getForwardedInputAttrNames() {
1385
+ const reserved = new Set([
1386
+ "label",
1387
+ "direction",
1388
+ "oninput",
1389
+ "onchange",
1390
+ "class",
1391
+ "style",
1392
+ "id",
1393
+ "size",
1394
+ "aria-label",
1395
+ "text",
1396
+ "default",
1397
+ "value",
1398
+ ]);
1399
+ return this.getAttributeNames().filter(
1400
+ (name) => !reserved.has(name) && !name.startsWith("data-"),
1401
+ );
1402
+ }
1403
+
1404
+ #syncInputAttributes() {
1405
+ if (!this.#input) return;
1406
+ const inputAttrs = this.#getForwardedInputAttrNames();
1407
+ const nextManaged = new Set(inputAttrs);
1408
+
1409
+ for (const attrName of this.#managedInputAttrs) {
1410
+ if (!nextManaged.has(attrName)) this.#input.removeAttribute(attrName);
1411
+ }
1412
+ for (const attrName of inputAttrs) {
1413
+ this.#input.setAttribute(attrName, this.getAttribute(attrName) ?? "");
1414
+ }
1415
+
1416
+ const nextJson = this.#pickerValueFromHost();
1417
+ if (this.#input.getAttribute("value") !== nextJson) {
1418
+ this.#input.setAttribute("value", nextJson);
1419
+ }
1420
+ this.#syncSwatch();
1421
+ this.#managedInputAttrs = nextManaged;
1422
+ }
1423
+
1424
+ #bindInputEvents() {
1425
+ if (!this.#input) return;
1426
+ this.#boundHandleInput ??= this.#forwardInputEvent.bind(this, "input");
1427
+ this.#boundHandleChange ??= this.#forwardInputEvent.bind(this, "change");
1428
+ this.#input.addEventListener("input", this.#boundHandleInput);
1429
+ this.#input.addEventListener("change", this.#boundHandleChange);
1430
+ }
1431
+
1432
+ #unbindInputEvents() {
1433
+ if (!this.#input) return;
1434
+ if (this.#boundHandleInput) {
1435
+ this.#input.removeEventListener("input", this.#boundHandleInput);
1436
+ }
1437
+ if (this.#boundHandleChange) {
1438
+ this.#input.removeEventListener("change", this.#boundHandleChange);
1439
+ }
1440
+ }
1441
+
1442
+ #fillFromEvent(event) {
1443
+ const detail =
1444
+ event instanceof CustomEvent && event.detail !== undefined
1445
+ ? event.detail
1446
+ : undefined;
1447
+ if (detail && typeof detail === "object") return detail;
1448
+ if (typeof detail === "string" && detail) {
1449
+ return figLabParseFillValue(detail);
1450
+ }
1451
+ const live = this.#input?.value;
1452
+ if (live && typeof live === "object") return live;
1453
+ if (typeof live === "string" && live) return figLabParseFillValue(live);
1454
+ return this.#parsedHostFill();
1455
+ }
1456
+
1457
+ #forwardInputEvent(type, event) {
1458
+ event.stopImmediatePropagation();
1459
+ if (figLabBooleanAttribute(this, "disabled")) return;
1460
+ const fill = this.#captureFill(this.#fillFromEvent(event));
1461
+ const serialized = figLabSerializeFillValue(fill);
1462
+ this.setAttribute("value", serialized);
1463
+ if (this.#input && this.#input.getAttribute("value") !== serialized) {
1464
+ this.#input.setAttribute("value", serialized);
1465
+ }
1466
+ this.#syncSwatch(fill);
1467
+ this.dispatchEvent(
1468
+ new CustomEvent(type, {
1469
+ detail: fill,
1470
+ bubbles: true,
1471
+ cancelable: true,
1472
+ composed: true,
1473
+ }),
1474
+ );
1475
+ }
1476
+
1477
+ #handleClick(event) {
1478
+ if (figLabBooleanAttribute(this, "disabled")) return;
1479
+ if (event.target instanceof Element && event.target.closest("fig-menu")) {
1480
+ return;
1481
+ }
1482
+ if (
1483
+ event.target instanceof Element &&
1484
+ event.target.closest("fig-fill-picker, fig-swatch")
1485
+ ) {
1486
+ return;
1487
+ }
1488
+ this.focus();
1489
+ this.#input?.open?.();
1490
+ }
1491
+
1492
+ get value() {
1493
+ return (
1494
+ this.getAttribute("value") ??
1495
+ figLabSerializeFillValue(this.#input?.value) ??
1496
+ ""
1497
+ );
1498
+ }
1499
+
1500
+ set value(nextValue) {
1501
+ if (nextValue === null || nextValue === undefined || nextValue === "") {
1502
+ this.removeAttribute("value");
1503
+ this.#input?.removeAttribute("value");
1504
+ this.#syncSwatch({ type: "solid", color: "", alpha: 1 });
1505
+ return;
1506
+ }
1507
+ const serialized = figLabSerializeFillValue(
1508
+ typeof nextValue === "string" ? figLabParseFillValue(nextValue) : nextValue,
1509
+ );
1510
+ this.setAttribute("value", serialized);
1511
+ this.#forceInputValue(serialized);
1512
+ }
1513
+
1514
+ #forceInputValue(next) {
1515
+ if (!this.#input) return;
1516
+ const json = figLabSerializeFillValue(figLabParseFillValue(next));
1517
+ if (this.#input.getAttribute("value") === json) {
1518
+ this.#input.removeAttribute("value");
1519
+ }
1520
+ this.#input.setAttribute("value", json);
1521
+ this.#syncSwatch(figLabParseFillValue(json));
1522
+ }
1523
+
1524
+ #defaultValue() {
1525
+ return this.getAttribute("default") ?? this.#initialValue ?? "";
1526
+ }
1527
+
1528
+ get defaultValue() {
1529
+ return String(this.#defaultValue());
1530
+ }
1531
+
1532
+ get isDefault() {
1533
+ return figLabPropskitJsonValuesEqual(this.value, this.defaultValue);
1534
+ }
1535
+
1536
+ resetToDefault() {
1537
+ const next = this.defaultValue;
1538
+ this.setAttribute("value", next);
1539
+ this.#forceInputValue(next);
1540
+ const fill = figLabParseFillValue(next);
1541
+ this.dispatchEvent(
1542
+ new CustomEvent("input", {
1543
+ detail: fill,
1544
+ bubbles: true,
1545
+ cancelable: true,
1546
+ composed: true,
1547
+ }),
1548
+ );
1549
+ this.dispatchEvent(
1550
+ new CustomEvent("change", {
1551
+ detail: fill,
1552
+ bubbles: true,
1553
+ cancelable: true,
1554
+ composed: true,
1555
+ }),
1556
+ );
1557
+ }
1558
+
1559
+ focus(options) {
1560
+ const swatch = this.querySelector("fig-swatch");
1561
+ if (swatch instanceof HTMLElement) swatch.focus(options);
1562
+ }
1563
+ }
1564
+ figLabDefineElement("propskit-fill", PropskitFill);
1565
+
1079
1566
  /* Field + Gradient wrapper */
1080
1567
  class PropskitGradient extends HTMLElement {
1081
1568
  #field = null;
@@ -1286,13 +1773,17 @@ class PropskitGradient extends HTMLElement {
1286
1773
 
1287
1774
  #handleClick(event) {
1288
1775
  if (figLabBooleanAttribute(this, "disabled")) return;
1776
+ if (event.target instanceof Element && event.target.closest("fig-menu")) {
1777
+ return;
1778
+ }
1289
1779
  if (
1290
1780
  event.target instanceof Element &&
1291
- event.target.closest("fig-input-gradient, fig-menu")
1781
+ event.target.closest("fig-input-gradient, fig-fill-picker, fig-swatch")
1292
1782
  ) {
1293
1783
  return;
1294
1784
  }
1295
1785
  this.focus();
1786
+ this.#input?.querySelector?.("fig-fill-picker")?.open?.();
1296
1787
  }
1297
1788
 
1298
1789
  get value() {
@@ -3718,6 +4209,7 @@ class PropskitGroup extends HTMLElement {
3718
4209
 
3719
4210
  static #CONTROL_SELECTOR = [
3720
4211
  "propskit-color",
4212
+ "propskit-fill",
3721
4213
  "propskit-gradient",
3722
4214
  "propskit-number",
3723
4215
  "propskit-position",
package/fig.js CHANGED
@@ -9055,8 +9055,11 @@ class FigInputFill extends HTMLElement {
9055
9055
  break;
9056
9056
  case "webcam":
9057
9057
  if (parsed.webcam && typeof parsed.webcam === "object") {
9058
- const { stream: _ignored, ...rest } = parsed.webcam;
9058
+ const { stream: _ignored, snapshot, ...rest } = parsed.webcam;
9059
9059
  this.#webcam = { ...this.#webcam, ...rest };
9060
+ if (typeof snapshot === "string" && snapshot) {
9061
+ this.#webcam.snapshot = snapshot;
9062
+ }
9060
9063
  } else if (parsed.image?.url) {
9061
9064
  this.#webcam.snapshot = parsed.image.url;
9062
9065
  if (parsed.image.scaleMode)
@@ -9145,10 +9148,12 @@ class FigInputFill extends HTMLElement {
9145
9148
  return this.#image.url ? figCssUrl(this.#image.url) : "#D9D9D9";
9146
9149
  case "video":
9147
9150
  return this.#video.poster ? figCssUrl(this.#video.poster) : "#D9D9D9";
9148
- case "webcam":
9149
- return this.#webcam.snapshot
9150
- ? figCssUrl(this.#webcam.snapshot)
9151
- : "";
9151
+ case "webcam": {
9152
+ const snapshot =
9153
+ this.#webcam.snapshot ||
9154
+ this.#fillPicker?.value?.webcam?.snapshot;
9155
+ return snapshot ? figCssUrl(snapshot) : "";
9156
+ }
9152
9157
  default:
9153
9158
  return this.#customSwatchBackground();
9154
9159
  }
@@ -9419,11 +9424,18 @@ class FigInputFill extends HTMLElement {
9419
9424
  break;
9420
9425
  case "webcam":
9421
9426
  if (detail.webcam) {
9422
- const { stream: _ignored, ...rest } = detail.webcam;
9427
+ const { stream: _ignored, snapshot, ...rest } = detail.webcam;
9423
9428
  this.#webcam = { ...this.#webcam, ...rest };
9429
+ if (typeof snapshot === "string" && snapshot) {
9430
+ this.#webcam.snapshot = snapshot;
9431
+ }
9424
9432
  } else if (detail.image?.url) {
9425
9433
  this.#webcam.snapshot = detail.image.url;
9426
9434
  }
9435
+ if (!this.#webcam.snapshot) {
9436
+ const fromPicker = this.#fillPicker?.value?.webcam?.snapshot;
9437
+ if (fromPicker) this.#webcam.snapshot = fromPicker;
9438
+ }
9427
9439
  break;
9428
9440
  default: {
9429
9441
  const { type: _type, colorSpace: _colorSpace, ...rest } = detail;
@@ -9686,7 +9698,15 @@ class FigInputFill extends HTMLElement {
9686
9698
  #syncSwatch() {
9687
9699
  const swatch = this.#fillPicker?.querySelector("fig-swatch");
9688
9700
  if (!swatch) return;
9689
- swatch.setAttribute("background", this.#fillPickerSwatchBackground());
9701
+ const background = this.#fillPickerSwatchBackground();
9702
+ if (this.#fillType === "webcam" && !background) {
9703
+ const current = swatch.getAttribute("background") || "";
9704
+ if (current.startsWith("url(")) {
9705
+ swatch.setAttribute("alpha", this.#fillPickerSwatchAlpha());
9706
+ return;
9707
+ }
9708
+ }
9709
+ swatch.setAttribute("background", background);
9690
9710
  swatch.setAttribute("alpha", this.#fillPickerSwatchAlpha());
9691
9711
  }
9692
9712
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogieking/figui3",
3
- "version": "8.9.19",
3
+ "version": "8.9.20",
4
4
  "description": "A lightweight web components library for building Figma plugin and widget UIs with native look and feel",
5
5
  "author": "Rogie King",
6
6
  "license": "MIT",