@biela.dev/devices 1.6.3 → 1.6.5

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.d.cts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { D as DeviceMeta, a as DeviceLayoutData, b as DeviceLayoutContract, S as SafeAreaInsets, C as ContentRect, H as HardwareOverlayType, O as OverlayRect, R as RegisteredDevice } from './contract-types-Cw1rmF3b.cjs';
2
2
  export { c as DeviceCSSVariables, d as DeviceFrameInfo, e as SVGCropRect, f as SVGScreenRect } from './contract-types-Cw1rmF3b.cjs';
3
- export { IPHONE_16E_FRAME, IPHONE_16E_LAYOUT, IPHONE_16E_META, IPHONE_16_FRAME, IPHONE_16_LAYOUT, IPHONE_16_META, IPHONE_16_SCREEN_RECT, IPHONE_17_PRO_FRAME, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_FRAME, IPHONE_17_PRO_MAX_LAYOUT, IPHONE_17_PRO_MAX_META, IPHONE_17_PRO_MAX_SCREEN_RECT, IPHONE_17_PRO_META, IPHONE_17_PRO_SCREEN_RECT, IPHONE_AIR_FRAME, IPHONE_AIR_LAYOUT, IPHONE_AIR_META, IPHONE_SE_3_FRAME, IPHONE_SE_3_LAYOUT, IPHONE_SE_3_META, IPhone16SVG, IPhone16eSVG, IPhone17ProMaxSVG, IPhone17ProSVG, IPhoneAirSVG, IPhoneSE3SVG } from './ios/index.cjs';
4
- export { GALAXY_S25_EDGE_FRAME, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_EDGE_META, GALAXY_S25_FRAME, GALAXY_S25_LAYOUT, GALAXY_S25_META, GALAXY_S25_ULTRA_FRAME, GALAXY_S25_ULTRA_LAYOUT, GALAXY_S25_ULTRA_META, GalaxyS25EdgeSVG, GalaxyS25SVG, GalaxyS25UltraSVG, PIXEL_9_PRO_FRAME, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_META, PIXEL_9_PRO_XL_FRAME, PIXEL_9_PRO_XL_LAYOUT, PIXEL_9_PRO_XL_META, Pixel9ProSVG, Pixel9ProXLSVG } from './android/index.cjs';
3
+ export { IPHONE_16E_FRAME, IPHONE_16E_LAYOUT, IPHONE_16E_META, IPHONE_16_FRAME, IPHONE_16_LAYOUT, IPHONE_16_META, IPHONE_16_SCREEN_RECT, IPHONE_17_PRO_FRAME, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_FRAME, IPHONE_17_PRO_MAX_LAYOUT, IPHONE_17_PRO_MAX_META, IPHONE_17_PRO_MAX_SCREEN_RECT, IPHONE_17_PRO_META, IPHONE_17_PRO_SCREEN_RECT, IPHONE_AIR_FRAME, IPHONE_AIR_LAYOUT, IPHONE_AIR_META, IPHONE_AIR_SCREEN_RECT, IPHONE_SE_3_FRAME, IPHONE_SE_3_LAYOUT, IPHONE_SE_3_META, IPhone16SVG, IPhone16eSVG, IPhone17ProMaxSVG, IPhone17ProSVG, IPhoneAirSVG, IPhoneSE3SVG } from './ios/index.cjs';
4
+ export { GALAXY_S25_EDGE_FRAME, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_EDGE_META, GALAXY_S25_FRAME, GALAXY_S25_LAYOUT, GALAXY_S25_META, GALAXY_S25_SCREEN_RECT, GALAXY_S25_ULTRA_FRAME, GALAXY_S25_ULTRA_LAYOUT, GALAXY_S25_ULTRA_META, GALAXY_S25_ULTRA_SCREEN_RECT, GALAXY_Z_FOLD_7_FRAME, GALAXY_Z_FOLD_7_LAYOUT, GALAXY_Z_FOLD_7_META, GALAXY_Z_FOLD_7_OPEN_FRAME, GALAXY_Z_FOLD_7_OPEN_LAYOUT, GALAXY_Z_FOLD_7_OPEN_META, GALAXY_Z_FOLD_7_OPEN_SCREEN_RECT, GALAXY_Z_FOLD_7_SCREEN_RECT, GalaxyS25EdgeSVG, GalaxyS25SVG, GalaxyS25UltraSVG, GalaxyZFold7OpenSVG, GalaxyZFold7SVG, PIXEL_9_PRO_FRAME, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_META, PIXEL_9_PRO_XL_FRAME, PIXEL_9_PRO_XL_LAYOUT, PIXEL_9_PRO_XL_META, Pixel9ProSVG, Pixel9ProXLSVG } from './android/index.cjs';
5
5
  import 'react/jsx-runtime';
6
6
 
7
7
  /**
8
- * Built-in device metadata registry — 11 devices.
8
+ * Built-in device metadata registry — 13 devices.
9
9
  *
10
10
  * Curated to cover every unique screen class (logical resolution).
11
11
  * Duplicate resolutions across generations are represented by the latest model.
@@ -21,6 +21,8 @@ import 'react/jsx-runtime';
21
21
  * Galaxy S25 Ultra: 384×824dp @3.75x (covers S24 Ultra)
22
22
  * Galaxy S25: 360×780dp @3x (covers S24)
23
23
  * Galaxy S25 Edge: 382×824dp @3.75x (unique narrow form factor)
24
+ * Galaxy Z Fold 7: 374×870dp @2.89x (foldable cover display)
25
+ * Galaxy Z Fold 7 Open: 771×855dp @2.86x (foldable inner display)
24
26
  * Pixel 9 Pro: 393×851dp @3x (covers Pixel 9, S25+)
25
27
  * Pixel 9 Pro XL: 448×968dp @3x (largest Android)
26
28
  */
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { D as DeviceMeta, a as DeviceLayoutData, b as DeviceLayoutContract, S as SafeAreaInsets, C as ContentRect, H as HardwareOverlayType, O as OverlayRect, R as RegisteredDevice } from './contract-types-Cw1rmF3b.js';
2
2
  export { c as DeviceCSSVariables, d as DeviceFrameInfo, e as SVGCropRect, f as SVGScreenRect } from './contract-types-Cw1rmF3b.js';
3
- export { IPHONE_16E_FRAME, IPHONE_16E_LAYOUT, IPHONE_16E_META, IPHONE_16_FRAME, IPHONE_16_LAYOUT, IPHONE_16_META, IPHONE_16_SCREEN_RECT, IPHONE_17_PRO_FRAME, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_FRAME, IPHONE_17_PRO_MAX_LAYOUT, IPHONE_17_PRO_MAX_META, IPHONE_17_PRO_MAX_SCREEN_RECT, IPHONE_17_PRO_META, IPHONE_17_PRO_SCREEN_RECT, IPHONE_AIR_FRAME, IPHONE_AIR_LAYOUT, IPHONE_AIR_META, IPHONE_SE_3_FRAME, IPHONE_SE_3_LAYOUT, IPHONE_SE_3_META, IPhone16SVG, IPhone16eSVG, IPhone17ProMaxSVG, IPhone17ProSVG, IPhoneAirSVG, IPhoneSE3SVG } from './ios/index.js';
4
- export { GALAXY_S25_EDGE_FRAME, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_EDGE_META, GALAXY_S25_FRAME, GALAXY_S25_LAYOUT, GALAXY_S25_META, GALAXY_S25_ULTRA_FRAME, GALAXY_S25_ULTRA_LAYOUT, GALAXY_S25_ULTRA_META, GalaxyS25EdgeSVG, GalaxyS25SVG, GalaxyS25UltraSVG, PIXEL_9_PRO_FRAME, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_META, PIXEL_9_PRO_XL_FRAME, PIXEL_9_PRO_XL_LAYOUT, PIXEL_9_PRO_XL_META, Pixel9ProSVG, Pixel9ProXLSVG } from './android/index.js';
3
+ export { IPHONE_16E_FRAME, IPHONE_16E_LAYOUT, IPHONE_16E_META, IPHONE_16_FRAME, IPHONE_16_LAYOUT, IPHONE_16_META, IPHONE_16_SCREEN_RECT, IPHONE_17_PRO_FRAME, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_FRAME, IPHONE_17_PRO_MAX_LAYOUT, IPHONE_17_PRO_MAX_META, IPHONE_17_PRO_MAX_SCREEN_RECT, IPHONE_17_PRO_META, IPHONE_17_PRO_SCREEN_RECT, IPHONE_AIR_FRAME, IPHONE_AIR_LAYOUT, IPHONE_AIR_META, IPHONE_AIR_SCREEN_RECT, IPHONE_SE_3_FRAME, IPHONE_SE_3_LAYOUT, IPHONE_SE_3_META, IPhone16SVG, IPhone16eSVG, IPhone17ProMaxSVG, IPhone17ProSVG, IPhoneAirSVG, IPhoneSE3SVG } from './ios/index.js';
4
+ export { GALAXY_S25_EDGE_FRAME, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_EDGE_META, GALAXY_S25_FRAME, GALAXY_S25_LAYOUT, GALAXY_S25_META, GALAXY_S25_SCREEN_RECT, GALAXY_S25_ULTRA_FRAME, GALAXY_S25_ULTRA_LAYOUT, GALAXY_S25_ULTRA_META, GALAXY_S25_ULTRA_SCREEN_RECT, GALAXY_Z_FOLD_7_FRAME, GALAXY_Z_FOLD_7_LAYOUT, GALAXY_Z_FOLD_7_META, GALAXY_Z_FOLD_7_OPEN_FRAME, GALAXY_Z_FOLD_7_OPEN_LAYOUT, GALAXY_Z_FOLD_7_OPEN_META, GALAXY_Z_FOLD_7_OPEN_SCREEN_RECT, GALAXY_Z_FOLD_7_SCREEN_RECT, GalaxyS25EdgeSVG, GalaxyS25SVG, GalaxyS25UltraSVG, GalaxyZFold7OpenSVG, GalaxyZFold7SVG, PIXEL_9_PRO_FRAME, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_META, PIXEL_9_PRO_XL_FRAME, PIXEL_9_PRO_XL_LAYOUT, PIXEL_9_PRO_XL_META, Pixel9ProSVG, Pixel9ProXLSVG } from './android/index.js';
5
5
  import 'react/jsx-runtime';
