From 0855724cb0ae58b2338ce3cda72b32d26d0a2d52 Mon Sep 17 00:00:00 2001
From: makearmy
Date: Fri, 10 Jul 2026 17:22:41 -0400
Subject: [PATCH 1/2] fix laser utility calculations
---
.../laser-toolkit/beam-spot-size/page.tsx | 15 +-
.../laser-toolkit/hatch-overlap/page.tsx | 17 ++-
.../laser-toolkit/job-time-estimator/page.tsx | 5 +-
.../laser-toolkit/power-lens-scaler/page.tsx | 129 +++---------------
.../laser-toolkit/pulse-overlap/page.tsx | 23 ++--
5 files changed, 58 insertions(+), 131 deletions(-)
diff --git a/app/components/utilities/laser-toolkit/beam-spot-size/page.tsx b/app/components/utilities/laser-toolkit/beam-spot-size/page.tsx
index 6874057..a91bb90 100644
--- a/app/components/utilities/laser-toolkit/beam-spot-size/page.tsx
+++ b/app/components/utilities/laser-toolkit/beam-spot-size/page.tsx
@@ -10,7 +10,7 @@ function num(v: string) {
return Number.isFinite(n) ? n : 0;
}
-// Spot diameter (µm) ≈ 1.27 * M² * λ(µm) * f(mm) / D(mm)
+// Focused Gaussian 1/e² diameter: d = 4 M² λ f / (π D).
export default function Page() {
const [lambdaNm, setLambdaNm] = useState("1064"); // nm (default fiber)
const [focalMm, setFocalMm] = useState("160"); // mm
@@ -21,9 +21,9 @@ export default function Page() {
const lamUm = num(lambdaNm) / 1000; // convert nm -> µm
const f = num(focalMm);
const D = num(beamDm);
- const M2 = Math.max(1, num(m2));
- if (lamUm <= 0 || f <= 0 || D <= 0) return 0;
- return 1.27 * M2 * lamUm * (f / D);
+ const M2 = num(m2);
+ if (lamUm <= 0 || f <= 0 || D <= 0 || M2 < 1) return 0;
+ return (4 / Math.PI) * M2 * lamUm * (f / D);
}, [lambdaNm, focalMm, beamDm, m2]);
const dMm = dUm / 1000;
@@ -71,7 +71,7 @@ export default function Page() {
-
Spot diameter
+
1/e² spot diameter
{dMm.toFixed(4)} mm
{dUm.toFixed(2)} µm
@@ -82,7 +82,10 @@ export default function Page() {
+
+ Gaussian-beam estimate: d = 4 M²λf/(πD), where D is the 1/e² beam diameter at the
+ lens. Real spots may be larger because of lens aberration, clipping, beam expansion, and focus error.
+
);
}
-
diff --git a/app/components/utilities/laser-toolkit/hatch-overlap/page.tsx b/app/components/utilities/laser-toolkit/hatch-overlap/page.tsx
index dbad915..34470c8 100644
--- a/app/components/utilities/laser-toolkit/hatch-overlap/page.tsx
+++ b/app/components/utilities/laser-toolkit/hatch-overlap/page.tsx
@@ -29,11 +29,12 @@ export default function Page() {
setGapUm(L > 0 ? (UM_PER_INCH / L).toFixed(2) : "");
}
- const overlap = useMemo(() => {
+ const coverage = useMemo(() => {
const d = num(spotUm);
const g = num(gapUm);
- if (d <= 0 || g <= 0) return 0;
- return Math.max(0, Math.min(100, 100 * (1 - g / d)));
+ if (d <= 0 || g <= 0) return { overlap: 0, uncovered: 0 };
+ const signed = 100 * (1 - g / d);
+ return { overlap: Math.max(0, signed), uncovered: Math.max(0, -signed) };
}, [spotUm, gapUm]);
const gapMm = (num(gapUm) / 1000) || 0;
@@ -68,7 +69,10 @@ export default function Page() {
Overlap
-
{overlap.toFixed(1)}%
+
{coverage.overlap.toFixed(1)}%
+ {coverage.uncovered > 0 && (
+
{coverage.uncovered.toFixed(1)}% uncovered gap
+ )}
Gap
@@ -83,10 +87,10 @@ export default function Page() {
From LPI
- {(UM_PER_INCH / Math.max(1, num(lpi)) / 1000).toFixed(4)} mm
+ {num(lpi) > 0 ? (UM_PER_INCH / num(lpi) / 1000).toFixed(4) : "0.0000"} mm
- {(UM_PER_INCH / Math.max(1, num(lpi))).toFixed(1)} µm
+ {num(lpi) > 0 ? (UM_PER_INCH / num(lpi)).toFixed(1) : "0.0"} µm
@@ -94,4 +98,3 @@ export default function Page() {
);
}
-
diff --git a/app/components/utilities/laser-toolkit/job-time-estimator/page.tsx b/app/components/utilities/laser-toolkit/job-time-estimator/page.tsx
index 24a1fd2..ec28886 100644
--- a/app/components/utilities/laser-toolkit/job-time-estimator/page.tsx
+++ b/app/components/utilities/laser-toolkit/job-time-estimator/page.tsx
@@ -44,7 +44,7 @@ export default function Page() {
if (w <= 0 || h <= 0 || D <= 0 || v <= 0) return { t: 0, gapMm: 0, gapUm: 0, rows: 0 };
const gapMm = 25.4 / D;
const gapUm = gapMm * 1000;
- const rows = h / gapMm;
+ const rows = Math.max(1, Math.ceil(h / gapMm));
const t = rows * (w / v) * p * k;
return { t, gapMm, gapUm, rows };
} else {
@@ -159,7 +159,7 @@ export default function Page() {
{/* Footnote */}
Overhead factor* accounts for real-world slowdowns:
- acceleration/decelleration, jump moves, polygon delays, laser on/off timing, overscan,
+ acceleration/deceleration, jump moves, polygon delays, laser on/off timing, overscan,
bidirectional settle time, and controller latency.{" "}
Typical values: Vector cuts/marks{" "}
1.05–1.15 (simple paths, long runs closer to 1.05; tiny
@@ -171,4 +171,3 @@ export default function Page() {
);
}
-
diff --git a/app/components/utilities/laser-toolkit/power-lens-scaler/page.tsx b/app/components/utilities/laser-toolkit/power-lens-scaler/page.tsx
index 6113608..3fdb31b 100644
--- a/app/components/utilities/laser-toolkit/power-lens-scaler/page.tsx
+++ b/app/components/utilities/laser-toolkit/power-lens-scaler/page.tsx
@@ -18,33 +18,10 @@ function clamp(v: number, lo: number, hi: number) {
return Math.max(lo, Math.min(hi, v));
}
-/** Default curve parameters based on rated power (very rough, editable). */
-function defaultCurveForRatedW(W: number) {
- // Peak frequency guess (kHz). Tune these to your hardware fleet.
- let fPeak = 50;
- if (W <= 35) fPeak = 25;
- else if (W <= 60) fPeak = 50;
- else if (W <= 90) fPeak = 75;
- else fPeak = 100;
-
- // Log-normal width parameter (dimensionless). Smaller = narrower peak.
- const sigma = 0.35;
- return { fPeak, sigma };
-}
-
-/** Log-normal shaped efficiency curve normalized to 1 at fPeak. */
-function etaOfF(f_kHz: number, fPeak_kHz: number, sigma: number) {
- const f = Math.max(f_kHz, 0.1);
- const r = Math.log(f / Math.max(fPeak_kHz, 0.1));
- const eta = Math.exp(-0.5 * (r / Math.max(sigma, 0.05)) ** 2);
- // Keep within [0.1, 1] to avoid absurd zeros; adjust if you want tails to hit 0.
- return clamp(eta, 0.1, 1);
-}
-
-/** Area factor from field (proxy for spot area scaling) */
-function areaFactorFromField(fieldSrc: number, fieldDst: number) {
- if (fieldSrc <= 0 || fieldDst <= 0) return 1;
- const r = fieldDst / fieldSrc;
+/** Circular spot-area ratio; pi/4 cancels. */
+function areaFactorFromDiameter(spotSrc: number, spotDst: number) {
+ if (spotSrc <= 0 || spotDst <= 0) return 1;
+ const r = spotDst / spotSrc;
return r * r;
}
@@ -59,7 +36,7 @@ export default function Page() {
const [hSrc, setHSrc] = useState('0.1'); // mm (raster line spacing)
const [fSrc, setFSrc] = useState('30'); // kHz
const [tauSrc, setTauSrc] = useState('100'); // ns pulse width
- const [fieldSrc, setFieldSrc] = useState('110'); // mm
+ const [fieldSrc, setFieldSrc] = useState('60'); // µm, 1/e² spot diameter
// DEST machine/lens
const [wDst, setWDst] = useState('50'); // rated W
@@ -67,16 +44,7 @@ export default function Page() {
const [hDst, setHDst] = useState('0.1'); // mm
const [fDst, setFDst] = useState('30'); // kHz
const [tauDst, setTauDst] = useState('100'); // ns
- const [fieldDst, setFieldDst] = useState('70'); // mm
-
- // Curve tuning / advanced
- const [advanced, setAdvanced] = useState(false);
- const srcDefaults = defaultCurveForRatedW(num(wSrc, 50));
- const dstDefaults = defaultCurveForRatedW(num(wDst, 50));
- const [fPeakSrc, setFPeakSrc] = useState(String(srcDefaults.fPeak));
- const [sigmaSrc, setSigmaSrc] = useState(String(srcDefaults.sigma));
- const [fPeakDst, setFPeakDst] = useState(String(dstDefaults.fPeak));
- const [sigmaDst, setSigmaDst] = useState(String(dstDefaults.sigma));
+ const [fieldDst, setFieldDst] = useState('40'); // µm
// Prefer adjusting speed/freq instead of exceeding 100% power
const [preferSpeedAdjust, setPreferSpeedAdjust] = useState(true);
@@ -91,21 +59,11 @@ export default function Page() {
const h2 = Math.max(num(hDst, 0), 0.000001);
const f1k = Math.max(num(fSrc, 0), 0.1);
const f2k = Math.max(num(fDst, 0), 0.1);
- const tau1_ns = Math.max(num(tauSrc, 0), 0.1);
const tau2_ns = Math.max(num(tauDst, 0), 0.1);
- const aFac = areaFactorFromField(num(fieldSrc, 0), num(fieldDst, 0));
-
- const fpk1 = Math.max(num(fPeakSrc, defaultCurveForRatedW(W1).fPeak), 0.1);
- const sig1 = Math.max(num(sigmaSrc, defaultCurveForRatedW(W1).sigma), 0.05);
- const fpk2 = Math.max(num(fPeakDst, defaultCurveForRatedW(W2).fPeak), 0.1);
- const sig2 = Math.max(num(sigmaDst, defaultCurveForRatedW(W2).sigma), 0.05);
-
- // Efficiency factors (0..1)
- const eta1 = etaOfF(f1k, fpk1, sig1);
- const eta2 = etaOfF(f2k, fpk2, sig2);
+ const aFac = areaFactorFromDiameter(num(fieldSrc, 0), num(fieldDst, 0));
// Effective average power (W) after frequency efficiency
- const P1eff = W1 * p1 * eta1;
+ const P1eff = W1 * p1;
let p2Frac = p1; // destination power fraction (0..1)
let suggestedSpeed: number | undefined;
@@ -114,7 +72,7 @@ export default function Page() {
// Helper: compute required P2eff for each match, then map to power%
const powerPercentFromEff = (P2effReq: number) => {
// P2eff = W2 * p2 * eta2 => p2 = P2eff / (W2*eta2)
- return P2effReq / (W2 * eta2);
+ return P2effReq / W2;
};
if (mode === 'vector') {
@@ -122,7 +80,7 @@ export default function Page() {
const P2effReq = P1eff * (v2 / v1);
p2Frac = powerPercentFromEff(P2effReq);
if (preferSpeedAdjust && p2Frac > 1) {
- suggestedSpeed = v1 * (W2 * eta2) / (W1 * eta1 * p1); // from p2<=1
+ suggestedSpeed = v1 * W2 / (W1 * p1); // from p2<=1
p2Frac = 1;
}
} else if (mode === 'raster') {
@@ -130,7 +88,7 @@ export default function Page() {
const P2effReq = P1eff * ((v2 * h2) / (v1 * h1));
p2Frac = powerPercentFromEff(P2effReq);
if (preferSpeedAdjust && p2Frac > 1) {
- suggestedSpeed = v1 * (W2 * eta2) * (h1 / h2) / (W1 * eta1 * p1);
+ suggestedSpeed = v1 * W2 * (h1 / h2) / (W1 * p1);
p2Frac = 1;
}
} else if (mode === 'irradiance') {
@@ -149,7 +107,7 @@ export default function Page() {
if (preferSpeedAdjust && p2Frac > 1) {
// Suggest lowering f2 to keep p2<=1: P2eff_max = W2*eta2*1
// f2_req = P2eff_max / Ep1
- const f2_req = (W2 * eta2) / Ep1; // Hz
+ const f2_req = W2 / Ep1; // Hz
suggestedFreq_kHz = Math.max(f2_req / 1e3, 0.1);
p2Frac = 1;
}
@@ -157,7 +115,7 @@ export default function Page() {
// Compute pulse metrics (for display) using **destination** settings
const p2Clamped = clamp(p2Frac, 0, 2);
- const P2eff = W2 * p2Clamped * eta2;
+ const P2eff = W2 * p2Clamped;
const f2Hz = f2k * 1e3;
const tau2_s = tau2_ns * 1e-9;
const Ep2 = P2eff / f2Hz; // J
@@ -167,8 +125,6 @@ export default function Page() {
p2Percent: clamp(p2Clamped * 100, 0, 200),
suggestedSpeed,
suggestedFreq_kHz,
- eta1,
- eta2,
P1eff,
P2eff,
Ep2,
@@ -177,13 +133,13 @@ export default function Page() {
};
}, [
mode, wSrc, wDst, pSrc, vSrc, vDst, hSrc, hDst, fSrc, fDst, tauSrc, tauDst,
- fieldSrc, fieldDst, preferSpeedAdjust, fPeakSrc, sigmaSrc, fPeakDst, sigmaDst,
+ fieldSrc, fieldDst, preferSpeedAdjust,
]);
return (
@@ -197,7 +153,7 @@ export default function Page() {
Vector: Energy per length (J/mm)
Raster: Energy per area (J/mm²)
- Irradiance: W/mm² (spot/field)
+ Spot irradiance: W/mm²
Pulse energy: J (fiber)
@@ -247,32 +203,11 @@ export default function Page() {
setHSrc(e.target.value)} inputMode="decimal" />
- Lens field size (mm)
+ 1/e² spot diameter (µm)
setFieldSrc(e.target.value)} inputMode="decimal" />
-
- setAdvanced((s) => !s)}
- >
- {advanced ? 'Hide' : 'Show'} advanced frequency curve
-
-
-
- Peak freq fₚ (kHz)
- setFPeakSrc(e.target.value)} inputMode="decimal" />
-
-
- Curve width σ (log-normal)
- setSigmaSrc(e.target.value)} inputMode="decimal" />
-
-
- η(f) is log-normal; 1.0 at fₚ, rolls off by σ.
-
-
-
{/* Destination */}
@@ -302,26 +237,11 @@ export default function Page() {
setHDst(e.target.value)} inputMode="decimal" />
- Lens field size (mm)
+ 1/e² spot diameter (µm)
setFieldDst(e.target.value)} inputMode="decimal" />
-
-
-
- Peak freq fₚ (kHz)
- setFPeakDst(e.target.value)} inputMode="decimal" />
-
-
- Curve width σ (log-normal)
- setSigmaDst(e.target.value)} inputMode="decimal" />
-
-
- Adjust if you know your machine’s real power–frequency curve.
-
-
-
{/* Result */}
@@ -348,11 +268,7 @@ export default function Page() {
)}
-
-
-
η(f) source / dest
-
{result.eta1.toFixed(3)} / {result.eta2.toFixed(3)}
-
+
Dest pulse energy
@@ -366,13 +282,12 @@ export default function Page() {
- Assumptions: Effective power includes a frequency efficiency factor η(f). Peak power uses a rectangular pulse
- approximation (shape factor ≈ 1). For real MOPA sources, pulse shape and
- true power–frequency maps vary by model; adjust fp and σ if you have vendor curves.
+ Assumptions: displayed power percentage scales average output linearly, spots are circular,
+ and peak power uses a rectangular pulse approximation. Confirm the result with a low-power test;
+ real sources can have model-specific power limits versus frequency and pulse width.
);
}
-
diff --git a/app/components/utilities/laser-toolkit/pulse-overlap/page.tsx b/app/components/utilities/laser-toolkit/pulse-overlap/page.tsx
index e8ce13c..fb443eb 100644
--- a/app/components/utilities/laser-toolkit/pulse-overlap/page.tsx
+++ b/app/components/utilities/laser-toolkit/pulse-overlap/page.tsx
@@ -21,16 +21,19 @@ export default function Page() {
const dUm = num(spotUm); // µm
if (v <= 0 || f <= 0 || dUm <= 0) {
- return { spacingUm: 0, spacingMm: 0, overlapPct: 0, pulsesPerMm: 0 };
+ return { spacingUm: 0, spacingMm: 0, overlapPct: 0, gapPct: 0, pulsesPerMm: 0, pulsesPerSpot: 0 };
}
// distance per pulse
const spacingUm = v / f; // µm (derives from v(mm/s) / (f(kHz)*1000) * 1000)
const spacingMm = spacingUm / 1000;
- const overlapPct = Math.max(0, Math.min(100, 100 * (1 - spacingUm / dUm)));
+ const signedOverlapPct = 100 * (1 - spacingUm / dUm);
+ const overlapPct = Math.max(0, signedOverlapPct);
+ const gapPct = Math.max(0, -signedOverlapPct);
const pulsesPerMm = (f * 1000) / v;
+ const pulsesPerSpot = dUm / spacingUm;
- return { spacingUm, spacingMm, overlapPct, pulsesPerMm };
+ return { spacingUm, spacingMm, overlapPct, gapPct, pulsesPerMm, pulsesPerSpot };
}, [speed, freq, spotUm]);
return (
@@ -68,20 +71,24 @@ export default function Page() {
Overlap
{result.overlapPct.toFixed(1)}%
+ {result.gapPct > 0 && (
+
{result.gapPct.toFixed(1)}% gap between pulses
+ )}
Pulses per mm
{result.pulsesPerMm.toFixed(1)}
-
Rule of thumb
-
- 60–80% overlap is common for marking; deeper engraving often higher.
-
+
Pulses per spot diameter
+
{result.pulsesPerSpot.toFixed(2)}
+
+ Geometric overlap along the scan direction only. It does not predict material response;
+ pulse energy, spot profile, hatch spacing, and thermal accumulation also matter.
+
);
}
-
From 305ea41ad33ec311b42531881c57af8965f4c54f Mon Sep 17 00:00:00 2001
From: makearmy
Date: Fri, 10 Jul 2026 17:25:34 -0400
Subject: [PATCH 2/2] improve utilities workspace navigation
---
app/app/portal/utilities/Client.tsx | 10 +++---
app/app/styles/globals.css | 20 +++++++++++
.../portal/LaserToolkitSwitcher.tsx | 28 ++++++++++++----
app/components/portal/UtilitySwitcher.tsx | 33 +++++++++++--------
app/components/toolkit/ToolShell.tsx | 17 +++-------
5 files changed, 71 insertions(+), 37 deletions(-)
diff --git a/app/app/portal/utilities/Client.tsx b/app/app/portal/utilities/Client.tsx
index 0e8e1e1..5c58e3a 100644
--- a/app/app/portal/utilities/Client.tsx
+++ b/app/app/portal/utilities/Client.tsx
@@ -9,11 +9,13 @@ const UtilitySwitcher = dynamic(() => import("@/components/portal/UtilitySwitche
export default function UtilitiesClient() {
return (
-
-
-
Utilities
-
+
+
+
~/utilities
+
Workshop utilities
+
Calculators and production tools in one workspace.
+
);
}
diff --git a/app/app/styles/globals.css b/app/app/styles/globals.css
index 4841171..9ec318d 100644
--- a/app/app/styles/globals.css
+++ b/app/app/styles/globals.css
@@ -64,3 +64,23 @@
}
}
+@layer components {
+ /* Dense, terminal-inspired calculator workspace without nested floating cards. */
+ .laser-tool-workspace [class~="rounded-lg"][class~="border"] {
+ @apply rounded-lg border-border/60 bg-background/20 shadow-none;
+ }
+
+ .laser-tool-workspace [class~="rounded-lg"][class~="border"] > div:first-child {
+ @apply px-4 py-3;
+ }
+
+ .laser-tool-workspace [class~="rounded-lg"][class~="border"] > div:not(:first-child) {
+ @apply px-4 pb-4;
+ }
+
+ .laser-tool-workspace input,
+ .laser-tool-workspace select,
+ .laser-tool-workspace button[role="combobox"] {
+ @apply border-border/70 bg-background/70 shadow-none transition-colors focus:border-accent/70 focus:ring-1 focus:ring-accent/30;
+ }
+}
diff --git a/app/components/portal/LaserToolkitSwitcher.tsx b/app/components/portal/LaserToolkitSwitcher.tsx
index 69ddfda..3448b7f 100644
--- a/app/components/portal/LaserToolkitSwitcher.tsx
+++ b/app/components/portal/LaserToolkitSwitcher.tsx
@@ -4,6 +4,7 @@
import { useMemo } from "react";
import { useSearchParams, useRouter } from "next/navigation";
import { cn } from "@/lib/utils";
+import { Calculator, ChevronDown } from "lucide-react";
import { TOOLKIT_TABS } from "@/components/utilities/laser-toolkit/registry";
export default function LaserToolkitSwitcher({ basePath = "/portal/utilities" }: { basePath?: string }) {
@@ -34,23 +35,38 @@ export default function LaserToolkitSwitcher({ basePath = "/portal/utilities" }:
const ActiveCmp = active.component;
return (
-
-
+
+
+
+
+ setTab(e.target.value)}
+ className="h-10 w-full appearance-none rounded-lg border-border/70 bg-card pl-10 pr-9 text-sm"
+ >
+ {TOOLKIT_TABS.map((t) => {t.label} )}
+
+
+
+
{TOOLKIT_TABS.map(t => (
setTab(t.key)}
className={cn(
- "rounded-md border px-3 py-1.5 text-sm",
- activeKey === t.key ? "bg-primary text-primary-foreground" : "hover:bg-muted"
+ "rounded-md border border-transparent px-3 py-1.5 text-xs transition-colors",
+ activeKey === t.key
+ ? "border-border/60 bg-card text-foreground shadow-sm"
+ : "text-muted-foreground hover:bg-muted/30 hover:text-foreground"
)}
>
{t.label}
))}
-
+
-
diff --git a/app/components/portal/UtilitySwitcher.tsx b/app/components/portal/UtilitySwitcher.tsx
index 36faf0e..86260aa 100644
--- a/app/components/portal/UtilitySwitcher.tsx
+++ b/app/components/portal/UtilitySwitcher.tsx
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
import dynamic from "next/dynamic";
import { useRouter, useSearchParams } from "next/navigation";
import { cn } from "@/lib/utils";
+import { ChevronRight, ExternalLink, TerminalSquare } from "lucide-react";
type Item = {
key: string; // used in ?t=
@@ -216,9 +217,13 @@ export default function UtilitySwitcher() {
}, []);
return (
-
- {/* top buttons unchanged */}
-
+
+
+
+
+ Select utility
+
+
{ITEMS.map((it) => {
const isInline = Boolean(it.component);
const external = !isInline && isExternalHref(it.href);
@@ -235,10 +240,10 @@ export default function UtilitySwitcher() {
}
}}
className={cn(
- "flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm transition",
+ "group flex min-w-0 items-center gap-2.5 rounded-lg px-2.5 py-2 text-left text-xs transition-all sm:text-sm",
isActive
- ? "bg-primary text-primary-foreground"
- : "hover:bg-muted"
+ ? "bg-accent/15 text-foreground shadow-[inset_2px_0_0_hsl(var(--accent))]"
+ : "text-muted-foreground hover:bg-muted/30 hover:text-foreground"
)}
title={it.note || it.label}
>
@@ -249,27 +254,27 @@ export default function UtilitySwitcher() {
alt=""
width={16}
height={16}
- className="h-4 w-4 rounded-sm border object-cover"
+ className="h-5 w-5 shrink-0 rounded border border-border/60 object-cover opacity-80"
onError={(e) => {
(e.currentTarget as HTMLImageElement).style.display = "none";
}}
/>
) : null}
- {it.label}
+ {it.label}
{!isInline && external && (
-
- new tab
-
+
)}
+ {isInline && }
);
})}
-
+
- {/* ⛔️ removed the old border/padding frame here */}
-
+
);
}
diff --git a/app/components/toolkit/ToolShell.tsx b/app/components/toolkit/ToolShell.tsx
index 3611a9d..28d134d 100644
--- a/app/components/toolkit/ToolShell.tsx
+++ b/app/components/toolkit/ToolShell.tsx
@@ -1,4 +1,3 @@
-import Link from "next/link";
import { cn } from "@/lib/utils";
type ToolShellProps = {
@@ -19,31 +18,23 @@ export default function ToolShell({
subtitle,
className,
children,
- backHref = "/laser-toolkit",
- backLabel = "Back to Toolkit",
}: ToolShellProps) {
const desc = description ?? subtitle;
return (
-
-
+
+
-
{title}
+
laser.calc
+
{title}
{desc ? (
{desc}
) : null}
-
- {backLabel}
-
{children}
);
}
-