@mmtitanl/tablets-core 0.2.0 → 0.3.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.
package/dist/index.cjs CHANGED
@@ -329,7 +329,40 @@ function registerDeviceSVG(deviceId, component, frame, screenRect, landscape) {
329
329
  function getDeviceSVG(deviceId) {
330
330
  return SVG_REGISTRY.get(deviceId);
331
331
  }
332
- function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suffix) {
332
+ function readSVGViewBox(svg) {
333
+ const vb = svg.match(/viewBox\s*=\s*["']([^"']+)["']/i);
334
+ if (vb) {
335
+ const parts = vb[1].split(/[\s,]+/).map(Number);
336
+ if (parts.length === 4 && parts.every((n) => Number.isFinite(n))) {
337
+ return { x: parts[0], y: parts[1], w: parts[2], h: parts[3] };
338
+ }
339
+ }
340
+ const wm = svg.match(/<svg\b[^>]*\swidth\s*=\s*["']([\d.]+)/i);
341
+ const hm = svg.match(/<svg\b[^>]*\sheight\s*=\s*["']([\d.]+)/i);
342
+ if (wm && hm) return { x: 0, y: 0, w: parseFloat(wm[1]), h: parseFloat(hm[1]) };
343
+ return null;
344
+ }
345
+ function injectScreenMask(svg, frame, suffix) {
346
+ if (frame.totalWidth <= 0 || frame.totalHeight <= 0) return svg;
347
+ if (frame.screenWidth <= 0 || frame.screenHeight <= 0) return svg;
348
+ const vb = readSVGViewBox(svg);
349
+ if (!vb) return svg;
350
+ const sx = vb.w / frame.totalWidth;
351
+ const sy = vb.h / frame.totalHeight;
352
+ const x = vb.x + frame.bezelLeft * sx;
353
+ const y = vb.y + frame.bezelTop * sy;
354
+ const w = frame.screenWidth * sx;
355
+ const h = frame.screenHeight * sy;
356
+ const rt = Math.max(0, frame.screenRadiusTop ?? frame.screenRadius) * Math.min(sx, sy);
357
+ const rb = Math.max(0, frame.screenRadiusBottom ?? frame.screenRadius) * Math.min(sx, sy);
358
+ const maskId = `biela-screen-mask-${suffix || "default"}`;
359
+ const innerPath = `M${x + rt},${y} H${x + w - rt} a${rt},${rt} 0 0 1 ${rt},${rt} V${y + h - rb} a${rb},${rb} 0 0 1 ${-rb},${rb} H${x + rb} a${rb},${rb} 0 0 1 ${-rb},${-rb} V${y + rt} a${rt},${rt} 0 0 1 ${rt},${-rt} Z`;
360
+ const maskDef = `<defs><mask id="${maskId}" maskUnits="userSpaceOnUse" x="${vb.x}" y="${vb.y}" width="${vb.w}" height="${vb.h}"><rect x="${vb.x}" y="${vb.y}" width="${vb.w}" height="${vb.h}" fill="white"/><path d="${innerPath}" fill="black"/></mask></defs>`;
361
+ let result = svg.replace(/(<svg\b[^>]*>)/i, (m) => `${m}${maskDef}<g mask="url(#${maskId})">`);
362
+ result = result.replace(/<\/svg\s*>/i, `</g></svg>`);
363
+ return result;
364
+ }
365
+ function buildCustomComponent(deviceId, svgString, cropViewBox, suffix, frame, dieCut = false) {
333
366
  const scopeKey = suffix ? `${deviceId}${suffix}` : deviceId;
334
367
  let svg = (0, import_tablets2.scopeSVGIds)(svgString, scopeKey);
335
368
  if (cropViewBox) {
@@ -349,7 +382,7 @@ function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suff
349
382
  }
350
383
  );
351
384
  }
352
- void screenRect;
385
+ if (frame && dieCut) svg = injectScreenMask(svg, frame, scopeKey);
353
386
  const Component2 = ({ style }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
354
387
  "span",
355
388
  {
@@ -360,9 +393,10 @@ function buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, suff
360
393
  Component2.displayName = `CustomDeviceSVG(${scopeKey})`;
361
394
  return Component2;
362
395
  }
363
- function registerCustomDeviceSVG(deviceId, svgString, frame, cropViewBox, screenRect, landscape) {
364
- const portraitComponent = buildCustomComponent(deviceId, svgString, cropViewBox, screenRect, "");
365
- const landscapeComponent = landscape ? buildCustomComponent(deviceId, landscape.svgString, landscape.cropViewBox, landscape.screenRect, "-landscape") : void 0;
396
+ function registerCustomDeviceSVG(deviceId, svgString, frame, cropViewBox, screenRect, landscape, options) {
397
+ const dieCut = options?.dieCutScreen ?? false;
398
+ const portraitComponent = buildCustomComponent(deviceId, svgString, cropViewBox, "", frame, dieCut);
399
+ const landscapeComponent = landscape ? buildCustomComponent(deviceId, landscape.svgString, landscape.cropViewBox, "-landscape", landscape.frame, dieCut) : void 0;
366
400
  SVG_REGISTRY.set(deviceId, {
367
401
  component: portraitComponent,
368
402
  frame,
@@ -680,7 +714,9 @@ function DeviceFrame({
680
714
  manualScale = 1,
681
715
  showSafeAreaOverlay = false,
682
716
  showScaleBar = true,
683
- showStatusBar = true,
717
+ // Implicit oprit: SVG-urile device-urilor au status bar-ul desenat în ramă
718
+ // (ceas, semnal, baterie); cel sintetic ar apărea dublat peste conținut.
719
+ showStatusBar = false,
684
720
  colorScheme = "light",
685
721
  iframeRef,
686
722
  onColorSchemeChange,
@@ -762,9 +798,6 @@ function DeviceFrame({
762
798
  const contentBezelTop = activeFrame?.bezelTop ?? 0;
763
799
  const contentScreenW = activeFrame?.screenWidth ?? dw;
764
800
  const contentScreenH = activeFrame?.screenHeight ?? dh;
765
- const baseRadius = activeFrame?.screenRadius ?? meta.screen.cornerRadius ?? 0;
766
- const radiusTop = activeFrame?.screenRadiusTop ?? baseRadius;
767
- const radiusBottom = activeFrame?.screenRadiusBottom ?? baseRadius;
768
801
  const useRotationFallback = rotateFrame && !hasLandscapeSVG;
769
802
  const scalerTransform = useRotationFallback ? `scale(${scale}) translate(0px, ${scalerW}px) rotate(-90deg)` : `scale(${scale})`;
770
803
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
@@ -803,6 +836,29 @@ function DeviceFrame({
803
836
  transition: "transform 400ms cubic-bezier(0.4, 0, 0.2, 1)"
804
837
  },
805
838
  children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
840
+ "div",
841
+ {
842
+ className: "bielaframe-content",
843
+ style: {
844
+ position: "absolute",
845
+ left: `${contentBezelLeft / scalerW * 100}%`,
846
+ top: `${contentBezelTop / scalerH * 100}%`,
847
+ width: `${contentScreenW / scalerW * 100}%`,
848
+ height: `${contentScreenH / scalerH * 100}%`,
849
+ overflow: "hidden",
850
+ zIndex: 0,
851
+ background: colorScheme === "dark" ? "#000" : "#fff",
852
+ borderRadius: activeFrame?.screenRadius ?? 0,
853
+ ...cssVarsStyle
854
+ },
855
+ children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DeviceErrorBoundary, { children }),
857
+ showStatusBar && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DynamicStatusBar, { contract, orientation, colorScheme }),
858
+ showSafeAreaOverlay && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SafeAreaOverlay, { contract, orientation })
859
+ ]
860
+ }
861
+ ),
806
862
  SVGComponent && portraitFrame && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
807
863
  "div",
808
864
  {
@@ -850,38 +906,6 @@ function DeviceFrame({
850
906
  }
851
907
  )
852
908
  }
853
- ),
854
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
855
- "div",
856
- {
857
- className: "bielaframe-content",
858
- style: {
859
- position: "absolute",
860
- left: contentBezelLeft,
861
- top: contentBezelTop,
862
- width: contentScreenW,
863
- height: contentScreenH,
864
- // `border-radius + overflow:hidden` clips the <iframe> child
865
- // in every browser. clip-path is kept as belt-and-braces — some
866
- // engines apply it to the iframe, some don't. Per-edge radii
867
- // (top vs bottom) let devices with asymmetric corners — e.g.
868
- // flat-bottom — clip correctly.
869
- borderRadius: `${radiusTop}px ${radiusTop}px ${radiusBottom}px ${radiusBottom}px`,
870
- clipPath: `inset(0 round ${radiusTop}px ${radiusTop}px ${radiusBottom}px ${radiusBottom}px)`,
871
- overflow: "hidden",
872
- isolation: "isolate",
873
- backfaceVisibility: "hidden",
874
- transform: "translateZ(0)",
875
- background: colorScheme === "dark" ? "#000" : "#fff",
876
- zIndex: 5,
877
- ...cssVarsStyle
878
- },
879
- children: [
880
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DeviceErrorBoundary, { children }),
881
- showStatusBar && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DynamicStatusBar, { contract, orientation, colorScheme }),
882
- showSafeAreaOverlay && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SafeAreaOverlay, { contract, orientation })
883
- ]
884
- }
885
909
  )
886
910
  ]
887
911
  }
@@ -1196,7 +1220,8 @@ var CustomSVGStore = class {
1196
1220
  },
1197
1221
  void 0,
1198
1222
  sr,
1199
- landscape
1223
+ landscape,
1224
+ { dieCutScreen: entry.dieCutScreen ?? false }
1200
1225
  );
1201
1226
  }
1202
1227
  persist(all) {