6
6
 
7
7
  /**
8
- * Built-in device metadata registry — 11 devices.
8
+ * Built-in device metadata registry — 13 devices.
9
9
  *
10
10
  * Curated to cover every unique screen class (logical resolution).
11
11
  * Duplicate resolutions across generations are represented by the latest model.
@@ -21,6 +21,8 @@ import 'react/jsx-runtime';
21
21
  * Galaxy S25 Ultra: 384×824dp @3.75x (covers S24 Ultra)
22
22
  * Galaxy S25: 360×780dp @3x (covers S24)
23
23
  * Galaxy S25 Edge: 382×824dp @3.75x (unique narrow form factor)
24
+ * Galaxy Z Fold 7: 374×870dp @2.89x (foldable cover display)
25
+ * Galaxy Z Fold 7 Open: 771×855dp @2.86x (foldable inner display)
24
26
  * Pixel 9 Pro: 393×851dp @3x (covers Pixel 9, S25+)
25
27
  * Pixel 9 Pro XL: 448×968dp @3x (largest Android)
26
28
  */
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { PIXEL_9_PRO_META, PIXEL_9_PRO_XL_META, GALAXY_S25_EDGE_META, GALAXY_S25_META, GALAXY_S25_ULTRA_META, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_XL_LAYOUT, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_LAYOUT, GALAXY_S25_ULTRA_LAYOUT } from './chunk-76PRN3YN.js';
2
- export { GALAXY_S25_EDGE_FRAME, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_EDGE_META, GALAXY_S25_FRAME, GALAXY_S25_LAYOUT, GALAXY_S25_META, GALAXY_S25_ULTRA_FRAME, GALAXY_S25_ULTRA_LAYOUT, GALAXY_S25_ULTRA_META, GalaxyS25EdgeSVG, GalaxyS25SVG, GalaxyS25UltraSVG, PIXEL_9_PRO_FRAME, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_META, PIXEL_9_PRO_XL_FRAME, PIXEL_9_PRO_XL_LAYOUT, PIXEL_9_PRO_XL_META, Pixel9ProSVG, Pixel9ProXLSVG } from './chunk-76PRN3YN.js';
3
- import { IPHONE_SE_3_META, IPHONE_16E_META, IPHONE_16_META, IPHONE_AIR_META, IPHONE_17_PRO_META, IPHONE_17_PRO_MAX_META, IPHONE_SE_3_LAYOUT, IPHONE_16E_LAYOUT, IPHONE_16_LAYOUT, IPHONE_AIR_LAYOUT, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_LAYOUT } from './chunk-DLT4UL35.js';
4
- export { IPHONE_16E_FRAME, IPHONE_16E_LAYOUT, IPHONE_16E_META, IPHONE_16_FRAME, IPHONE_16_LAYOUT, IPHONE_16_META, IPHONE_16_SCREEN_RECT, IPHONE_17_PRO_FRAME, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_FRAME, IPHONE_17_PRO_MAX_LAYOUT, IPHONE_17_PRO_MAX_META, IPHONE_17_PRO_MAX_SCREEN_RECT, IPHONE_17_PRO_META, IPHONE_17_PRO_SCREEN_RECT, IPHONE_AIR_FRAME, IPHONE_AIR_LAYOUT, IPHONE_AIR_META, IPHONE_SE_3_FRAME, IPHONE_SE_3_LAYOUT, IPHONE_SE_3_META, IPhone16SVG, IPhone16eSVG, IPhone17ProMaxSVG, IPhone17ProSVG, IPhoneAirSVG, IPhoneSE3SVG } from './chunk-DLT4UL35.js';
1
+ import { PIXEL_9_PRO_META, PIXEL_9_PRO_XL_META, GALAXY_Z_FOLD_7_OPEN_META, GALAXY_Z_FOLD_7_META, GALAXY_S25_EDGE_META, GALAXY_S25_META, GALAXY_S25_ULTRA_META, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_XL_LAYOUT, GALAXY_Z_FOLD_7_OPEN_LAYOUT, GALAXY_Z_FOLD_7_LAYOUT, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_LAYOUT, GALAXY_S25_ULTRA_LAYOUT } from './chunk-LNEWMNRU.js';
2
+ export { GALAXY_S25_EDGE_FRAME, GALAXY_S25_EDGE_LAYOUT, GALAXY_S25_EDGE_META, GALAXY_S25_FRAME, GALAXY_S25_LAYOUT, GALAXY_S25_META, GALAXY_S25_SCREEN_RECT, GALAXY_S25_ULTRA_FRAME, GALAXY_S25_ULTRA_LAYOUT, GALAXY_S25_ULTRA_META, GALAXY_S25_ULTRA_SCREEN_RECT, GALAXY_Z_FOLD_7_FRAME, GALAXY_Z_FOLD_7_LAYOUT, GALAXY_Z_FOLD_7_META, GALAXY_Z_FOLD_7_OPEN_FRAME, GALAXY_Z_FOLD_7_OPEN_LAYOUT, GALAXY_Z_FOLD_7_OPEN_META, GALAXY_Z_FOLD_7_OPEN_SCREEN_RECT, GALAXY_Z_FOLD_7_SCREEN_RECT, GalaxyS25EdgeSVG, GalaxyS25SVG, GalaxyS25UltraSVG, GalaxyZFold7OpenSVG, GalaxyZFold7SVG, PIXEL_9_PRO_FRAME, PIXEL_9_PRO_LAYOUT, PIXEL_9_PRO_META, PIXEL_9_PRO_XL_FRAME, PIXEL_9_PRO_XL_LAYOUT, PIXEL_9_PRO_XL_META, Pixel9ProSVG, Pixel9ProXLSVG } from './chunk-LNEWMNRU.js';
3
+ import { IPHONE_SE_3_META, IPHONE_16E_META, IPHONE_16_META, IPHONE_AIR_META, IPHONE_17_PRO_META, IPHONE_17_PRO_MAX_META, IPHONE_SE_3_LAYOUT, IPHONE_16E_LAYOUT, IPHONE_16_LAYOUT, IPHONE_AIR_LAYOUT, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_LAYOUT } from './chunk-DF2YDUHR.js';
4
+ export { IPHONE_16E_FRAME, IPHONE_16E_LAYOUT, IPHONE_16E_META, IPHONE_16_FRAME, IPHONE_16_LAYOUT, IPHONE_16_META, IPHONE_16_SCREEN_RECT, IPHONE_17_PRO_FRAME, IPHONE_17_PRO_LAYOUT, IPHONE_17_PRO_MAX_FRAME, IPHONE_17_PRO_MAX_LAYOUT, IPHONE_17_PRO_MAX_META, IPHONE_17_PRO_MAX_SCREEN_RECT, IPHONE_17_PRO_META, IPHONE_17_PRO_SCREEN_RECT, IPHONE_AIR_FRAME, IPHONE_AIR_LAYOUT, IPHONE_AIR_META, IPHONE_AIR_SCREEN_RECT, IPHONE_SE_3_FRAME, IPHONE_SE_3_LAYOUT, IPHONE_SE_3_META, IPhone16SVG, IPhone16eSVG, IPhone17ProMaxSVG, IPhone17ProSVG, IPhoneAirSVG, IPhoneSE3SVG } from './chunk-DF2YDUHR.js';
5
5
 
6
6
  // src/custom/device-registry.ts
7
7
  var STORAGE_KEY = "bielaframe-custom-devices";
@@ -145,6 +145,8 @@ var BUILTIN_DEVICES = {
145
145
  "galaxy-s25-ultra": GALAXY_S25_ULTRA_META,
146
146
  "galaxy-s25": GALAXY_S25_META,
147
147
  "galaxy-s25-edge": GALAXY_S25_EDGE_META,
148
+ "galaxy-z-fold-7": GALAXY_Z_FOLD_7_META,
149
+ "galaxy-z-fold-7-open": GALAXY_Z_FOLD_7_OPEN_META,
148
150
  "pixel-9-pro-xl": PIXEL_9_PRO_XL_META,
149
151
  "pixel-9-pro": PIXEL_9_PRO_META
150
152
  };
@@ -160,6 +162,8 @@ var DEVICE_LAYOUTS = {
160
162
  "galaxy-s25-ultra": GALAXY_S25_ULTRA_LAYOUT,
161
163
  "galaxy-s25": GALAXY_S25_LAYOUT,
162
164
  "galaxy-s25-edge": GALAXY_S25_EDGE_LAYOUT,
165
+ "galaxy-z-fold-7": GALAXY_Z_FOLD_7_LAYOUT,
166
+ "galaxy-z-fold-7-open": GALAXY_Z_FOLD_7_OPEN_LAYOUT,
163
167
  "pixel-9-pro-xl": PIXEL_9_PRO_XL_LAYOUT,
164
168
  "pixel-9-pro": PIXEL_9_PRO_LAYOUT
165
169
  };
@@ -713,6 +717,12 @@ function analyzeSVGGeometry(svgString) {
713
717
  id: null
714
718
  };
715
719
  }
720
+ if (screenRect) {
721
+ const maskScreen = findMaskBasedScreen(svgString, screenRect);
722
+ if (maskScreen) {
723
+ screenRect = maskScreen;
724
+ }
725
+ }
716
726
  const islandRect = findDynamicIsland(rects, screenRect);
717
727
  const sideButtons = findSideButtons(rects, phoneBody);
718
728
  const zones = [];
@@ -782,10 +792,10 @@ function findScreenArea(rects, phoneBody, svgW, svgH) {
782
792
  const area = r.width * r.height;
783
793
  if (area < bodyArea * 0.5) return false;
784
794
  if (area >= bodyArea * 0.98) return false;
785
- if (r.rx < 10) return false;
795
+ if (!phoneBody && r.rx < 10) return false;
786
796
  if (!r.fill || r.fill === "none") return false;
787
797
  if (r.fill.startsWith("url(")) return false;
788
- if (isDarkFill(r.fill)) return false;
798
+ if (isPureBlackFill(r.fill)) return false;
789
799
  return true;
790
800
  }).sort((a, b) => b.width * b.height - a.width * a.height);
791
801
  return fallbackCandidates[0] ?? null;
@@ -894,6 +904,55 @@ function classifySideButtons(sideButtons, phoneBody) {
894
904
  }
895
905
  return classified;
896
906
  }
907
+ function findMaskBasedScreen(svgString, detectedScreen, _svgW, _svgH) {
908
+ const maskRegex = /<mask\b[^>]*>([\s\S]*?)<\/mask>/gi;
909
+ let maskMatch;
910
+ const candidates = [];
911
+ while ((maskMatch = maskRegex.exec(svgString)) !== null) {
912
+ const maskBody = maskMatch[1];
913
+ const pathRegex = /<path\b([^>]*?)\/>/gi;
914
+ let pathMatch;
915
+ while ((pathMatch = pathRegex.exec(maskBody)) !== null) {
916
+ const attrs = pathMatch[1];
917
+ const d = getAttrValue(attrs, "d");
918
+ if (!d) continue;
919
+ const bb = subpathBBox(d);
920
+ if (!bb || bb.w < 10 || bb.h < 10) continue;
921
+ if (bb.h < bb.w * 1.3) continue;
922
+ const overlapX = Math.max(0, Math.min(bb.x + bb.w, detectedScreen.x + detectedScreen.width) - Math.max(bb.x, detectedScreen.x));
923
+ const overlapY = Math.max(0, Math.min(bb.y + bb.h, detectedScreen.y + detectedScreen.height) - Math.max(bb.y, detectedScreen.y));
924
+ const overlapArea = overlapX * overlapY;
925
+ const detectedArea = detectedScreen.width * detectedScreen.height;
926
+ if (overlapArea < detectedArea * 0.8) continue;
927
+ const mMatch = d.match(/M\s*(-?[\d.]+)[\s,]+(-?[\d.]+)/i);
928
+ let rx = 0;
929
+ if (mMatch) {
930
+ const mx = parseFloat(mMatch[1]);
931
+ const my = parseFloat(mMatch[2]);
932
+ const rxFromY = Math.abs(my - bb.y);
933
+ const rxFromX = Math.abs(mx - bb.x);
934
+ rx = Math.max(rxFromY, rxFromX);
935
+ if (rx < 5) rx = 0;
936
+ }
937
+ candidates.push({
938
+ x: bb.x,
939
+ y: bb.y,
940
+ width: bb.w,
941
+ height: bb.h,
942
+ rx,
943
+ fill: "mask-path",
944
+ id: null
945
+ });
946
+ }
947
+ }
948
+ if (candidates.length === 0) return null;
949
+ candidates.sort((a, b) => {
950
+ const aDiff = Math.abs(a.width * a.height - detectedScreen.width * detectedScreen.height);
951
+ const bDiff = Math.abs(b.width * b.height - detectedScreen.width * detectedScreen.height);
952
+ return aDiff - bDiff;
953
+ });
954
+ return candidates[0];
955
+ }
897
956
  function findPathCutoutScreen(svgString, svgW, svgH) {
898
957
  const svgArea = svgW * svgH;
899
958
  const pathRegex = /<path\b([^>]*?)\/>/gi;
@@ -1057,6 +1116,20 @@ function isLightFill(fill) {
1057
1116
  }
1058
1117
  return false;
1059
1118
  }
1119
+ function isPureBlackFill(fill) {
1120
+ if (!fill) return false;
1121
+ const f = fill.toLowerCase().trim();
1122
+ if (f === "black" || f === "#000" || f === "#000000") return true;
1123
+ if (f === "rgb(0,0,0)" || f === "rgb(0, 0, 0)") return true;
1124
+ const hex6 = f.match(/^#([0-9a-f]{6})$/);
1125
+ if (hex6) {
1126
+ const r = parseInt(hex6[1].slice(0, 2), 16);
1127
+ const g = parseInt(hex6[1].slice(2, 4), 16);
1128
+ const b = parseInt(hex6[1].slice(4, 6), 16);
1129
+ return r < 10 && g < 10 && b < 10;
1130
+ }
1131
+ return false;
1132
+ }
1060
1133
  function isDarkFill(fill) {
1061
1134
  if (!fill) return false;
1062
1135
  const f = fill.toLowerCase().trim();
@@ -1141,6 +1214,14 @@ function countOccurrences(str, substr) {
1141
1214
  function applyTransformToRect(attrs, x, y, width, height) {
1142
1215
  const transformAttr = getAttrValue(attrs, "transform");
1143
1216
  if (!transformAttr) return { x, y, width, height };
1217
+ const translateMatch = transformAttr.match(
1218
+ /translate\(\s*(-?[\d.]+)[\s,]+(-?[\d.]+)\s*\)/
1219
+ );
1220
+ if (translateMatch) {
1221
+ const tx = parseFloat(translateMatch[1]);
1222
+ const ty = parseFloat(translateMatch[2]);
1223
+ return { x: x + tx, y: y + ty, width, height };
1224
+ }
1144
1225
  const matrixMatch = transformAttr.match(
1145
1226
  /matrix\(\s*(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s*\)/
1146
1227
  );