From 2f5d37993e958d7dcc3c8b57ffa9b16c659aba74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20T=C3=B6rcsv=C3=A1ri?= Date: Tue, 16 Jun 2026 06:13:41 +0200 Subject: [PATCH] feat: /l/:libId/:tool lib-scoped route + fix lib items for fileless launches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a deep-linkable, reload-safe route /l// (LibToolPage) that boots the editor scoped to one backend library; the home page's lib chips become anchors to it (full nav → clean Emscripten). Local lib FILES stay in-place (bytes can't be URL-addressed). Fix empty lib trees against a registry server: fileless/lib-scoped launches use the placeholder projectId "local", which was sent as x-pcbjam-project — the closed server then scoped its (0005 mirror) lib resolution to a non-existent project and returned nothing. Suppress the project header for "local"; real backend projects keep sending their uuid and keep project-scoped resolution. Co-Authored-By: Claude Opus 4.8 (1M context) --- web/standalone/src/App.tsx | 2 + web/standalone/src/lib/config.ts | 8 +++- web/standalone/src/pages/HomePage.tsx | 33 +++++------------ web/standalone/src/pages/LibToolPage.tsx | 47 ++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 web/standalone/src/pages/LibToolPage.tsx diff --git a/web/standalone/src/App.tsx b/web/standalone/src/App.tsx index ab49022..ed1b7f3 100644 --- a/web/standalone/src/App.tsx +++ b/web/standalone/src/App.tsx @@ -1,5 +1,6 @@ import { Route, Routes } from "react-router-dom"; import { HomePage } from "@/pages/HomePage"; +import { LibToolPage } from "@/pages/LibToolPage"; import { ProjectView } from "@/pages/ProjectView"; import { ToolPage } from "@/pages/ToolPage"; @@ -9,6 +10,7 @@ export default function App() { } /> } /> } /> + } /> ); } diff --git a/web/standalone/src/lib/config.ts b/web/standalone/src/lib/config.ts index 7883390..70a8b32 100644 --- a/web/standalone/src/lib/config.ts +++ b/web/standalone/src/lib/config.ts @@ -74,12 +74,18 @@ export function libsOwner(): string { export function libsSourceConfig(projectId?: string): LibsSource | null { const kind = import.meta.env.VITE_LIBS_SOURCE ?? "remote"; + // "local" is the placeholder id for launches with no real backend project + // (local folder, tool grid, lib-scoped open). It is NOT a project on the + // backend, so don't send it as the project header — a registry server would + // scope its lib resolution (project-pinned mirrors) to a non-existent project + // and return nothing. Real backend projects pass their uuid and keep scoping. + const project = projectId && projectId !== "local" ? projectId : undefined; const base = kind === "off" ? null : kind === "static" ? staticLibsSource() - : remoteLibsSource(API_BASE_URL, libsOwner(), projectId); + : remoteLibsSource(API_BASE_URL, libsOwner(), project); // 0004-A spike: `?libwrite=1` adds one in-memory writable user SYMBOL lib so the // editor save path works with no backend (a dev/test aid). The real remote diff --git a/web/standalone/src/pages/HomePage.tsx b/web/standalone/src/pages/HomePage.tsx index 863a446..ae1766a 100644 --- a/web/standalone/src/pages/HomePage.tsx +++ b/web/standalone/src/pages/HomePage.tsx @@ -3,8 +3,6 @@ import { Link } from "react-router-dom"; import type { Lib, Tool } from "@pcbjam/shared"; import { FolderOpen, Library, Loader2, Package } from "lucide-react"; import { useLibs, useProjects } from "@/lib/api"; -import { libsSourceConfig } from "@/lib/config"; -import { scopedLibsSource } from "@/wasm/libs/scoped-source"; import { localFileLibsSource } from "@/wasm/libs/local-file-source"; import type { LibsSource } from "@/wasm/libs/source"; import { downloadBytes } from "@/lib/download"; @@ -121,15 +119,6 @@ export function HomePage() { libsSource?: LibsSource | null; } | null>(null); - /** Open a backend library scoped to itself in its matching editor. */ - const openScopedLib = (tool: Tool, lib: Lib) => { - const base = libsSourceConfig("local"); - setLaunchedTool({ - tool, - libsSource: base ? scopedLibsSource(base, lib.id) : undefined, - }); - }; - // is non-standard; set it imperatively. React.useEffect(() => { if (inputRef.current) inputRef.current.setAttribute("webkitdirectory", ""); @@ -299,13 +288,13 @@ export function HomePage() { icon={} label="Symbols" query={symbolLibs} - onOpen={(lib) => openScopedLib("symbol_editor", lib)} + tool="symbol_editor" /> } label="Footprints" query={footprintLibs} - onOpen={(lib) => openScopedLib("footprint_editor", lib)} + tool="footprint_editor" /> @@ -314,21 +303,20 @@ export function HomePage() { } /** - * One backend-library group (Symbols / Footprints): clickable chips that launch - * the matching editor to browse the library. The editor lists ALL libs of that - * kind in its own tree (lib-scoped open is a later iteration); clicking here - * just opens the right tool. + * One backend-library group (Symbols / Footprints): each lib is a deep-link to + * `/l//` (LibToolPage), which boots the editor scoped to that one + * library. A full navigation (anchor) gives Emscripten a clean page. */ function LibGroup({ icon, label, query, - onOpen, + tool, }: { icon: React.ReactNode; label: string; query: ReturnType; - onOpen: (lib: Lib) => void; + tool: Tool; }) { const { data: libs, isLoading, error } = query; return ( @@ -352,10 +340,9 @@ function LibGroup({ {libs && libs.length > 0 && (
{libs.map((lib) => ( - + ))}
)} diff --git a/web/standalone/src/pages/LibToolPage.tsx b/web/standalone/src/pages/LibToolPage.tsx new file mode 100644 index 0000000..c7b00d3 --- /dev/null +++ b/web/standalone/src/pages/LibToolPage.tsx @@ -0,0 +1,47 @@ +import { useParams } from "react-router-dom"; +import { toolSchema } from "@pcbjam/shared"; +import { libsSourceConfig } from "@/lib/config"; +import { scopedLibsSource } from "@/wasm/libs/scoped-source"; +import { WasmTool } from "@/components/WasmTool"; +import { PreflightGate } from "@/preflight/PreflightGate"; + +/** + * Open one BACKEND library scoped to itself in its editor, addressed by URL: + * /l// e.g. /l/Diode/symbol_editor + * + * Deep-linkable + reload-safe (unlike the home page's in-place lib launch). The + * editor boots with no project/files and a `scopedLibsSource` so its lib tree + * shows exactly this one library. (Local lib FILES can't be URL-addressed — they + * stay an in-place launch on the home page.) + */ +export function LibToolPage() { + const params = useParams(); + const libId = params.lib ?? ""; + const parsedTool = toolSchema.safeParse(params.tool); + + if (!parsedTool.success) { + return ( +
+ Unknown tool: {params.tool} +
+ ); + } + + const base = libsSourceConfig("local"); + const libsSource = base ? scopedLibsSource(base, libId) : null; + + return ( + + { + throw new Error(`no project file to fetch: ${relPath}`); + }} + /> + + ); +}