feat(web): wire pl_editor + symbol_editor tools (open, wizard-skip, UI)
Add the two newly WASM-ported editors to the web app the same way as the existing tools: - pl_editor (drawing-sheet, .kicad_wks): PL_EDITOR_FRAME overrides OpenProjectFiles, so it gets the generic kicadOpenFile embind hook (wasm/bindings/pl_editor_embind.cpp) for deterministic open. Mapped .kicad_wks -> pl_editor in EXTENSION_TOOL. - symbol_editor (symbol library): SYMBOL_EDIT_FRAME does NOT override OpenProjectFiles, so it's treated as file-less (boot standalone, opens libraries via its own UI). Added to FILELESS_TOOLS. Both boot through single_top.cpp's STARTWIZARD, so both seed config to skip the first-run wizard (TOOL_NEEDS_CONFIG_SEED) and get a /usr/bin/<binary> argv0. contract: add to TOOLS, plus a TOOL_LABELS map for friendly names. The project UI now renders file-less launch links generically from FILELESS_TOOLS and per-file "Open in <label>" links from EXTENSION_TOOL (auto file-type detection), so adding a tool needs no UI edits. Verified in-browser: pl_editor opens a .kicad_wks (renders the sheet), symbol_editor boots wizard-free; both with 0 console errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c77fed2ef1
commit
8341573c5c
4 changed files with 92 additions and 12 deletions
44
wasm/bindings/pl_editor_embind.cpp
Normal file
44
wasm/bindings/pl_editor_embind.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Embind bindings for KiCad pl_editor (drawing-sheet / page-layout editor) WASM.
|
||||||
|
*
|
||||||
|
* Picked up automatically by scripts/kicad/build-kicad-target.sh when building
|
||||||
|
* the pl_editor app (it compiles wasm/bindings/<app>_embind.cpp if present).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#include <emscripten/bind.h>
|
||||||
|
#include <kiway_player.h>
|
||||||
|
#include <kiway.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <wx/app.h>
|
||||||
|
#include <wx/string.h>
|
||||||
|
#include <wx/window.h>
|
||||||
|
|
||||||
|
using namespace emscripten;
|
||||||
|
|
||||||
|
// Programmatically open a drawing-sheet file (.kicad_wks) in the running editor
|
||||||
|
// frame, without UI automation. Mirrors single_top.cpp's MacOpenFile path: the
|
||||||
|
// editor frame is the app's top window and is a KIWAY_PLAYER (PL_EDITOR_FRAME
|
||||||
|
// overrides OpenProjectFiles). Returns the result of OpenProjectFiles, or false
|
||||||
|
// if no frame is available — letting the JS caller fall back to driving
|
||||||
|
// File→Open.
|
||||||
|
bool kicadOpenFile( std::string path )
|
||||||
|
{
|
||||||
|
KIWAY_PLAYER* frame =
|
||||||
|
wxTheApp ? static_cast<KIWAY_PLAYER*>( wxTheApp->GetTopWindow() ) : nullptr;
|
||||||
|
|
||||||
|
if( !frame )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if( wxWindow* blocking = frame->Kiway().GetBlockingDialog() )
|
||||||
|
blocking->Close( true );
|
||||||
|
|
||||||
|
return frame->OpenProjectFiles(
|
||||||
|
std::vector<wxString>( 1, wxString::FromUTF8( path.c_str() ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_BINDINGS(pl_editor) {
|
||||||
|
// Programmatic file open (preferred over UI automation from the web app).
|
||||||
|
function("kicadOpenFile", &kicadOpenFile);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
import { EXTENSION_TOOL, type Tool } from "@kicad-web/contract";
|
import {
|
||||||
|
EXTENSION_TOOL,
|
||||||
|
FILELESS_TOOLS,
|
||||||
|
TOOL_LABELS,
|
||||||
|
type Tool,
|
||||||
|
} from "@kicad-web/contract";
|
||||||
import { ArrowLeft, ExternalLink, Loader2 } from "lucide-react";
|
import { ArrowLeft, ExternalLink, Loader2 } from "lucide-react";
|
||||||
import { useProject } from "@/lib/api";
|
import { useProject } from "@/lib/api";
|
||||||
import { formatBytes } from "@/lib/utils";
|
import { formatBytes } from "@/lib/utils";
|
||||||
|
|
@ -44,13 +49,17 @@ export function ProjectDetailPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-6 flex flex-wrap gap-3">
|
<div className="mb-6 flex flex-wrap gap-3">
|
||||||
{/* Full reload (anchor) so Emscripten boots into a clean page. */}
|
{/* Standalone (file-less) tools — launched without a target file.
|
||||||
|
Full reload (anchor) so Emscripten boots into a clean page. */}
|
||||||
|
{[...FILELESS_TOOLS].map((tool) => (
|
||||||
<a
|
<a
|
||||||
|
key={tool}
|
||||||
className="text-sm underline underline-offset-4"
|
className="text-sm underline underline-offset-4"
|
||||||
href={`/p/${slug}/calculator/`}
|
href={`/p/${slug}/${tool}/`}
|
||||||
>
|
>
|
||||||
Open PCB Calculator
|
Open {TOOL_LABELS[tool]}
|
||||||
</a>
|
</a>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
|
|
@ -79,7 +88,7 @@ export function ProjectDetailPage() {
|
||||||
className="inline-flex shrink-0 items-center gap-1 rounded-md border px-3 py-1.5 text-sm hover:bg-accent"
|
className="inline-flex shrink-0 items-center gap-1 rounded-md border px-3 py-1.5 text-sm hover:bg-accent"
|
||||||
href={`/p/${slug}/${tool}/${f.path}`}
|
href={`/p/${slug}/${tool}/${f.path}`}
|
||||||
>
|
>
|
||||||
<ExternalLink size={14} /> Open in {tool}
|
<ExternalLink size={14} /> Open in {TOOL_LABELS[tool]}
|
||||||
</a>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ export const TOOL_ARGV0: Record<Tool, string> = {
|
||||||
pcbnew: "/usr/bin/pcbnew",
|
pcbnew: "/usr/bin/pcbnew",
|
||||||
eeschema: "/usr/bin/eeschema",
|
eeschema: "/usr/bin/eeschema",
|
||||||
calculator: "/usr/bin/pcb_calculator",
|
calculator: "/usr/bin/pcb_calculator",
|
||||||
|
pl_editor: "/usr/bin/pl_editor",
|
||||||
|
symbol_editor: "/usr/bin/symbol_editor",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -39,6 +41,8 @@ export const TOOL_NEEDS_CONFIG_SEED: Record<Tool, boolean> = {
|
||||||
pcbnew: true,
|
pcbnew: true,
|
||||||
eeschema: true,
|
eeschema: true,
|
||||||
calculator: true,
|
calculator: true,
|
||||||
|
pl_editor: true,
|
||||||
|
symbol_editor: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** KiCad user settings dir for this build (PATHS::GetUserSettingsPath()). */
|
/** KiCad user settings dir for this build (PATHS::GetUserSettingsPath()). */
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,41 @@
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
/** WASM tools that can be selected by the `:tool` URL segment. */
|
/** WASM tools that can be selected by the `:tool` URL segment. */
|
||||||
export const TOOLS = ["pcbnew", "eeschema", "calculator"] as const;
|
export const TOOLS = [
|
||||||
|
"pcbnew",
|
||||||
|
"eeschema",
|
||||||
|
"calculator",
|
||||||
|
"pl_editor",
|
||||||
|
"symbol_editor",
|
||||||
|
] as const;
|
||||||
export const toolSchema = z.enum(TOOLS);
|
export const toolSchema = z.enum(TOOLS);
|
||||||
export type Tool = z.infer<typeof toolSchema>;
|
export type Tool = z.infer<typeof toolSchema>;
|
||||||
|
|
||||||
|
/** Human-readable labels for tools (UI links, status text). */
|
||||||
|
export const TOOL_LABELS: Record<Tool, string> = {
|
||||||
|
pcbnew: "PCB Editor",
|
||||||
|
eeschema: "Schematic Editor",
|
||||||
|
calculator: "PCB Calculator",
|
||||||
|
pl_editor: "Drawing Sheet Editor",
|
||||||
|
symbol_editor: "Symbol Editor",
|
||||||
|
};
|
||||||
|
|
||||||
/** Default file-extension → tool mapping (the explicit URL segment always wins). */
|
/** Default file-extension → tool mapping (the explicit URL segment always wins). */
|
||||||
export const EXTENSION_TOOL: Record<string, Tool> = {
|
export const EXTENSION_TOOL: Record<string, Tool> = {
|
||||||
".kicad_pcb": "pcbnew",
|
".kicad_pcb": "pcbnew",
|
||||||
".kicad_sch": "eeschema",
|
".kicad_sch": "eeschema",
|
||||||
|
".kicad_wks": "pl_editor",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Tools that do not take a file (booted standalone). */
|
/**
|
||||||
export const FILELESS_TOOLS: ReadonlySet<Tool> = new Set<Tool>(["calculator"]);
|
* Tools that do not take a file (booted standalone). The calculator has no file
|
||||||
|
* concept; the symbol editor opens libraries through its own UI (its frame does
|
||||||
|
* not implement OpenProjectFiles), so we boot it standalone rather than auto-open.
|
||||||
|
*/
|
||||||
|
export const FILELESS_TOOLS: ReadonlySet<Tool> = new Set<Tool>([
|
||||||
|
"calculator",
|
||||||
|
"symbol_editor",
|
||||||
|
]);
|
||||||
|
|
||||||
export const projectSlugSchema = z
|
export const projectSlugSchema = z
|
||||||
.string()
|
.string()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue