svgnest render fix
This commit is contained in:
parent
13879407f7
commit
582cafca0b
2 changed files with 48 additions and 110 deletions
|
|
@ -1,4 +1,3 @@
|
||||||
// components/portal/UtilitySwitcher.tsx
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
@ -35,71 +34,13 @@ const FileBrowserPanel = dynamic(
|
||||||
);
|
);
|
||||||
|
|
||||||
const ITEMS: Item[] = [
|
const ITEMS: Item[] = [
|
||||||
// ✅ Laser Toolkit now renders inline with sub-tabs
|
{ key: "laser-toolkit", label: "Laser Toolkit", note: "convert laser settings, interval and more", icon: "toolkit.png", component: LaserToolkitSwitcher, href: "https://makearmy.io/laser-toolkit" },
|
||||||
{
|
{ key: "files", label: "File Server", note: "download from our file explorer", icon: "fs.png", component: FileBrowserPanel, href: "https://makearmy.io/files" },
|
||||||
key: "laser-toolkit",
|
{ key: "svgnest", label: "SVGnest", note: "automatically nests parts and exports svg", icon: "nest.png", component: SVGNestPanel, href: "https://makearmy.io/svgnest" },
|
||||||
label: "Laser Toolkit",
|
{ key: "background-remover", label: "BG Remover", note: "open source background remover", icon: "bgrm.png", component: BackgroundRemoverPanel, href: "https://makearmy.io/background-remover" },
|
||||||
note: "convert laser settings, interval and more",
|
{ key: "picsur", label: "Picsur", note: "Simple Image Host", icon: "picsur.png", href: "https://images.makearmy.io" },
|
||||||
icon: "toolkit.png",
|
{ key: "privatebin", label: "PrivateBin", note: "Encrypted internet clipboard", icon: "privatebin.png", href: "https://paste.makearmy.io/" },
|
||||||
component: LaserToolkitSwitcher,
|
{ key: "forgejo", label: "Forgejo", note: "git for our community members", icon: "forge.png", href: "https://forge.makearmy.io" },
|
||||||
href: "https://makearmy.io/laser-toolkit", // optional; component takes precedence
|
|
||||||
},
|
|
||||||
|
|
||||||
// ✅ File Server inline (no iframe)
|
|
||||||
{
|
|
||||||
key: "files",
|
|
||||||
label: "File Server",
|
|
||||||
note: "download from our file explorer",
|
|
||||||
icon: "fs.png",
|
|
||||||
component: FileBrowserPanel,
|
|
||||||
href: "https://makearmy.io/files",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Buying Guide moved to main portal tab — remove here to avoid duplication
|
|
||||||
// { key: "buying-guide", ... }
|
|
||||||
|
|
||||||
// ✅ SVGnest inline (micro-frontend wrapper)
|
|
||||||
{
|
|
||||||
key: "svgnest",
|
|
||||||
label: "SVGnest",
|
|
||||||
note: "automatically nests parts and exports svg",
|
|
||||||
icon: "nest.png",
|
|
||||||
component: SVGNestPanel,
|
|
||||||
href: "https://makearmy.io/svgnest",
|
|
||||||
},
|
|
||||||
|
|
||||||
// ✅ Background Remover inline
|
|
||||||
{
|
|
||||||
key: "background-remover",
|
|
||||||
label: "BG Remover",
|
|
||||||
note: "open source background remover",
|
|
||||||
icon: "bgrm.png",
|
|
||||||
component: BackgroundRemoverPanel,
|
|
||||||
href: "https://makearmy.io/background-remover",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Subdomains (new tab)
|
|
||||||
{
|
|
||||||
key: "picsur",
|
|
||||||
label: "Picsur",
|
|
||||||
note: "Simple Image Host",
|
|
||||||
icon: "picsur.png",
|
|
||||||
href: "https://images.makearmy.io",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "privatebin",
|
|
||||||
label: "PrivateBin",
|
|
||||||
note: "Encrypted internet clipboard",
|
|
||||||
icon: "privatebin.png",
|
|
||||||
href: "https://paste.makearmy.io/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "forgejo",
|
|
||||||
label: "Forgejo",
|
|
||||||
note: "git for our community members",
|
|
||||||
icon: "forgejo.png",
|
|
||||||
href: "https://forge.makearmy.io",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function isExternal(urlStr: string | undefined) {
|
function isExternal(urlStr: string | undefined) {
|
||||||
|
|
@ -112,7 +53,6 @@ function isExternal(urlStr: string | undefined) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For on-site URLs, convert absolute https://makearmy.io/path → /path for iframe src */
|
|
||||||
function toOnsitePath(urlStr: string): string {
|
function toOnsitePath(urlStr: string): string {
|
||||||
try {
|
try {
|
||||||
const u = new URL(urlStr);
|
const u = new URL(urlStr);
|
||||||
|
|
@ -128,7 +68,7 @@ function Panel({ item }: { item: Item }) {
|
||||||
const Cmp = item.component;
|
const Cmp = item.component;
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{item.note ? <div className="text-sm opacity-70">{item.note}</div> : null}
|
{/* Removed notes/headers to keep UI clean */}
|
||||||
<Cmp embedded />
|
<Cmp embedded />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -138,16 +78,8 @@ function Panel({ item }: { item: Item }) {
|
||||||
if (external) {
|
if (external) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div>
|
<a href={item.href} target="_blank" rel="noopener noreferrer" className="underline">
|
||||||
Opened <span className="font-medium">{item.label}</span> in a new tab.
|
Open {item.label}
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={item.href}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="underline"
|
|
||||||
>
|
|
||||||
Click here if it didn’t open.
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -155,15 +87,13 @@ function Panel({ item }: { item: Item }) {
|
||||||
|
|
||||||
const src = toOnsitePath(item.href || "/");
|
const src = toOnsitePath(item.href || "/");
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
|
||||||
{item.note ? <div className="text-sm opacity-70">{item.note}</div> : null}
|
|
||||||
<iframe
|
<iframe
|
||||||
key={src}
|
key={src}
|
||||||
src={src}
|
src={src}
|
||||||
className="w-full h-[72vh] rounded-md border"
|
className="w-full"
|
||||||
sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
|
style={{ height: "72vh" }}
|
||||||
|
// no sandbox; needs drag/drop etc.
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +142,7 @@ export default function UtilitySwitcher() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
{/* top buttons unchanged */}
|
||||||
<div className="mb-4 flex flex-wrap items-center gap-2">
|
<div className="mb-4 flex flex-wrap items-center gap-2">
|
||||||
{ITEMS.map((it) => {
|
{ITEMS.map((it) => {
|
||||||
const isInline = Boolean(it.component);
|
const isInline = Boolean(it.component);
|
||||||
|
|
@ -257,9 +188,8 @@ export default function UtilitySwitcher() {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-md border p-4">
|
{/* ⛔️ removed the old border/padding frame here */}
|
||||||
<Panel item={activeItem} />
|
<Panel item={activeItem} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,46 @@
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SVGNest panel – same-origin iframe wrapper.
|
* SVGNest panel – same-origin iframe wrapper (clean, frameless).
|
||||||
* Renders /public/svgnest/index.html in an isolated doc so the legacy app can
|
* Also hides the legacy page’s heading/tagline for a minimal look.
|
||||||
* own the DOM/CSS/JS without conflicting with Next.js styles or scripts.
|
|
||||||
*/
|
*/
|
||||||
export default function SVGNestPanel() {
|
export default function SVGNestPanel() {
|
||||||
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
||||||
const [ready, setReady] = useState(false);
|
const [ready, setReady] = useState(false);
|
||||||
const [err, setErr] = useState<string | null>(null);
|
const [err, setErr] = useState<string | null>(null);
|
||||||
|
|
||||||
// Basic load/error status for UX
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = iframeRef.current;
|
const el = iframeRef.current;
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
|
|
||||||
const onLoad = () => setReady(true);
|
const onLoad = () => {
|
||||||
|
setReady(true);
|
||||||
|
|
||||||
|
// Hide the legacy heading/tagline inside svgnest without touching the vendor files.
|
||||||
|
try {
|
||||||
|
const doc = el.contentDocument;
|
||||||
|
if (!doc) return;
|
||||||
|
|
||||||
|
// 1) Hide the first H1 (their title)
|
||||||
|
const h1 = doc.querySelector("h1");
|
||||||
|
if (h1) (h1 as HTMLElement).style.display = "none";
|
||||||
|
|
||||||
|
// 2) Hide any node that literally contains that tagline text
|
||||||
|
const all = Array.from(doc.querySelectorAll<HTMLElement>("body *"));
|
||||||
|
for (const n of all) {
|
||||||
|
const t = (n.textContent || "").trim().toLowerCase();
|
||||||
|
if (t.includes("automatically nests parts") && t.includes("exports svg")) {
|
||||||
|
n.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Optional: tighten top padding/margins so content sits snugly
|
||||||
|
(doc.body as HTMLElement).style.marginTop = "4px";
|
||||||
|
} catch {
|
||||||
|
// ignore; same-origin so this should succeed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onError = () => setErr("Failed to load /svgnest/index.html");
|
const onError = () => setErr("Failed to load /svgnest/index.html");
|
||||||
|
|
||||||
el.addEventListener("load", onLoad);
|
el.addEventListener("load", onLoad);
|
||||||
|
|
@ -39,31 +64,14 @@ export default function SVGNestPanel() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Same-origin iframe, no sandbox to allow drag/drop, file dialogs, workers, etc. */}
|
{/* Frameless iframe (no border, no wrapper frame) */}
|
||||||
<iframe
|
<iframe
|
||||||
ref={iframeRef}
|
ref={iframeRef}
|
||||||
src="/svgnest/index.html"
|
src="/svgnest/index.html"
|
||||||
title="SVGNest"
|
title="SVGNest"
|
||||||
className="w-full rounded-md border"
|
className="w-full"
|
||||||
style={{
|
style={{ height: "72vh", background: "transparent", border: "none" }}
|
||||||
height: "72vh",
|
|
||||||
background: "transparent",
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Fallback open-in-new-tab helper */}
|
|
||||||
<div className="mt-2 text-xs text-zinc-400">
|
|
||||||
If the app doesn’t respond,{" "}
|
|
||||||
<a
|
|
||||||
href="/svgnest/index.html"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="underline"
|
|
||||||
>
|
|
||||||
open SVGnest in a new tab
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue