From f06cbd5e5f1ac9a99c5df0f3bfc97fc0545181e1 Mon Sep 17 00:00:00 2001 From: Dante Loi Date: Wed, 24 Jun 2026 19:48:13 +0200 Subject: [PATCH 1/2] Modified the AGENTS.md and deleted the MINIMAL-FIDOSPECS.md --- AGENTS.md | 9 +-- MINIMAL-FIDOSPECS.md | 177 ------------------------------------------- 2 files changed, 3 insertions(+), 183 deletions(-) delete mode 100644 MINIMAL-FIDOSPECS.md diff --git a/AGENTS.md b/AGENTS.md index 95d6b35..249a895 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,20 +1,17 @@ # Agent Notes -FidoCadJS is a TypeScript browser port of FidoCadJ, built with Vite, Vitest, -and jsdom. Keep the project small, readable, and close to the Java reference -where that helps preserve FCL behavior. +FidoCadJS is a self-contained FidoCad parser and editor implemented in TypeScript, built with Vite, Vitest, +and jsdom. It was originaly developed as a browser port of FidoCadJ. ## Goals - Preserve compatibility with the FidoCadJ file format and drawing semantics. - Prefer clear TypeScript over broad abstractions. - Keep browser behavior deterministic and covered by focused tests. -- Use `~/FidoCadJ` only as read-only reference material. - Keep dependencies minimal; ask before adding any new package. ## Quality Rules -- Read `AGENTS.md` before meaningful work. - Write tests alongside new modules and meaningful behavior changes. - Run the relevant build or test command after non-trivial edits. - Comment only where code intent is not obvious from local context. @@ -44,4 +41,4 @@ where that helps preserve FCL behavior. - Use `npm test` for the main Vitest suite. - Use `npm run build` for production build validation. - Use Playwright or browser tests only when changing interactive UI behavior. - +- Prefer e2e tests to unit tests. diff --git a/MINIMAL-FIDOSPECS.md b/MINIMAL-FIDOSPECS.md deleted file mode 100644 index 193f6a8..0000000 --- a/MINIMAL-FIDOSPECS.md +++ /dev/null @@ -1,177 +0,0 @@ -# Minimal FidoCad Drawing Format - -A FidoCad drawing is a UTF-8 plain-text file made of one command per line. Each command is split into tokens using spaces. - -A file should start with: - -```fidocad id="yfxuky" -[FIDOCAD] -``` - -Example: - -```fidocad id="uf5p6s" -[FIDOCAD] -LI 10 10 80 10 0 -RV 20 20 80 60 0 -TY 25 70 4 3 0 0 0 * Hello -``` - -## Coordinates - -Coordinates are non-negative integers. - -The origin is at the top-left. The x axis grows to the right, and the y axis grows downward. - -Floating-point numbers are not used. - -## Layers - -Each drawable primitive ends with a layer number. - -Layers are integers from `0` to `15`. Use layer `0` by default. - -## Commands - -### Line - -```fidocad id="yqegrt" -LI x1 y1 x2 y2 layer -``` - -Draws a line from `(x1,y1)` to `(x2,y2)`. - -Example: - -```fidocad id="n1e0zs" -LI 10 10 80 10 0 -``` - -### Rectangle - -```fidocad id="6ep1xr" -RV x1 y1 x2 y2 layer -RP x1 y1 x2 y2 layer -``` - -`RV` draws an outline rectangle. -`RP` draws a filled rectangle. - -Example: - -```fidocad id="rb2qik" -RV 20 20 80 60 0 -``` - -### Ellipse - -```fidocad id="hq8o26" -EV x1 y1 x2 y2 layer -EP x1 y1 x2 y2 layer -``` - -`EV` draws an outline ellipse. -`EP` draws a filled ellipse. - -The ellipse is drawn inside the bounding box from `(x1,y1)` to `(x2,y2)`. - -Example: - -```fidocad id="6lvi3d" -EV 20 20 80 60 0 -``` - -### Polygon - -```fidocad id="udip6y" -PV x1 y1 x2 y2 ... xn yn layer -PP x1 y1 x2 y2 ... xn yn layer -``` - -`PV` draws an outline polygon. -`PP` draws a filled polygon. - -The last number is the layer. - -Example: - -```fidocad id="qev4dg" -PV 20 60 50 25 80 60 0 -``` - -### Text - -```fidocad id="jnt08x" -TY x y sizeY sizeX orientation style layer font text -``` - -Fields: - -```text id="b3a0re" -x y text position -sizeY sizeX text size -orientation rotation in degrees -style 0 plain, 1 bold, 2 italic, 3 bold italic -layer layer number -font font name, or * for the default font -text remaining text on the line -``` - -Example: - -```fidocad id="3yblf8" -TY 20 80 4 3 0 0 0 * Hello world -``` - -A simpler legacy text form is also allowed: - -```fidocad id="h14d3o" -TE x y text -``` - -Example: - -```fidocad id="8lqmx0" -TE 20 80 Hello world -``` - -## Grammar - -```text id="5olwzp" -file ::= "[FIDOCAD]"? command* - -command ::= line - | rectangle - | ellipse - | polygon - | text - -line ::= "LI" int int int int layer - -rectangle ::= ("RV" | "RP") int int int int layer - -ellipse ::= ("EV" | "EP") int int int int layer - -polygon ::= ("PV" | "PP") point point point* layer - -text ::= "TY" int int int int int int layer font text - | "TE" int int text - -point ::= int int -layer ::= int -font ::= "*" | font_name -text ::= remaining characters on the line -``` - -## Complete example - -```fidocad id="9r9a6e" -[FIDOCAD] -PV 20 60 50 25 80 60 0 -RV 25 60 75 105 0 -RV 45 80 55 105 0 -RV 32 70 42 82 0 -RV 58 70 68 82 0 -LI 10 105 90 105 0 -TY 28 112 4 3 0 0 0 * Little house -``` From 4d75dba364a3bc8b93810267cd95f2443e97c092 Mon Sep 17 00:00:00 2001 From: Dante Loi Date: Wed, 24 Jun 2026 20:20:30 +0200 Subject: [PATCH 2/2] refactor: remove dead code and Java-port scaffolding Simplify the codebase by deleting unused infrastructure that was ported from FidoCadJ (or stubbed for future work) but never wired to a consumer. No behavioral change; full test suite, production build, and export fixtures remain green. - Strip boilerplate @file/@author/@copyright headers from src and test (per AGENTS.md "no boilerplate file headers"). - Remove dead files: FontG, UndoState, LibraryListenerAdapter. - Remove the unused LibraryModel observer system (event classes, listener registration, and fire* plumbing); no production code ever registered a listener. - Delete 54 never-called accessor methods and the fields they orphaned, plus the unused createRenderCtx factory. - Delete the abandoned AbstractExport base class (no exporter extended it); keep the live escapeLatex helper, renamed to LatexEscape. Net: ~2,400 lines removed across 185 files. --- src/app.ts | 8 - src/circuit/CanvasManager.ts | 11 - src/circuit/CircuitPanel.ts | 52 ---- src/circuit/ContextMenuManager.ts | 11 - src/circuit/EditorFacade.ts | 20 -- src/circuit/GhostPreview.ts | 108 +++++-- src/circuit/ImageAsCanvas.ts | 11 - src/circuit/InputHandler.ts | 23 -- src/circuit/KeyboardHost.ts | 10 - src/circuit/MacroVectorizer.ts | 10 - src/circuit/PastePlacement.ts | 14 - src/circuit/Ruler.ts | 13 - src/circuit/ToolGhostHandler.ts | 13 +- src/circuit/controllers/AddElements.ts | 11 - .../controllers/ClipboardController.ts | 8 - src/circuit/controllers/EditorActions.ts | 18 -- src/circuit/controllers/ElementsEdtActions.ts | 77 ++--- src/circuit/controllers/KeyboardController.ts | 8 - src/circuit/controllers/LibraryLoader.ts | 8 - src/circuit/controllers/ParserActions.ts | 8 - src/circuit/controllers/SelectionActions.ts | 8 - src/circuit/controllers/UndoActions.ts | 19 -- src/circuit/model/DrawingModel.ts | 19 -- src/circuit/model/ProcessElementsInterface.ts | 8 - src/circuit/services.ts | 21 +- src/circuit/views/Drawing.ts | 12 - src/circuit/views/Export.ts | 17 +- src/export/AbstractExport.ts | 276 ------------------ src/export/ExportBitmap.ts | 17 -- src/export/ExportBitmapOptions.ts | 8 - src/export/ExportFacade.ts | 10 - src/export/ExportInterface.ts | 178 ++++++++--- src/export/ExportPDF.ts | 30 -- src/export/ExportPGF.ts | 10 +- src/export/ExportSVG.ts | 8 - src/export/ExportTikZ.ts | 10 +- src/export/LatexEscape.ts | 30 ++ src/export/PointPr.ts | 8 - src/export/SvgPathToPdf.ts | 12 - src/geom/DrawingSize.ts | 12 +- src/geom/GeometricDistances.ts | 8 - src/geom/MapCoordinates.ts | 8 - src/globals/Globals.ts | 8 - src/graphic/ColorInterface.ts | 8 - src/graphic/DimensionG.ts | 8 - src/graphic/FontG.ts | 19 -- src/graphic/GraphicsInterface.ts | 11 - src/graphic/MathLayout.ts | 17 -- src/graphic/PointDouble.ts | 8 - src/graphic/PointG.ts | 8 - src/graphic/PolygonInterface.ts | 8 - src/graphic/RectangleG.ts | 78 +++-- src/graphic/RenderCtx.ts | 16 - src/graphic/ShapeInterface.ts | 23 +- src/graphic/TeXMode.ts | 6 - src/graphic/TextInterface.ts | 10 - src/graphic/canvas/ColorCanvas.ts | 8 - src/graphic/canvas/GraphicsCanvas.ts | 26 +- src/graphic/canvas/PolygonCanvas.ts | 28 +- src/graphic/canvas/ShapeCanvas.ts | 23 +- src/graphic/canvas/TextCanvas.ts | 18 -- src/graphic/nil/GraphicsNull.ts | 17 -- src/i18n/AccessResources.ts | 8 - src/i18n/i18n.ts | 9 - src/layers/LayerDesc.ts | 48 +-- src/layers/StandardLayers.ts | 64 ++-- src/librarymodel/Category.ts | 39 ++- src/librarymodel/LibUtils.ts | 35 ++- src/librarymodel/Library.ts | 48 +-- src/librarymodel/LibraryFolder.ts | 20 -- src/librarymodel/LibraryModel.ts | 161 +++++----- src/librarymodel/UserLibraryStorage.ts | 11 - src/librarymodel/event/AddEvent.ts | 14 - src/librarymodel/event/KeyChangeEvent.ts | 15 - src/librarymodel/event/LibraryListener.ts | 20 -- .../event/LibraryListenerAdapter.ts | 21 -- src/librarymodel/event/RemoveEvent.ts | 14 - src/librarymodel/event/RenameEvent.ts | 15 - src/macropicker/MacroPicker.ts | 12 - src/macropicker/OperationPermissions.ts | 11 - src/primitives/Arrow.ts | 128 +++++--- src/primitives/GraphicPrimitive.ts | 19 -- src/primitives/MacroDesc.ts | 27 +- src/primitives/PrimitiveAdvText.ts | 8 - src/primitives/PrimitiveBezier.ts | 8 - src/primitives/PrimitiveComplexCurve.ts | 8 - src/primitives/PrimitiveConnection.ts | 8 - src/primitives/PrimitiveLine.ts | 8 - src/primitives/PrimitiveMacro.ts | 21 -- src/primitives/PrimitiveOval.ts | 8 - src/primitives/PrimitivePCBLine.ts | 8 - src/primitives/PrimitivePCBPad.ts | 8 - src/primitives/PrimitivePolygon.ts | 8 - src/primitives/PrimitiveRectangle.ts | 8 - src/settings/SettingsManager.ts | 8 - src/ui/ConfirmDialog.ts | 8 - src/ui/ContextMenu.ts | 8 - src/ui/DashStyleDropdown.ts | 10 - src/ui/DialogAbout.ts | 9 - src/ui/DialogLayer.ts | 9 - src/ui/DialogSymbolize.ts | 12 - src/ui/ExportDialog.ts | 9 - src/ui/InPlaceTextEditor.ts | 8 - src/ui/LayerDropdown.ts | 8 - src/ui/MenuBar.ts | 8 - src/ui/OptionsDialog.ts | 8 - src/ui/PromptDialog.ts | 8 - src/ui/PropertiesPanelController.ts | 8 - src/ui/Toast.ts | 11 - src/ui/ToolbarController.ts | 8 - src/undo/UndoState.ts | 21 -- src/vendor/mathjax-shim.ts | 11 - test/circuit/align-distribute.test.ts | 9 - test/circuit/controllers/add-elements.test.ts | 8 - .../controllers/editor-actions.test.ts | 19 -- .../controllers/library-loader.test.ts | 9 - .../controllers/selection-actions.test.ts | 8 - test/circuit/copy-all-as-primitives.test.ts | 11 - test/circuit/image-background.test.ts | 9 - test/circuit/keyboard-shortcuts.test.ts | 8 - test/circuit/listener-leaks.test.ts | 8 - test/circuit/macro-rotation.test.ts | 10 - test/circuit/model/drawing-model.test.ts | 8 - test/circuit/placement.test.ts | 8 - test/circuit/ruler.test.ts | 8 - test/circuit/views/export.test.ts | 18 -- test/e2e/app-loads.test.ts | 8 - test/e2e/clipboard.test.ts | 8 - test/e2e/drawing-tools.test.ts | 8 - test/e2e/edge-cases.test.ts | 8 - test/e2e/export-bitmap-render.test.ts | 11 - test/e2e/export-png.test.ts | 17 -- test/e2e/export-svg-pixel-parity.test.ts | 26 -- test/e2e/export.test.ts | 256 ++++++++-------- test/e2e/file-operations.test.ts | 8 - test/e2e/grid-snap.test.ts | 8 - test/e2e/keyboard-e2e.test.ts | 8 - test/e2e/layer-dialog.test.ts | 12 - test/e2e/macro-library.test.ts | 8 - test/e2e/math-render.test.ts | 10 - test/e2e/menu-bar.test.ts | 8 - test/e2e/selection-and-transform.test.ts | 8 - test/e2e/undo-redo.test.ts | 8 - test/e2e/utils.ts | 8 - test/e2e/zoom-pan.test.ts | 8 - test/export/export-adversarial.test.ts | 9 - test/export/export-bitmap.test.ts | 13 - test/export/export-dialog.test.ts | 14 - test/export/export-math.test.ts | 9 - test/export/export-pdf.test.ts | 8 - test/export/export-pgf.fixtures.test.ts | 8 - test/export/export-pgf.test.ts | 8 - test/export/export-svg.fixtures.test.ts | 17 -- test/export/export-svg.java-parity.test.ts | 22 -- test/export/export-svg.test.ts | 8 - test/export/export-tikz.fixtures.test.ts | 12 - test/export/export-tikz.test.ts | 8 - test/export/fixtures/helpers.ts | 15 - test/export/latex-escape.test.ts | 10 +- test/geom/drawing-size.test.ts | 12 - test/geom/geometric-distances.test.ts | 8 - test/geom/map-coordinates.test.ts | 8 - test/globals/globals.test.ts | 8 - test/graphic/color-canvas.test.ts | 9 - test/graphic/dash-scaling.test.ts | 9 - test/graphic/math-layout.test.ts | 9 - test/i18n/routing.test.ts | 10 - test/layers/layer-desc.test.ts | 10 +- test/librarymodel/library-model.test.ts | 82 ++---- test/macropicker/macro-picker.test.ts | 9 - test/macropicker/preview-connection.test.ts | 14 - test/parser/parser-adversarial.test.ts | 20 -- test/parser/parser-global-state.test.ts | 9 - test/parser/primitive-round-trip.test.ts | 24 +- test/primitives/complex-curve-fixes.test.ts | 10 - test/primitives/primitive-edge-cases.test.ts | 28 +- test/settings/settings-manager.test.ts | 97 +++--- test/setup.ts | 5 - test/ui/confirm-dialog.test.ts | 9 - test/ui/menubar-open-libraries.test.ts | 14 - test/ui/prompt-dialog.test.ts | 9 - test/ui/properties-batch.test.ts | 10 - test/ui/toolbar-controller.test.ts | 10 - test/undo/undo-actions.test.ts | 62 ++-- test/undo/undo-state.test.ts | 34 --- 185 files changed, 946 insertions(+), 2852 deletions(-) delete mode 100644 src/export/AbstractExport.ts create mode 100644 src/export/LatexEscape.ts delete mode 100644 src/graphic/FontG.ts delete mode 100644 src/librarymodel/event/AddEvent.ts delete mode 100644 src/librarymodel/event/KeyChangeEvent.ts delete mode 100644 src/librarymodel/event/LibraryListener.ts delete mode 100644 src/librarymodel/event/LibraryListenerAdapter.ts delete mode 100644 src/librarymodel/event/RemoveEvent.ts delete mode 100644 src/librarymodel/event/RenameEvent.ts delete mode 100644 src/undo/UndoState.ts delete mode 100644 test/undo/undo-state.test.ts diff --git a/src/app.ts b/src/app.ts index b2ed4b5..9adf7b7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,11 +1,3 @@ -/** - * @file app.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Application entry point & UI bootstrap - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { CircuitPanel } from './circuit/CircuitPanel.js'; import { loadStandardLibraries } from './circuit/controllers/LibraryLoader.js'; import { LibraryModel } from './librarymodel/LibraryModel.js'; diff --git a/src/circuit/CanvasManager.ts b/src/circuit/CanvasManager.ts index e72a9a8..d7fbaee 100644 --- a/src/circuit/CanvasManager.ts +++ b/src/circuit/CanvasManager.ts @@ -1,14 +1,3 @@ -/** - * @file CanvasManager.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Canvas element lifecycle manager extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Handles canvas creation, ResizeObserver for responsive sizing, - * device-pixel-ratio change detection, and cleanup on destroy. - * Extracted during agent-friendly refactoring (P2.1). - */ - export class CanvasManager { readonly canvas: HTMLCanvasElement; private container: HTMLElement; diff --git a/src/circuit/CircuitPanel.ts b/src/circuit/CircuitPanel.ts index 1a10453..a2bfd10 100644 --- a/src/circuit/CircuitPanel.ts +++ b/src/circuit/CircuitPanel.ts @@ -1,11 +1,3 @@ -/** - * @file CircuitPanel.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Main editor panel controller coordinating input, model, and views - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { DrawingModel } from './model/DrawingModel.js'; import { ParserActions } from './controllers/ParserActions.js'; import { MapCoordinates } from '../geom/MapCoordinates.js'; @@ -372,10 +364,6 @@ export class CircuitPanel implements KeyboardHost, EditorFacade { this.onZoomChange?.(); } - getZoom(): number { - return this.mapCoordinates.getXMagnitude(); - } - isSnapActive(): boolean { return this.mapCoordinates.getSnap(); } @@ -990,46 +978,6 @@ export class CircuitPanel implements KeyboardHost, EditorFacade { isImageAttached(): boolean { return this.model.getImgCanvas().isAttached(); } - - getImageAlpha(): number { - return this.model.getImgCanvas().getAlpha(); - } - - setImageAlpha(a: number): void { - this.model.getImgCanvas().setAlpha(a); - this.model.setModified(true); - this.render(); - } - - getImageScale(): number { - return this.model.getImgCanvas().getScale(); - } - - setImageScale(s: number): void { - this.model.getImgCanvas().setScale(s); - this.model.setModified(true); - this.render(); - } - - getImageX(): number { - return this.model.getImgCanvas().getX(); - } - - setImageX(x: number): void { - this.model.getImgCanvas().setX(x); - this.model.setModified(true); - this.render(); - } - - getImageY(): number { - return this.model.getImgCanvas().getY(); - } - - setImageY(y: number): void { - this.model.getImgCanvas().setY(y); - this.model.setModified(true); - this.render(); - } } function hexToRgb(hex: string): { r: number; g: number; b: number } { diff --git a/src/circuit/ContextMenuManager.ts b/src/circuit/ContextMenuManager.ts index 4fb0539..99977c1 100644 --- a/src/circuit/ContextMenuManager.ts +++ b/src/circuit/ContextMenuManager.ts @@ -1,14 +1,3 @@ -/** - * @file ContextMenuManager.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Context menu management extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Builds and shows the right-click context menu, handles add/remove - * node actions. Extracted mechanically during agent-friendly refactoring - * (P2.6). - */ - import { getString } from '../i18n/i18n.js'; import { ContextMenu } from '../ui/ContextMenu.js'; import { PrimitiveMacro } from '../primitives/PrimitiveMacro.js'; diff --git a/src/circuit/EditorFacade.ts b/src/circuit/EditorFacade.ts index 2d8ca84..157102f 100644 --- a/src/circuit/EditorFacade.ts +++ b/src/circuit/EditorFacade.ts @@ -1,15 +1,3 @@ -/** - * @file EditorFacade.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Narrow interface that UI components consume instead of concrete CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Extracted during agent-friendly refactoring. This interface exposes only - * the methods and properties that UI classes (ToolbarController, MenuBar, - * PropertiesPanelController, ExportDialog, SettingsManager) actually call. - * CircuitPanel implements this interface so UI code can be mocked in tests. - */ - import type { MapCoordinates } from '../geom/MapCoordinates.js'; import type { DrawingModel } from './model/DrawingModel.js'; import type { LayerDesc } from '../layers/LayerDesc.js'; @@ -112,12 +100,4 @@ export interface EditorFacade { attachImage(file: File): Promise; detachImage(): void; isImageAttached(): boolean; - getImageAlpha(): number; - setImageAlpha(a: number): void; - getImageScale(): number; - setImageScale(s: number): void; - getImageX(): number; - setImageX(x: number): void; - getImageY(): number; - setImageY(y: number): void; } diff --git a/src/circuit/GhostPreview.ts b/src/circuit/GhostPreview.ts index 3a8132d..7e7a8e3 100644 --- a/src/circuit/GhostPreview.ts +++ b/src/circuit/GhostPreview.ts @@ -1,14 +1,3 @@ -/** - * @file GhostPreview.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Ghost preview generation for drawing tools, extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Generates transparent "ghost" primitives that follow the cursor while - * a drawing tool is active. Uses a strategy pattern — each tool registers - * its own ghost handler, eliminating the central switch/case. - */ - import { ElementsEdtActions } from './controllers/ElementsEdtActions.js'; import type { DrawingModel } from './model/DrawingModel.js'; import type { GraphicPrimitive } from '../primitives/GraphicPrimitive.js'; @@ -31,8 +20,19 @@ import { StandardLayers } from '../layers/StandardLayers.js'; const lineGhost: ToolGhostHandler = (ctx) => { if (ctx.clickNum === 1) { return new PrimitiveLine( - ctx.xpoly[1], ctx.ypoly[1], ctx.lx, ctx.ly, ctx.layer, - false, false, 0, 3, 2, 0, ctx.font, ctx.fontSize + ctx.xpoly[1], + ctx.ypoly[1], + ctx.lx, + ctx.ly, + ctx.layer, + false, + false, + 0, + 3, + 2, + 0, + ctx.font, + ctx.fontSize, ); } return null; @@ -41,11 +41,23 @@ const lineGhost: ToolGhostHandler = (ctx) => { const bezierGhost: ToolGhostHandler = (ctx) => { if (ctx.clickNum === 3) { return new PrimitiveBezier( - ctx.xpoly[1], ctx.ypoly[1], - ctx.xpoly[2], ctx.ypoly[2], - ctx.xpoly[3], ctx.ypoly[3], - ctx.lx, ctx.ly, - ctx.layer, false, false, 0, 3, 2, 0, ctx.font, ctx.fontSize + ctx.xpoly[1], + ctx.ypoly[1], + ctx.xpoly[2], + ctx.ypoly[2], + ctx.xpoly[3], + ctx.ypoly[3], + ctx.lx, + ctx.ly, + ctx.layer, + false, + false, + 0, + 3, + 2, + 0, + ctx.font, + ctx.fontSize, ); } else if (ctx.clickNum >= 1 && ctx.clickNum < 3) { const ctrlPoly = new PrimitivePolygon(false, ctx.layer, 0, ctx.font, ctx.fontSize); @@ -61,7 +73,15 @@ const bezierGhost: ToolGhostHandler = (ctx) => { const rectangleGhost: ToolGhostHandler = (ctx) => { if (ctx.clickNum === 1) { return new PrimitiveRectangle( - ctx.xpoly[1], ctx.ypoly[1], ctx.lx, ctx.ly, false, ctx.layer, 0, ctx.font, ctx.fontSize + ctx.xpoly[1], + ctx.ypoly[1], + ctx.lx, + ctx.ly, + false, + ctx.layer, + 0, + ctx.font, + ctx.fontSize, ); } return null; @@ -70,7 +90,15 @@ const rectangleGhost: ToolGhostHandler = (ctx) => { const ellipseGhost: ToolGhostHandler = (ctx) => { if (ctx.clickNum === 1) { return new PrimitiveOval( - ctx.xpoly[1], ctx.ypoly[1], ctx.lx, ctx.ly, false, ctx.layer, 0, ctx.font, ctx.fontSize + ctx.xpoly[1], + ctx.ypoly[1], + ctx.lx, + ctx.ly, + false, + ctx.layer, + 0, + ctx.font, + ctx.fontSize, ); } return null; @@ -91,9 +119,17 @@ const polygonGhost: ToolGhostHandler = (ctx) => { const complexCurveGhost: ToolGhostHandler = (ctx) => { if (ctx.clickNum >= 1) { const cc = new PrimitiveComplexCurve( - false, false, ctx.layer, - false, false, 0, 3, 2, 0, - ctx.font, ctx.fontSize + false, + false, + ctx.layer, + false, + false, + 0, + 3, + 2, + 0, + ctx.font, + ctx.fontSize, ); for (let i = 1; i <= ctx.clickNum; i++) { cc.addPoint(ctx.xpoly[i], ctx.ypoly[i]); @@ -107,19 +143,29 @@ const complexCurveGhost: ToolGhostHandler = (ctx) => { const pcbPadGhost: ToolGhostHandler = (ctx, edt) => { const ae = edt.getAddElements(); return new PrimitivePCBPad( - ctx.lx, ctx.ly, - ae.getPcbPadSizeX(), ae.getPcbPadSizeY(), - ae.getPcbPadDrill(), ae.getPcbPadStyle(), - ctx.layer, ctx.font, ctx.fontSize + ctx.lx, + ctx.ly, + ae.getPcbPadSizeX(), + ae.getPcbPadSizeY(), + ae.getPcbPadDrill(), + ae.getPcbPadStyle(), + ctx.layer, + ctx.font, + ctx.fontSize, ); }; const pcbLineGhost: ToolGhostHandler = (ctx, edt) => { if (ctx.clickNum === 1) { return new PrimitivePCBLine( - ctx.xpoly[1], ctx.ypoly[1], ctx.lx, ctx.ly, + ctx.xpoly[1], + ctx.ypoly[1], + ctx.lx, + ctx.ly, edt.getAddElements().getPcbThickness(), - ctx.layer, ctx.font, ctx.fontSize + ctx.layer, + ctx.font, + ctx.fontSize, ); } return null; @@ -139,7 +185,7 @@ const macroGhost: ToolGhostHandler = (ctx, edt, model) => { model.getLibrary(), StandardLayers.createEditingLayerArray(), ctx.font, - ctx.fontSize + ctx.fontSize, ); macroPreview.virtualPoint[0]!.x = ctx.lx; macroPreview.virtualPoint[0]!.y = ctx.ly; @@ -188,7 +234,7 @@ export class GhostPreview { ly: number, tool: number, edt: ElementsEdtActions, - model: DrawingModel + model: DrawingModel, ): GraphicPrimitive | null { const handler = registry.get(tool); if (!handler) return null; diff --git a/src/circuit/ImageAsCanvas.ts b/src/circuit/ImageAsCanvas.ts index dab0552..a67e02b 100644 --- a/src/circuit/ImageAsCanvas.ts +++ b/src/circuit/ImageAsCanvas.ts @@ -1,14 +1,3 @@ -/** - * @file ImageAsCanvas.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Image-to-canvas rendering utility for background image attachment. - * Stores a raster image, its position, scale, and alpha, and - * renders it behind all drawing layers. Mirrors FidoCadJ's - * DialogAttachImage / ImageAsCanvas concept. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { MapCoordinates } from '../geom/MapCoordinates.js'; import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; diff --git a/src/circuit/InputHandler.ts b/src/circuit/InputHandler.ts index a8ad370..0970ae3 100644 --- a/src/circuit/InputHandler.ts +++ b/src/circuit/InputHandler.ts @@ -1,15 +1,3 @@ -/** - * @file InputHandler.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Mouse/wheel event handler and gesture state machine extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Handles all pointer events: selection (click, rubber-band, drag-handle, - * move-all), drawing tools (click dispatch, ghost preview), pan (middle - * button, hand tool), zoom (tool, wheel), and double-click detection. - * Extracted during agent-friendly refactoring (P2.5). - */ - import { MapCoordinates } from '../geom/MapCoordinates.js'; import type { DrawingModel } from './model/DrawingModel.js'; import { Ruler } from './Ruler.js'; @@ -238,17 +226,6 @@ export class InputHandler { this._ghostPrimitive = p; } - /** Set drag handle state directly (for external reset). */ - setDragHandle(index: number, prim: GraphicPrimitive | null): void { - this._dragHandleIndex = index; - this._dragHandlePrim = prim; - } - - /** Set selRectActive directly (for external reset). */ - setSelRectActive(active: boolean): void { - this._selRectActive = active; - } - // ═══════════════════════════════════════════════════════════════════════════ // Event handlers (attached by CircuitPanel) // ═══════════════════════════════════════════════════════════════════════════ diff --git a/src/circuit/KeyboardHost.ts b/src/circuit/KeyboardHost.ts index 8b8504a..f21633b 100644 --- a/src/circuit/KeyboardHost.ts +++ b/src/circuit/KeyboardHost.ts @@ -1,13 +1,3 @@ -/** - * @file KeyboardHost.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Interface that the host (CircuitPanel) must satisfy for KeyboardController - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Extracted from KeyboardController.ts during agent-friendly refactoring - * to provide a standalone, importable contract. - */ - import type { MenuBar } from '../ui/MenuBar.js'; /** diff --git a/src/circuit/MacroVectorizer.ts b/src/circuit/MacroVectorizer.ts index d2bf13b..4707a62 100644 --- a/src/circuit/MacroVectorizer.ts +++ b/src/circuit/MacroVectorizer.ts @@ -1,13 +1,3 @@ -/** - * @file MacroVectorizer.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Macro vectorization logic extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Converts a selected macro instance back into individual primitives. - * Extracted mechanically during agent-friendly refactoring (P2.7). - */ - import { DrawingModel } from './model/DrawingModel.js'; import { ParserActions } from './controllers/ParserActions.js'; import { PrimitiveMacro } from '../primitives/PrimitiveMacro.js'; diff --git a/src/circuit/PastePlacement.ts b/src/circuit/PastePlacement.ts index 0022a1a..f196a63 100644 --- a/src/circuit/PastePlacement.ts +++ b/src/circuit/PastePlacement.ts @@ -1,17 +1,3 @@ -/** - * @file PastePlacement.ts - * @author Dante Loi - * @date 2026-06-04 - * @brief Interactive "place where you click" state for clipboard paste - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Holds a transient, off-screen drawing parsed from the clipboard so a - * green ghost of the pasted content can follow the cursor until the user - * confirms its position with a click (or Esc/Enter to drop in place). - * The original clipboard text is preserved verbatim; on commit the host - * re-parses it into the real model and offsets it by the chosen amount, - * exactly mirroring the non-interactive paste path so undo stays simple. - */ - import { DrawingModel } from './model/DrawingModel.js'; import { ParserActions } from './controllers/ParserActions.js'; import { Drawing } from './views/Drawing.js'; diff --git a/src/circuit/Ruler.ts b/src/circuit/Ruler.ts index 5800727..a02c0f0 100644 --- a/src/circuit/Ruler.ts +++ b/src/circuit/Ruler.ts @@ -1,16 +1,3 @@ -/** - * @file Ruler.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief On-canvas measuring ruler, ported from FidoCadJ 0.24.9 - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Draws a green ruler with tick marks between a start and an end point - * while the user drags with the right mouse button, and shows the - * measured length both in FidoCadJ logical units and in millimetres - * (one logical unit = 127 microns). The geometry mirrors FidoCadJ's - * fidocadj.circuit.Ruler so measurements match the desktop editor. - */ - import type { MapCoordinates } from '../geom/MapCoordinates.js'; /** One FidoCadJ logical unit expressed in millimetres (127 microns). */ diff --git a/src/circuit/ToolGhostHandler.ts b/src/circuit/ToolGhostHandler.ts index e44f5ea..13d8922 100644 --- a/src/circuit/ToolGhostHandler.ts +++ b/src/circuit/ToolGhostHandler.ts @@ -1,14 +1,3 @@ -/** - * @file ToolGhostHandler.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Strategy interface for tool-specific ghost preview generation - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Each drawing tool provides its own ghost primitive creation logic - * via this interface. GhostPreview delegates to the active tool's - * handler instead of using a central switch/case. - */ - import type { GraphicPrimitive } from '../primitives/GraphicPrimitive.js'; import type { ElementsEdtActions } from './controllers/ElementsEdtActions.js'; import type { DrawingModel } from './model/DrawingModel.js'; @@ -42,5 +31,5 @@ export interface GhostContext { export type ToolGhostHandler = ( ctx: GhostContext, edt: ElementsEdtActions, - model: DrawingModel + model: DrawingModel, ) => GraphicPrimitive | null; diff --git a/src/circuit/controllers/AddElements.ts b/src/circuit/controllers/AddElements.ts index dd4c950..5eebf0d 100644 --- a/src/circuit/controllers/AddElements.ts +++ b/src/circuit/controllers/AddElements.ts @@ -1,11 +1,3 @@ -/** - * @file AddElements.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Primitive creation handlers for each drawing tool - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { DrawingModel } from '../model/DrawingModel.js'; import { UndoActions } from './UndoActions.js'; import { SelectionActions } from './SelectionActions.js'; @@ -384,9 +376,6 @@ export class AddElements { getPcbPadSizeY(): number { return this.pcbPadSizeY; } - setPcbPadStyle(s: number): void { - this.pcbPadStyle = s; - } getPcbPadStyle(): number { return this.pcbPadStyle; } diff --git a/src/circuit/controllers/ClipboardController.ts b/src/circuit/controllers/ClipboardController.ts index ca53169..956ef53 100644 --- a/src/circuit/controllers/ClipboardController.ts +++ b/src/circuit/controllers/ClipboardController.ts @@ -1,11 +1,3 @@ -/** - * @file ClipboardController.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Clipboard operations (copy, cut, paste, duplicate) extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { SelectionActions } from './SelectionActions.js'; import type { ParserActions } from './ParserActions.js'; import type { EditorActions } from './EditorActions.js'; diff --git a/src/circuit/controllers/EditorActions.ts b/src/circuit/controllers/EditorActions.ts index fd1b258..b4d7f43 100644 --- a/src/circuit/controllers/EditorActions.ts +++ b/src/circuit/controllers/EditorActions.ts @@ -1,11 +1,3 @@ -/** - * @file EditorActions.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief High-level editor operations (open, save, clipboard, zoom) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { DrawingModel } from '../model/DrawingModel.js'; import { UndoActions } from './UndoActions.js'; import { SelectionActions } from './SelectionActions.js'; @@ -29,16 +21,6 @@ export class EditorActions { this.selTolerance = 10; } - /** Set selection tolerance in pixels */ - setSelectionTolerance(s: number): void { - this.selTolerance = s; - } - - /** Get selection tolerance in pixels */ - getSelectionTolerance(): number { - return this.selTolerance; - } - /** Rotate all selected primitives 90 degrees clockwise */ rotateAllSelected(): void { const first = this.selectionActions.getFirstSelectedPrimitive(); diff --git a/src/circuit/controllers/ElementsEdtActions.ts b/src/circuit/controllers/ElementsEdtActions.ts index 4bbbb97..342f2fe 100644 --- a/src/circuit/controllers/ElementsEdtActions.ts +++ b/src/circuit/controllers/ElementsEdtActions.ts @@ -1,11 +1,3 @@ -/** - * @file ElementsEdtActions.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Element editing action dispatcher for tool-specific behaviour - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { DrawingModel } from '../model/DrawingModel.js'; import { UndoActions } from './UndoActions.js'; import { SelectionActions } from './SelectionActions.js'; @@ -60,7 +52,8 @@ export class ElementsEdtActions { public static readonly NPOLY = 256; // Callbacks - public onTextEditRequested: ((prim: PrimitiveAdvText, sx: number, sy: number) => void) | null = null; + public onTextEditRequested: ((prim: PrimitiveAdvText, sx: number, sy: number) => void) | null = + null; public onExistingTextEditRequested: ((prim: PrimitiveAdvText) => void) | null = null; public onPropertiesRequested: ((prim: GraphicPrimitive) => void) | null = null; public onContextMenuRequested: ((sx: number, sy: number) => void) | null = null; @@ -69,7 +62,7 @@ export class ElementsEdtActions { model: DrawingModel, selectionActions: SelectionActions, undoActions: UndoActions | null, - editorActions: EditorActions + editorActions: EditorActions, ) { this.model = model; this.undoActions = undoActions; @@ -87,11 +80,6 @@ export class ElementsEdtActions { return this.addElements; } - /** Check if currently entering a macro */ - isEnteringMacro(): boolean { - return this.primEdit instanceof PrimitiveMacro; - } - /** Set the current tool state */ setState(s: number, macro: string = ''): void { this.actionSelected = s; @@ -129,10 +117,11 @@ export class ElementsEdtActions { */ handleClick( cs: MapCoordinates, - x: number, y: number, + x: number, + y: number, button3: boolean, toggle: boolean, - doubleClick: boolean + doubleClick: boolean, ): boolean { let repaint = false; @@ -185,17 +174,13 @@ export class ElementsEdtActions { this.addElements.addConnection( cs.unmapXsnap(x), cs.unmapYsnap(y), - this.currentLayer + this.currentLayer, ); repaint = true; break; case ElementsEdtActions.PCB_PAD: - this.addElements.addPCBPad( - cs.unmapXsnap(x), - cs.unmapYsnap(y), - this.currentLayer - ); + this.addElements.addPCBPad(cs.unmapXsnap(x), cs.unmapYsnap(y), this.currentLayer); repaint = true; break; @@ -211,7 +196,7 @@ export class ElementsEdtActions { this.ypoly, this.currentLayer, ++this.clickNumber, - button3 + button3, ); repaint = true; } @@ -228,11 +213,13 @@ export class ElementsEdtActions { const newText = new PrimitiveAdvText( cs.unmapXsnap(x), cs.unmapYsnap(y), - 3, 4, + 3, + 4, this.model.getTextFont(), - 0, 0, + 0, + 0, 'String', - this.currentLayer + this.currentLayer, ); this.selectionActions.setSelectionAll(false); this.model.addPrimitive(newText, true, this.undoActions); @@ -257,7 +244,7 @@ export class ElementsEdtActions { this.xpoly, this.ypoly, this.currentLayer, - ++this.clickNumber + ++this.clickNumber, ); } break; @@ -269,7 +256,7 @@ export class ElementsEdtActions { this.currentLayer, 0, this.model.getTextFont(), - this.model.getTextFontSize() + this.model.getTextFontSize(), ); for (let i = 1; i <= this.clickNumber; i++) { poly.addPoint(this.xpoly[i], this.ypoly[i]); @@ -294,9 +281,14 @@ export class ElementsEdtActions { false, false, this.currentLayer, - false, false, 0, 3, 2, 0, + false, + false, + 0, + 3, + 2, + 0, this.model.getTextFont(), - this.model.getTextFontSize() + this.model.getTextFontSize(), ); for (let i = 1; i <= this.clickNumber; i++) { compc.addPoint(this.xpoly[i], this.ypoly[i]); @@ -324,7 +316,7 @@ export class ElementsEdtActions { this.ypoly, this.currentLayer, ++this.clickNumber, - toggle && this.clickNumber > 0 + toggle && this.clickNumber > 0, ); repaint = true; break; @@ -338,7 +330,7 @@ export class ElementsEdtActions { this.ypoly, this.currentLayer, ++this.clickNumber, - toggle && this.clickNumber > 0 + toggle && this.clickNumber > 0, ); repaint = true; break; @@ -356,7 +348,7 @@ export class ElementsEdtActions { this.currentLayer, ++this.clickNumber, button3, - this.addElements.getPcbThickness() + this.addElements.getPcbThickness(), ); repaint = true; } @@ -369,7 +361,7 @@ export class ElementsEdtActions { cs.unmapYsnap(y), this.selectionActions, this.primEdit, - this.macroKey + this.macroKey, ); repaint = true; break; @@ -377,19 +369,4 @@ export class ElementsEdtActions { return repaint; } - - /** Get current selection state (active tool) */ - getSelectionState(): number { - return this.actionSelected; - } - - /** Set current editing primitive */ - setPrimEdit(gp: GraphicPrimitive | null): void { - this.primEdit = gp; - } - - /** Get current editing primitive */ - getPrimEdit(): GraphicPrimitive | null { - return this.primEdit; - } } diff --git a/src/circuit/controllers/KeyboardController.ts b/src/circuit/controllers/KeyboardController.ts index 1b5c386..da9cf69 100644 --- a/src/circuit/controllers/KeyboardController.ts +++ b/src/circuit/controllers/KeyboardController.ts @@ -1,11 +1,3 @@ -/** - * @file KeyboardController.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Keyboard shortcut handler extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { ElementsEdtActions } from './ElementsEdtActions.js'; import type { ClipboardController } from './ClipboardController.js'; import type { KeyboardHost } from '../KeyboardHost.js'; diff --git a/src/circuit/controllers/LibraryLoader.ts b/src/circuit/controllers/LibraryLoader.ts index 7f0c060..8f52f67 100644 --- a/src/circuit/controllers/LibraryLoader.ts +++ b/src/circuit/controllers/LibraryLoader.ts @@ -1,11 +1,3 @@ -/** - * @file LibraryLoader.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Fetches and loads standard FCL component libraries from public/lib/. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ParserActions } from './ParserActions.js'; import { getCurrentLocale } from '../../i18n/i18n.js'; diff --git a/src/circuit/controllers/ParserActions.ts b/src/circuit/controllers/ParserActions.ts index 41af24c..ac3c7ee 100644 --- a/src/circuit/controllers/ParserActions.ts +++ b/src/circuit/controllers/ParserActions.ts @@ -1,11 +1,3 @@ -/** - * @file ParserActions.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief FCD/FCL format parser and serializer - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { DrawingModel } from '../model/DrawingModel.js'; import { Globals } from '../../globals/Globals.js'; import { LayerDesc } from '../../layers/LayerDesc.js'; diff --git a/src/circuit/controllers/SelectionActions.ts b/src/circuit/controllers/SelectionActions.ts index d85c7a8..e3fed83 100644 --- a/src/circuit/controllers/SelectionActions.ts +++ b/src/circuit/controllers/SelectionActions.ts @@ -1,11 +1,3 @@ -/** - * @file SelectionActions.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Selection query and bulk-mutation helpers - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { DrawingModel } from '../model/DrawingModel.js'; import { GraphicPrimitive } from '../../primitives/GraphicPrimitive.js'; import { PrimitiveMacro } from '../../primitives/PrimitiveMacro.js'; diff --git a/src/circuit/controllers/UndoActions.ts b/src/circuit/controllers/UndoActions.ts index a0a7543..be5390d 100644 --- a/src/circuit/controllers/UndoActions.ts +++ b/src/circuit/controllers/UndoActions.ts @@ -1,11 +1,3 @@ -/** - * @file UndoActions.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Undo/redo action interface for editor controllers - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { ParserActions } from './ParserActions.js'; /** @@ -18,7 +10,6 @@ export class UndoActions { private redoStack: string[]; private readonly maxUndoLevels: number = 100; private isModified: boolean = false; - private openFileName: string | null = null; constructor(parserActions: ParserActions) { this.parserActions = parserActions; @@ -91,14 +82,4 @@ export class UndoActions { setModified(state: boolean): void { this.isModified = state; } - - /** Set open file name */ - setOpenFileName(name: string | null): void { - this.openFileName = name; - } - - /** Get open file name */ - getOpenFileName(): string | null { - return this.openFileName; - } } diff --git a/src/circuit/model/DrawingModel.ts b/src/circuit/model/DrawingModel.ts index ba13646..a36dc55 100644 --- a/src/circuit/model/DrawingModel.ts +++ b/src/circuit/model/DrawingModel.ts @@ -1,11 +1,3 @@ -/** - * @file DrawingModel.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Core drawing data model holding primitives, layers, and macro library - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicPrimitive } from '../../primitives/GraphicPrimitive.js'; import type { LayerDesc } from '../../layers/LayerDesc.js'; import type { MacroDesc } from '../../primitives/MacroDesc.js'; @@ -80,14 +72,6 @@ export class DrawingModel { } } - setTextFont(f: string, tsize: number, ua: { setModified(b: boolean): void } | null): void { - this.defaultTextFont = f; - this.defaultTextFontSize = tsize; - for (const g of this.primitiveVector) g.setMacroFont(f, tsize); - this.changedFlag = true; - if (ua !== null) ua.setModified(true); - } - getTextFont(): string { return this.defaultTextFont; } @@ -179,9 +163,6 @@ export class DrawingModel { getDrawOnlyLayer(): number { return this.drawOnlyLayerVal; } - setImgCanvas(ic: ImageAsCanvas): void { - this.imgCanvas = ic; - } getImgCanvas(): ImageAsCanvas { return this.imgCanvas; } diff --git a/src/circuit/model/ProcessElementsInterface.ts b/src/circuit/model/ProcessElementsInterface.ts index 4041f39..019983e 100644 --- a/src/circuit/model/ProcessElementsInterface.ts +++ b/src/circuit/model/ProcessElementsInterface.ts @@ -1,11 +1,3 @@ -/** - * @file ProcessElementsInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Interface for element processing visitors - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicPrimitive } from '../../primitives/GraphicPrimitive.js'; export interface ProcessElementsInterface { diff --git a/src/circuit/services.ts b/src/circuit/services.ts index f21ab1b..f9a202d 100644 --- a/src/circuit/services.ts +++ b/src/circuit/services.ts @@ -1,14 +1,3 @@ -/** - * @file services.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Service container / factory for wiring editor dependencies - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Central factory function that creates and wires all controllers, - * making the dependency graph explicit. Enables unit testing of - * individual controllers without constructing the full CircuitPanel. - */ - import { DrawingModel } from './model/DrawingModel.js'; import { StandardLayers } from '../layers/StandardLayers.js'; import { MapCoordinates } from '../geom/MapCoordinates.js'; @@ -49,11 +38,13 @@ export function createEditorServices(): EditorServices { const selectionActions = new SelectionActions(model); const undoActions = new UndoActions(parserActions); const editorActions = new EditorActions(model, selectionActions, undoActions); - const elementsEdt = new ElementsEdtActions( - model, selectionActions, undoActions, editorActions - ); + const elementsEdt = new ElementsEdtActions(model, selectionActions, undoActions, editorActions); const clipboardController = new ClipboardController( - selectionActions, parserActions, editorActions, undoActions, mapCoordinates + selectionActions, + parserActions, + editorActions, + undoActions, + mapCoordinates, ); return { diff --git a/src/circuit/views/Drawing.ts b/src/circuit/views/Drawing.ts index d2ed542..2275880 100644 --- a/src/circuit/views/Drawing.ts +++ b/src/circuit/views/Drawing.ts @@ -1,11 +1,3 @@ -/** - * @file Drawing.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Canvas rendering view dispatching per-primitive draw hooks - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../../graphic/GraphicsInterface.js'; import type { MapCoordinates } from '../../geom/MapCoordinates.js'; import { DrawingModel } from '../model/DrawingModel.js'; @@ -95,10 +87,6 @@ export class Drawing { } } - getNeedHoles(): boolean { - return this.needHoles; - } - private drawPrimitives(jIndex: number, graphic: GraphicsInterface, cs: MapCoordinates): void { for (const gg of this.drawingModel.getPrimitiveVector()) { if (jIndex > 0 && gg.getLayer() > jIndex) { diff --git a/src/circuit/views/Export.ts b/src/circuit/views/Export.ts index 280304a..5014a90 100644 --- a/src/circuit/views/Export.ts +++ b/src/circuit/views/Export.ts @@ -1,11 +1,3 @@ -/** - * @file Export.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Export view dispatching per-primitive export hooks - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ExportInterface } from '../../export/ExportInterface.js'; import type { MapCoordinates } from '../../geom/MapCoordinates.js'; import { DrawingModel } from '../model/DrawingModel.js'; @@ -31,10 +23,7 @@ export class Export { } exportDrawing(exp: ExportInterface, exportInvisible: boolean, mp: MapCoordinates): void { - if ( - this.drawingModel.getDrawOnlyLayer() >= 0 && - !this.drawingModel.getDrawOnlyPads() - ) { + if (this.drawingModel.getDrawOnlyLayer() >= 0 && !this.drawingModel.getDrawOnlyPads()) { this.exportAllObjects(exp, exportInvisible, mp); } else if (!this.drawingModel.getDrawOnlyPads()) { for (let j = 0; j < this.drawingModel.getLayers().length; ++j) { @@ -73,7 +62,7 @@ export class Export { private exportAllObjects( exp: ExportInterface, exportInvisible: boolean, - mp: MapCoordinates + mp: MapCoordinates, ): void { for (let i = 0; i < this.drawingModel.getPrimitiveVector().length; ++i) { const g = this.drawingModel.getPrimitiveVector()[i]; @@ -108,7 +97,7 @@ export function registerExportHooks(): void { model: DrawingModel, exp: ExportInterface, exportInvisible: boolean, - cs: MapCoordinates + cs: MapCoordinates, ): void => { const e = new Export(model); e.exportDrawing(exp, exportInvisible, cs); diff --git a/src/export/AbstractExport.ts b/src/export/AbstractExport.ts deleted file mode 100644 index 31a05ce..0000000 --- a/src/export/AbstractExport.ts +++ /dev/null @@ -1,276 +0,0 @@ -/** - * @file AbstractExport.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Abstract base class for export filters (SVG, PGF, TikZ) - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Provides shared buffer management, layer tracking, dash style, - * and stroke width bookkeeping. Format-specific subclasses only - * override the per-primitive rendering methods defined in - * ExportInterface. - */ - -import type { ExportInterface } from './ExportInterface.js'; -import type { DimensionG } from '../graphic/DimensionG.js'; -import type { PointDouble } from '../graphic/PointDouble.js'; -import type { LayerDesc } from '../layers/LayerDesc.js'; -import type { MacroDesc } from '../primitives/MacroDesc.js'; -import type { PointPr } from './PointPr.js'; - -/** - * Escape special LaTeX characters in user-supplied text. - * Characters escaped: \ { } # $ % & _ ^ ~ - * This prevents malformed output when user labels contain characters - * that are active in LaTeX (e.g. underscores in component names). - */ -export function escapeLatex(s: string): string { - // Escape special LaTeX characters: \ { } # $ % & _ ^ ~ - // - // The tricky part is that replacement strings (e.g. \textbackslash{}) - // themselves contain special chars (\, {, }). We use the standard - // approach: replace \ with a placeholder token first, do all other - // escaping, then replace the placeholder last. - // - // Sentinel: \x00 is forbidden in JavaScript strings so it cannot - // appear in user-supplied FidoCad text (which is ASCII text lines). - const BS = '\x00BS\x00'; - const CIRC = '\x00CIRC\x00'; - const TILDE = '\x00TILDE\x00'; - - return s - .replace(/\\/g, BS) - .replace(/\^/g, CIRC) - .replace(/~/g, TILDE) - .replace(/[#$%&_]/g, (c) => '\\' + c) - .replace(/[{}]/g, (c) => '\\' + c) - .replace(new RegExp(BS, 'g'), '\\textbackslash{}') - .replace(new RegExp(CIRC, 'g'), '\\textasciicircum{}') - .replace(new RegExp(TILDE, 'g'), '\\textasciitilde{}'); -} - -export abstract class AbstractExport implements ExportInterface { - /** Output buffer — format-specific content is pushed here. */ - protected buffer: string[] = []; - - /** Active layer vector (set by exportStart). */ - protected layerV: LayerDesc[] = []; - - /** Current dash style index (for change detection). */ - protected currentDash: number = -1; - - /** Dash unit (px). */ - protected dashUnit: number = 10; - - /** Dash phase offset. */ - protected dashPhase: number = 0; - - /** Current stroke width (for change detection). */ - protected currentStrokeWidth: number = 1; - - // ── Buffer helpers ──────────────────────────────────────────────────── - - /** Append a line to the output buffer. */ - protected emit(line: string): void { - this.buffer.push(line); - } - - /** Append a formatted line to the output buffer. */ - protected emitFmt(format: string, ...args: (string | number)[]): void { - this.buffer.push(format.replace(/%s/g, () => String(args.shift() ?? ''))); - } - - /** Join all buffered lines into a single string. */ - protected join(): string { - return this.buffer.join('\n'); - } - - // ── Dash helpers ────────────────────────────────────────────────────── - - setDashUnit(u: number): void { - this.dashUnit = u; - } - - setDashPhase(p: number): void { - this.dashPhase = p; - } - - /** Compute a dash-array pattern from FidoCadJ dash style index. */ - protected dashArray(style: number): number[] { - const u = this.dashUnit; - switch (style) { - case 1: - return [u * 2, u * 2]; - case 2: - return [u * 2, u * 2, u, u * 2]; - case 3: - return [u * 3, u * 2]; - case 4: - return [u, u]; - default: - return []; - } - } - - // ── Coordinate helpers ──────────────────────────────────────────────── - - /** Round a number to one decimal place. */ - protected r1(v: number): string { - return (Math.round(v * 10) / 10).toString(); - } - - /** Round a number to two decimal places. */ - protected r2(v: number): string { - return (Math.round(v * 100) / 100).toString(); - } - - // ── Abstract: lifecycle (implemented by subclass) ───────────────────── - - abstract exportStart(totalSize: DimensionG, la: LayerDesc[], grid: number): void; - abstract exportEnd(): void; - - // ── Abstract: per-primitive rendering ───────────────────────────────── - - abstract exportAdvText( - x: number, - y: number, - sizex: number, - sizey: number, - fontname: string, - isBold: boolean, - isMirrored: boolean, - isItalic: boolean, - orientation: number, - layer: number, - text: string, - ): void; - - abstract exportBezier( - x1: number, - y1: number, - x2: number, - y2: number, - x3: number, - y3: number, - x4: number, - y4: number, - layer: number, - arrowStart: boolean, - arrowEnd: boolean, - arrowStyle: number, - arrowLength: number, - arrowHalfWidth: number, - dashStyle: number, - strokeWidth: number, - ): void; - - abstract exportConnection(x: number, y: number, layer: number, size: number): void; - - abstract exportLine( - x1: number, - y1: number, - x2: number, - y2: number, - layer: number, - arrowStart: boolean, - arrowEnd: boolean, - arrowStyle: number, - arrowLength: number, - arrowHalfWidth: number, - dashStyle: number, - strokeWidth: number, - ): void; - - abstract exportMacro( - x: number, - y: number, - isMirrored: boolean, - orientation: number, - macroName: string, - macroDesc: string, - name: string, - xn: number, - yn: number, - value: string, - xv: number, - yv: number, - font: string, - fontSize: number, - m: Map, - ): boolean; - - abstract exportOval( - x1: number, - y1: number, - x2: number, - y2: number, - isFilled: boolean, - layer: number, - dashStyle: number, - strokeWidth: number, - ): void; - - abstract exportPCBLine( - x1: number, - y1: number, - x2: number, - y2: number, - width: number, - layer: number, - ): void; - - abstract exportPCBPad( - x: number, - y: number, - style: number, - six: number, - siy: number, - indiam: number, - layer: number, - onlyHole: boolean, - ): void; - - abstract exportPolygon( - vertices: PointDouble[], - nVertices: number, - isFilled: boolean, - layer: number, - dashStyle: number, - strokeWidth: number, - ): void; - - abstract exportCurve( - vertices: PointDouble[], - nVertices: number, - isFilled: boolean, - isClosed: boolean, - layer: number, - arrowStart: boolean, - arrowEnd: boolean, - arrowStyle: number, - arrowLength: number, - arrowHalfWidth: number, - dashStyle: number, - strokeWidth: number, - ): boolean; - - abstract exportRectangle( - x1: number, - y1: number, - x2: number, - y2: number, - isFilled: boolean, - layer: number, - dashStyle: number, - strokeWidth: number, - ): void; - - abstract exportArrow( - x: number, - y: number, - xc: number, - yc: number, - l: number, - h: number, - style: number, - ): PointPr; -} diff --git a/src/export/ExportBitmap.ts b/src/export/ExportBitmap.ts index b52055f..4599fc3 100644 --- a/src/export/ExportBitmap.ts +++ b/src/export/ExportBitmap.ts @@ -1,20 +1,3 @@ -/** - * @file ExportBitmap.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Bitmap export engine: renders the drawing model to an offscreen - * canvas at configurable DPI/pixel resolution, with optional B&W - * post-processing and per-layer splitting. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Mirrors FidoCadJ's ExportGraphic.exportImage / exportImageBW. - * The FCD logical unit → pixel mapping is: - * magnitude = targetDPI / 72 - * (72 FCD units per logical inch — the standard screen mapping). - * For explicit-pixel mode, magnitude is computed to fit the - * drawing bounding box into the requested pixel dimensions while - * preserving aspect ratio. - */ - import type { DrawingModel } from '../circuit/model/DrawingModel.js'; import type { MapCoordinates } from '../geom/MapCoordinates.js'; import { MapCoordinates as MapCoordsImpl } from '../geom/MapCoordinates.js'; diff --git a/src/export/ExportBitmapOptions.ts b/src/export/ExportBitmapOptions.ts index 337032b..9b84a71 100644 --- a/src/export/ExportBitmapOptions.ts +++ b/src/export/ExportBitmapOptions.ts @@ -1,11 +1,3 @@ -/** - * @file ExportBitmapOptions.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Configuration options for bitmap export (PNG, JPG). - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - /** DPI presets matching FidoCadJ's DialogExport. */ export const DPI_PRESETS = [72, 150, 300, 600, 1200, 1800, 2400] as const; export type DpiPreset = (typeof DPI_PRESETS)[number]; diff --git a/src/export/ExportFacade.ts b/src/export/ExportFacade.ts index 16478e8..67f7407 100644 --- a/src/export/ExportFacade.ts +++ b/src/export/ExportFacade.ts @@ -1,13 +1,3 @@ -/** - * @file ExportFacade.ts - * @author Dante Loi - * @date 2026-05-13 - * @brief Export methods (SVG, PGF, TikZ) extracted from CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Delegates to the export/ package. CircuitPanel used to own these - * methods even though they don't touch the canvas or DOM. - */ - import { MapCoordinates } from '../geom/MapCoordinates.js'; import { Export } from '../circuit/views/Export.js'; import { DrawingSize } from '../geom/DrawingSize.js'; diff --git a/src/export/ExportInterface.ts b/src/export/ExportInterface.ts index a2218f4..f5557c3 100644 --- a/src/export/ExportInterface.ts +++ b/src/export/ExportInterface.ts @@ -1,11 +1,3 @@ -/** - * @file ExportInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Abstract export interface - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { DimensionG } from '../graphic/DimensionG.js'; import type { PointDouble } from '../graphic/PointDouble.js'; import type { LayerDesc } from '../layers/LayerDesc.js'; @@ -18,55 +10,147 @@ export interface ExportInterface { setDashUnit(u: number): void; setDashPhase(p: number): void; - exportAdvText(x: number, y: number, sizex: number, sizey: number, - fontname: string, isBold: boolean, isMirrored: boolean, isItalic: boolean, - orientation: number, layer: number, text: string): void; + exportAdvText( + x: number, + y: number, + sizex: number, + sizey: number, + fontname: string, + isBold: boolean, + isMirrored: boolean, + isItalic: boolean, + orientation: number, + layer: number, + text: string, + ): void; - exportBezier(x1: number, y1: number, x2: number, y2: number, - x3: number, y3: number, x4: number, y4: number, layer: number, - arrowStart: boolean, arrowEnd: boolean, arrowStyle: number, - arrowLength: number, arrowHalfWidth: number, - dashStyle: number, strokeWidth: number): void; + exportBezier( + x1: number, + y1: number, + x2: number, + y2: number, + x3: number, + y3: number, + x4: number, + y4: number, + layer: number, + arrowStart: boolean, + arrowEnd: boolean, + arrowStyle: number, + arrowLength: number, + arrowHalfWidth: number, + dashStyle: number, + strokeWidth: number, + ): void; exportConnection(x: number, y: number, layer: number, size: number): void; - exportLine(x1: number, y1: number, x2: number, y2: number, layer: number, - arrowStart: boolean, arrowEnd: boolean, arrowStyle: number, - arrowLength: number, arrowHalfWidth: number, - dashStyle: number, strokeWidth: number): void; + exportLine( + x1: number, + y1: number, + x2: number, + y2: number, + layer: number, + arrowStart: boolean, + arrowEnd: boolean, + arrowStyle: number, + arrowLength: number, + arrowHalfWidth: number, + dashStyle: number, + strokeWidth: number, + ): void; - exportMacro(x: number, y: number, isMirrored: boolean, - orientation: number, macroName: string, macroDesc: string, - name: string, xn: number, yn: number, - value: string, xv: number, yv: number, - font: string, fontSize: number, - m: Map): boolean; + exportMacro( + x: number, + y: number, + isMirrored: boolean, + orientation: number, + macroName: string, + macroDesc: string, + name: string, + xn: number, + yn: number, + value: string, + xv: number, + yv: number, + font: string, + fontSize: number, + m: Map, + ): boolean; - exportOval(x1: number, y1: number, x2: number, y2: number, - isFilled: boolean, layer: number, - dashStyle: number, strokeWidth: number): void; + exportOval( + x1: number, + y1: number, + x2: number, + y2: number, + isFilled: boolean, + layer: number, + dashStyle: number, + strokeWidth: number, + ): void; - exportPCBLine(x1: number, y1: number, x2: number, y2: number, - width: number, layer: number): void; + exportPCBLine( + x1: number, + y1: number, + x2: number, + y2: number, + width: number, + layer: number, + ): void; - exportPCBPad(x: number, y: number, style: number, - six: number, siy: number, indiam: number, - layer: number, onlyHole: boolean): void; + exportPCBPad( + x: number, + y: number, + style: number, + six: number, + siy: number, + indiam: number, + layer: number, + onlyHole: boolean, + ): void; - exportPolygon(vertices: PointDouble[], nVertices: number, - isFilled: boolean, layer: number, - dashStyle: number, strokeWidth: number): void; + exportPolygon( + vertices: PointDouble[], + nVertices: number, + isFilled: boolean, + layer: number, + dashStyle: number, + strokeWidth: number, + ): void; - exportCurve(vertices: PointDouble[], nVertices: number, - isFilled: boolean, isClosed: boolean, layer: number, - arrowStart: boolean, arrowEnd: boolean, arrowStyle: number, - arrowLength: number, arrowHalfWidth: number, - dashStyle: number, strokeWidth: number): boolean; + exportCurve( + vertices: PointDouble[], + nVertices: number, + isFilled: boolean, + isClosed: boolean, + layer: number, + arrowStart: boolean, + arrowEnd: boolean, + arrowStyle: number, + arrowLength: number, + arrowHalfWidth: number, + dashStyle: number, + strokeWidth: number, + ): boolean; - exportRectangle(x1: number, y1: number, x2: number, y2: number, - isFilled: boolean, layer: number, - dashStyle: number, strokeWidth: number): void; + exportRectangle( + x1: number, + y1: number, + x2: number, + y2: number, + isFilled: boolean, + layer: number, + dashStyle: number, + strokeWidth: number, + ): void; - exportArrow(x: number, y: number, xc: number, yc: number, - l: number, h: number, style: number): PointPr; + exportArrow( + x: number, + y: number, + xc: number, + yc: number, + l: number, + h: number, + style: number, + ): PointPr; } diff --git a/src/export/ExportPDF.ts b/src/export/ExportPDF.ts index 7519e42..e2e62cf 100644 --- a/src/export/ExportPDF.ts +++ b/src/export/ExportPDF.ts @@ -1,33 +1,3 @@ -/** - * @file ExportPDF.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief PDF export implementation — vendor-free, no npm dependencies. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Writes a minimal PDF 1.4 document by emitting content-stream - * operators directly. Mirrors FidoCadJ's ExportPDF.java but drops - * the Unicode glyph mapping (uses WinAnsiEncoding); fonts are - * limited to the 14 standard PDF base fonts (Helvetica, Courier, - * Times, Symbol families). The drawing is rendered at 200 dpi - * internally and scaled to PDF units (72 dpi). - * - * Object layout: - * 1 /Info - * 2 /ProcSet - * 3 /Catalog - * 4 /Page - * 5 /Pages - * 6 Font F1: Courier - * 7 Font F2: Courier-Bold - * 8 Font F3: Times-Roman - * 9 Font F4: Times-Bold - * 10 Font F5: Helvetica - * 11 Font F6: Helvetica-Bold - * 12 Font F7: Symbol - * 13 Font F8: ZapfDingbats - * 14 Content stream - */ - import type { ExportInterface } from './ExportInterface.js'; import type { DimensionG } from '../graphic/DimensionG.js'; import type { PointDouble } from '../graphic/PointDouble.js'; diff --git a/src/export/ExportPGF.ts b/src/export/ExportPGF.ts index aa1ea9c..de6626f 100644 --- a/src/export/ExportPGF.ts +++ b/src/export/ExportPGF.ts @@ -1,11 +1,3 @@ -/** - * @file ExportPGF.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief PGF (Portable Graphic File) export for LaTeX documents - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ExportInterface } from './ExportInterface.js'; import type { DimensionG } from '../graphic/DimensionG.js'; import type { PointDouble } from '../graphic/PointDouble.js'; @@ -13,7 +5,7 @@ import type { LayerDesc } from '../layers/LayerDesc.js'; import { Globals } from '../globals/Globals.js'; import { Arrow } from '../primitives/Arrow.js'; import { PointPr } from './PointPr.js'; -import { escapeLatex } from './AbstractExport.js'; +import { escapeLatex } from './LatexEscape.js'; export class ExportPGF implements ExportInterface { private buffer: string[] = []; diff --git a/src/export/ExportSVG.ts b/src/export/ExportSVG.ts index 653a82e..85e012f 100644 --- a/src/export/ExportSVG.ts +++ b/src/export/ExportSVG.ts @@ -1,11 +1,3 @@ -/** - * @file ExportSVG.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief SVG export implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ExportInterface } from './ExportInterface.js'; import type { DimensionG } from '../graphic/DimensionG.js'; import type { PointDouble } from '../graphic/PointDouble.js'; diff --git a/src/export/ExportTikZ.ts b/src/export/ExportTikZ.ts index 96a90d1..1c8f0ee 100644 --- a/src/export/ExportTikZ.ts +++ b/src/export/ExportTikZ.ts @@ -1,11 +1,3 @@ -/** - * @file ExportTikZ.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief TikZ export for modern LaTeX documents (built on PGF, more idiomatic) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ExportInterface } from './ExportInterface.js'; import type { DimensionG } from '../graphic/DimensionG.js'; import type { PointDouble } from '../graphic/PointDouble.js'; @@ -13,7 +5,7 @@ import type { LayerDesc } from '../layers/LayerDesc.js'; import { Globals } from '../globals/Globals.js'; import { Arrow } from '../primitives/Arrow.js'; import { PointPr } from './PointPr.js'; -import { escapeLatex } from './AbstractExport.js'; +import { escapeLatex } from './LatexEscape.js'; export class ExportTikZ implements ExportInterface { private buffer: string[] = []; diff --git a/src/export/LatexEscape.ts b/src/export/LatexEscape.ts new file mode 100644 index 0000000..a15bfcd --- /dev/null +++ b/src/export/LatexEscape.ts @@ -0,0 +1,30 @@ +/** + * Escape special LaTeX characters in user-supplied text. + * Characters escaped: \ { } # $ % & _ ^ ~ + * This prevents malformed output when user labels contain characters + * that are active in LaTeX (e.g. underscores in component names). + */ +export function escapeLatex(s: string): string { + // Escape special LaTeX characters: \ { } # $ % & _ ^ ~ + // + // The tricky part is that replacement strings (e.g. \textbackslash{}) + // themselves contain special chars (\, {, }). We use the standard + // approach: replace \ with a placeholder token first, do all other + // escaping, then replace the placeholder last. + // + // Sentinel: \x00 is forbidden in JavaScript strings so it cannot + // appear in user-supplied FidoCad text (which is ASCII text lines). + const BS = '\x00BS\x00'; + const CIRC = '\x00CIRC\x00'; + const TILDE = '\x00TILDE\x00'; + + return s + .replace(/\\/g, BS) + .replace(/\^/g, CIRC) + .replace(/~/g, TILDE) + .replace(/[#$%&_]/g, (c) => '\\' + c) + .replace(/[{}]/g, (c) => '\\' + c) + .replace(new RegExp(BS, 'g'), '\\textbackslash{}') + .replace(new RegExp(CIRC, 'g'), '\\textasciicircum{}') + .replace(new RegExp(TILDE, 'g'), '\\textasciitilde{}'); +} diff --git a/src/export/PointPr.ts b/src/export/PointPr.ts index febf7ea..e73490b 100644 --- a/src/export/PointPr.ts +++ b/src/export/PointPr.ts @@ -1,11 +1,3 @@ -/** - * @file PointPr.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Point printer utility for coordinate formatting - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class PointPr { x: number; y: number; diff --git a/src/export/SvgPathToPdf.ts b/src/export/SvgPathToPdf.ts index 4f183e8..3d4a0c8 100644 --- a/src/export/SvgPathToPdf.ts +++ b/src/export/SvgPathToPdf.ts @@ -1,15 +1,3 @@ -/** - * @file SvgPathToPdf.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Converts an SVG path `d` string (as emitted by MathJax glyph outlines) - * into PDF content-stream path operators (m / l / c / h). Quadratic - * Béziers (Q/T) are converted to cubics since PDF has no quadratic - * operator. Coordinates are passed through unscaled; the caller sets the - * CTM (via cm) so the raw glyph units map into place. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - /** Tokenise a path `d` string into command letters and numbers. */ function tokenize(d: string): Array { const out: Array = []; diff --git a/src/geom/DrawingSize.ts b/src/geom/DrawingSize.ts index 946799a..05d9a26 100644 --- a/src/geom/DrawingSize.ts +++ b/src/geom/DrawingSize.ts @@ -1,11 +1,3 @@ -/** - * @file DrawingSize.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Drawing dimensions (width × height) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { Drawing } from '../circuit/views/Drawing.js'; import { DrawingModel } from '../circuit/model/DrawingModel.js'; import { MapCoordinates } from './MapCoordinates.js'; @@ -20,7 +12,7 @@ export class DrawingSize { dm: DrawingModel, unitPerPixel: number, countMin: boolean, - origin: PointG + origin: PointG, ): DimensionG { let width: number; let height: number; @@ -84,7 +76,7 @@ export class DrawingSize { dm: DrawingModel, sizex: number, sizey: number, - countMin: boolean + countMin: boolean, ): MapCoordinates { const org = new PointG(0, 0); const newZoom = new MapCoordinates(); diff --git a/src/geom/GeometricDistances.ts b/src/geom/GeometricDistances.ts index 74adea9..b2742a3 100644 --- a/src/geom/GeometricDistances.ts +++ b/src/geom/GeometricDistances.ts @@ -1,11 +1,3 @@ -/** - * @file GeometricDistances.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Geometric distance and intersection calculations - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class GeometricDistances { static readonly MIN_DISTANCE = 100; static readonly MAX_BEZIER_SEGMENTS = 10; diff --git a/src/geom/MapCoordinates.ts b/src/geom/MapCoordinates.ts index 6484043..f3099c6 100644 --- a/src/geom/MapCoordinates.ts +++ b/src/geom/MapCoordinates.ts @@ -1,11 +1,3 @@ -/** - * @file MapCoordinates.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Logical-to-screen coordinate mapping, zoom, snap, and orientation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class MapCoordinates { private xCenter: number = 0; private yCenter: number = 0; diff --git a/src/globals/Globals.ts b/src/globals/Globals.ts index 000cfad..fc9b3cc 100644 --- a/src/globals/Globals.ts +++ b/src/globals/Globals.ts @@ -1,11 +1,3 @@ -/** - * @file Globals.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Global constants and static utility functions - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { AccessResources } from '../i18n/AccessResources.js'; export class Globals { diff --git a/src/graphic/ColorInterface.ts b/src/graphic/ColorInterface.ts index f1399d4..cffd3f6 100644 --- a/src/graphic/ColorInterface.ts +++ b/src/graphic/ColorInterface.ts @@ -1,11 +1,3 @@ -/** - * @file ColorInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Colour abstraction interface - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export interface ColorInterface { white(): ColorInterface; gray(): ColorInterface; diff --git a/src/graphic/DimensionG.ts b/src/graphic/DimensionG.ts index c7df3fb..ffc1a10 100644 --- a/src/graphic/DimensionG.ts +++ b/src/graphic/DimensionG.ts @@ -1,11 +1,3 @@ -/** - * @file DimensionG.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief 2D dimension type (width × height) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class DimensionG { width: number; height: number; diff --git a/src/graphic/FontG.ts b/src/graphic/FontG.ts deleted file mode 100644 index 8a2e0e6..0000000 --- a/src/graphic/FontG.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @file FontG.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Font descriptor type - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -export class FontG { - fontFamily: string; - - constructor(name: string) { - this.fontFamily = name; - } - - getFamily(): string { - return this.fontFamily; - } -} diff --git a/src/graphic/GraphicsInterface.ts b/src/graphic/GraphicsInterface.ts index ae03755..1efa3b5 100644 --- a/src/graphic/GraphicsInterface.ts +++ b/src/graphic/GraphicsInterface.ts @@ -1,11 +1,3 @@ -/** - * @file GraphicsInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Graphics abstraction layer interface - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ColorInterface } from './ColorInterface.js'; import type { TextInterface } from './TextInterface.js'; import type { ShapeInterface } from './ShapeInterface.js'; @@ -17,7 +9,6 @@ import type { LaidOutSegment } from './MathLayout.js'; export interface GraphicsInterface { getColor(): ColorInterface; setZoom(z: number): void; - getZoom(): number; setColor(c: ColorInterface): void; getTextInterface(): TextInterface; applyStroke(w: number, dashStyle: number): void; @@ -34,8 +25,6 @@ export interface GraphicsInterface { hitClip(x: number, y: number, width: number, height: number): boolean; drawLine(x1: number, y1: number, x2: number, y2: number): void; setFont(name: string, size: number, isItalic?: boolean, isBold?: boolean): void; - getFontSize(): number; - setFontSize(size: number): void; getFontAscent(): number; getFontDescent(): number; getStringWidth(s: string): number; diff --git a/src/graphic/MathLayout.ts b/src/graphic/MathLayout.ts index 149bc67..51c3b11 100644 --- a/src/graphic/MathLayout.ts +++ b/src/graphic/MathLayout.ts @@ -1,20 +1,3 @@ -/** - * @file MathLayout.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Splits a FidoCad text string into plain-text and LaTeX-math segments, - * renders math to glyph geometry via the vendored MathJax engine, and - * lays the segments out left-to-right with x-advances. Shared by the - * on-screen canvas renderer and the SVG/PDF exporters so math appears - * identically everywhere. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Math coordinates come back from MathJax in native units - * (unitsPerEm, y-down, baseline at 0); a math segment's advance is - * `widthEm * emPx`. Text advance is supplied by the caller's - * `measure` callback so each backend uses its own font metrics. - * Malformed LaTeX degrades to literal `$...$` text. - */ - import { tex2mathgeom, type MathGeometry } from '../vendor/mathjax-shim.js'; export type { MathGeometry } from '../vendor/mathjax-shim.js'; diff --git a/src/graphic/PointDouble.ts b/src/graphic/PointDouble.ts index 29549d3..7b2cbf8 100644 --- a/src/graphic/PointDouble.ts +++ b/src/graphic/PointDouble.ts @@ -1,11 +1,3 @@ -/** - * @file PointDouble.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Double-precision 2D point - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class PointDouble { x: number; y: number; diff --git a/src/graphic/PointG.ts b/src/graphic/PointG.ts index 807c29b..429c5b4 100644 --- a/src/graphic/PointG.ts +++ b/src/graphic/PointG.ts @@ -1,11 +1,3 @@ -/** - * @file PointG.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Integer 2D point type - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class PointG { x: number; y: number; diff --git a/src/graphic/PolygonInterface.ts b/src/graphic/PolygonInterface.ts index f34a33d..34adb45 100644 --- a/src/graphic/PolygonInterface.ts +++ b/src/graphic/PolygonInterface.ts @@ -1,11 +1,3 @@ -/** - * @file PolygonInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Polygon drawing interface - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export interface PolygonInterface { addPoint(x: number, y: number): void; getNpoints(): number; diff --git a/src/graphic/RectangleG.ts b/src/graphic/RectangleG.ts index 1509113..7ad9138 100644 --- a/src/graphic/RectangleG.ts +++ b/src/graphic/RectangleG.ts @@ -1,11 +1,3 @@ -/** - * @file RectangleG.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Integer rectangle type - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class RectangleG { x: number; y: number; @@ -19,29 +11,42 @@ export class RectangleG { this.height = height; } - getX(): number { return this.x; } - getY(): number { return this.y; } - getWidth(): number { return this.width; } - getHeight(): number { return this.height; } + getX(): number { + return this.x; + } + getY(): number { + return this.y; + } + getWidth(): number { + return this.width; + } + getHeight(): number { + return this.height; + } contains(px: number, py: number): boolean { - return px >= this.x && px <= this.x + this.width - && py >= this.y && py <= this.y + this.height; + return ( + px >= this.x && px <= this.x + this.width && py >= this.y && py <= this.y + this.height + ); } intersects(other: RectangleG): boolean { - return other.x < this.x + this.width - && other.x + other.width > this.x - && other.y < this.y + this.height - && other.y + other.height > this.y; + return ( + other.x < this.x + this.width && + other.x + other.width > this.x && + other.y < this.y + this.height && + other.y + other.height > this.y + ); } intersectsLine(x1: number, y1: number, x2: number, y2: number): boolean { const { x, y, width, height } = this; - return this._lineXline(x1, y1, x2, y2, x, y, x + width, y) - || this._lineXline(x1, y1, x2, y2, x, y, x, y + height) - || this._lineXline(x1, y1, x2, y2, x + width, y, x + width, y + height) - || this._lineXline(x1, y1, x2, y2, x, y + height, x + width, y + height); + return ( + this._lineXline(x1, y1, x2, y2, x, y, x + width, y) || + this._lineXline(x1, y1, x2, y2, x, y, x, y + height) || + this._lineXline(x1, y1, x2, y2, x + width, y, x + width, y + height) || + this._lineXline(x1, y1, x2, y2, x, y + height, x + width, y + height) + ); } private _dir(px: number, py: number, qx: number, qy: number, rx: number, ry: number): number { @@ -49,13 +54,32 @@ export class RectangleG { return val === 0 ? 0 : val > 0 ? 1 : 2; } - private _onSeg(px: number, py: number, qx: number, qy: number, rx: number, ry: number): boolean { - return rx >= Math.min(px, qx) && rx <= Math.max(px, qx) - && ry >= Math.min(py, qy) && ry <= Math.max(py, qy); + private _onSeg( + px: number, + py: number, + qx: number, + qy: number, + rx: number, + ry: number, + ): boolean { + return ( + rx >= Math.min(px, qx) && + rx <= Math.max(px, qx) && + ry >= Math.min(py, qy) && + ry <= Math.max(py, qy) + ); } - private _lineXline(x1: number, y1: number, x2: number, y2: number, - x3: number, y3: number, x4: number, y4: number): boolean { + private _lineXline( + x1: number, + y1: number, + x2: number, + y2: number, + x3: number, + y3: number, + x4: number, + y4: number, + ): boolean { const d1 = this._dir(x3, y3, x4, y4, x1, y1); const d2 = this._dir(x3, y3, x4, y4, x2, y2); const d3 = this._dir(x1, y1, x2, y2, x3, y3); diff --git a/src/graphic/RenderCtx.ts b/src/graphic/RenderCtx.ts index 638f32c..d22389b 100644 --- a/src/graphic/RenderCtx.ts +++ b/src/graphic/RenderCtx.ts @@ -1,20 +1,4 @@ -/** - * @file RenderCtx.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Per-render context replacing static oldalpha in GraphicPrimitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Each render pass creates a fresh RenderCtx. This eliminates the static - * oldalpha field and makes the rendering pipeline re-entrant. - */ - export interface RenderCtx { /** Last alpha value seen during rendering (for change detection). */ alpha: number; } - -/** Factory for a fresh render context. */ -export function createRenderCtx(): RenderCtx { - return { alpha: 1.0 }; -} diff --git a/src/graphic/ShapeInterface.ts b/src/graphic/ShapeInterface.ts index 84b3855..7c53308 100644 --- a/src/graphic/ShapeInterface.ts +++ b/src/graphic/ShapeInterface.ts @@ -1,20 +1,19 @@ -/** - * @file ShapeInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Shape drawing interface - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { RectangleG } from './RectangleG.js'; export interface ShapeInterface { getBounds(): RectangleG; - createCubicCurve(x0: number, y0: number, x1: number, y1: number, - x2: number, y2: number, x3: number, y3: number): void; + createCubicCurve( + x0: number, + y0: number, + x1: number, + y1: number, + x2: number, + y2: number, + x3: number, + y3: number, + ): void; createGeneralPath(npoints: number): void; moveTo(x: number, y: number): void; - curveTo(x0: number, y0: number, x1: number, y1: number, - x2: number, y2: number): void; + curveTo(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number): void; closePath(): void; } diff --git a/src/graphic/TeXMode.ts b/src/graphic/TeXMode.ts index 4f36066..eb6ea2c 100644 --- a/src/graphic/TeXMode.ts +++ b/src/graphic/TeXMode.ts @@ -1,9 +1,3 @@ -/** - * @file TeXMode.ts - * @brief Tiny module holding the math-rendering flag, kept separate to avoid - * circular imports between CircuitPanel and PrimitiveAdvText. - */ - /** When true, PrimitiveAdvText.draw() typesets $...$ math onto the canvas via * MathLayout/MathJax. When false, the literal source text is drawn instead. * On-screen it follows the renderTeX setting; bitmap export forces it on. */ diff --git a/src/graphic/TextInterface.ts b/src/graphic/TextInterface.ts index 474fe39..274b162 100644 --- a/src/graphic/TextInterface.ts +++ b/src/graphic/TextInterface.ts @@ -1,14 +1,4 @@ -/** - * @file TextInterface.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Text rendering interface - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export interface TextInterface { - getFontSize(): number; - setFontSize(size: number): void; getStringWidth(s: string): number; drawString(str: string, x: number, y: number): void; } diff --git a/src/graphic/canvas/ColorCanvas.ts b/src/graphic/canvas/ColorCanvas.ts index 819a605..034e951 100644 --- a/src/graphic/canvas/ColorCanvas.ts +++ b/src/graphic/canvas/ColorCanvas.ts @@ -1,11 +1,3 @@ -/** - * @file ColorCanvas.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief HTML Canvas colour implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ColorInterface } from '../ColorInterface.js'; export class ColorCanvas implements ColorInterface { diff --git a/src/graphic/canvas/GraphicsCanvas.ts b/src/graphic/canvas/GraphicsCanvas.ts index 60c490d..8287cf1 100644 --- a/src/graphic/canvas/GraphicsCanvas.ts +++ b/src/graphic/canvas/GraphicsCanvas.ts @@ -1,11 +1,3 @@ -/** - * @file GraphicsCanvas.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief HTML Canvas graphics implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../GraphicsInterface.js'; import type { ColorInterface } from '../ColorInterface.js'; import type { TextInterface } from '../TextInterface.js'; @@ -24,10 +16,7 @@ export class GraphicsCanvas implements GraphicsInterface { private ctx: CanvasRenderingContext2D; private canvas: HTMLCanvasElement; private currentColor: ColorInterface = new ColorCanvas(0, 0, 0); - private zoom: number = 1.0; private textInterface: TextCanvas; - private fontName: string = 'sans-serif'; - private fontSize: number = 12; private fontItalic: boolean = false; private fontBold: boolean = false; private selectedColor: ColorCanvas = new ColorCanvas(0, 255, 0); @@ -48,12 +37,7 @@ export class GraphicsCanvas implements GraphicsInterface { return this.currentColor; } - setZoom(z: number): void { - this.zoom = z; - } - getZoom(): number { - return this.zoom; - } + setZoom(_z: number): void {} setColor(c: ColorInterface): void { this.currentColor = c; @@ -115,8 +99,6 @@ export class GraphicsCanvas implements GraphicsInterface { } setFont(name: string, size: number, isItalic?: boolean, isBold?: boolean): void { - this.fontName = name; - this.fontSize = size; this.fontItalic = isItalic ?? false; this.fontBold = isBold ?? false; const style = `${this.fontItalic ? 'italic ' : ''}${this.fontBold ? 'bold ' : ''}`; @@ -130,12 +112,6 @@ export class GraphicsCanvas implements GraphicsInterface { this.textInterface.setFont(name, size, isItalic, isBold); } - getFontSize(): number { - return this.fontSize; - } - setFontSize(size: number): void { - this.setFont(this.fontName, size, this.fontItalic, this.fontBold); - } getFontAscent(): number { return this.textInterface.getFontAscent(); } diff --git a/src/graphic/canvas/PolygonCanvas.ts b/src/graphic/canvas/PolygonCanvas.ts index aee5217..79e8e05 100644 --- a/src/graphic/canvas/PolygonCanvas.ts +++ b/src/graphic/canvas/PolygonCanvas.ts @@ -1,11 +1,3 @@ -/** - * @file PolygonCanvas.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief HTML Canvas polygon drawing implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { PolygonInterface } from '../PolygonInterface.js'; import type { PointG } from '../PointG.js'; import { RectangleG } from '../RectangleG.js'; @@ -28,17 +20,25 @@ export class PolygonCanvas implements PolygonInterface { this.ypoints = []; } - getNpoints(): number { return this.xpoints.length; } - getXpoints(): number[] { return this.xpoints; } - getYpoints(): number[] { return this.ypoints; } + getNpoints(): number { + return this.xpoints.length; + } + getXpoints(): number[] { + return this.xpoints; + } + getYpoints(): number[] { + return this.ypoints; + } contains(x: number, y: number): boolean { if (this.xpoints.length < 3) return false; let inside = false; for (let i = 0, j = this.xpoints.length - 1; i < this.xpoints.length; j = i++) { - const xi = this.xpoints[i]!, yi = this.ypoints[i]!; - const xj = this.xpoints[j]!, yj = this.ypoints[j]!; - if (((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) { + const xi = this.xpoints[i]!, + yi = this.ypoints[i]!; + const xj = this.xpoints[j]!, + yj = this.ypoints[j]!; + if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) { inside = !inside; } } diff --git a/src/graphic/canvas/ShapeCanvas.ts b/src/graphic/canvas/ShapeCanvas.ts index 9482c18..630376c 100644 --- a/src/graphic/canvas/ShapeCanvas.ts +++ b/src/graphic/canvas/ShapeCanvas.ts @@ -1,11 +1,3 @@ -/** - * @file ShapeCanvas.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief HTML Canvas shape drawing implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ShapeInterface } from '../ShapeInterface.js'; import { RectangleG } from '../RectangleG.js'; @@ -17,7 +9,9 @@ export class ShapeCanvas implements ShapeInterface { this.path = new Path2D(); } - getPath(): Path2D { return this.path; } + getPath(): Path2D { + return this.path; + } moveTo(x: number, y: number): void { this.path.moveTo(x, y); @@ -32,7 +26,16 @@ export class ShapeCanvas implements ShapeInterface { this.updateBounds(x2, y2); } - createCubicCurve(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): void { + createCubicCurve( + x0: number, + y0: number, + x1: number, + y1: number, + x2: number, + y2: number, + x3: number, + y3: number, + ): void { // Cubic curve: start at (x0,y0), control points at (x1,y1) and (x2,y2), end at (x3,y3) this.path.moveTo(x0, y0); this.path.bezierCurveTo(x1, y1, x2, y2, x3, y3); diff --git a/src/graphic/canvas/TextCanvas.ts b/src/graphic/canvas/TextCanvas.ts index 12d4e1e..e944a01 100644 --- a/src/graphic/canvas/TextCanvas.ts +++ b/src/graphic/canvas/TextCanvas.ts @@ -1,11 +1,3 @@ -/** - * @file TextCanvas.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief HTML Canvas text rendering implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { TextInterface } from '../TextInterface.js'; export class TextCanvas implements TextInterface { @@ -48,14 +40,4 @@ export class TextCanvas implements TextInterface { this.ctx.font = this.font; this.ctx.fillText(str, x, y); } - - getFontSize(): number { - return this.fontSize; - } - - setFontSize(size: number): void { - this.fontSize = size; - this.font = `${size}px sans-serif`; - this.ctx.font = this.font; - } } diff --git a/src/graphic/nil/GraphicsNull.ts b/src/graphic/nil/GraphicsNull.ts index 39e8c4d..91ae09f 100644 --- a/src/graphic/nil/GraphicsNull.ts +++ b/src/graphic/nil/GraphicsNull.ts @@ -1,11 +1,3 @@ -/** - * @file GraphicsNull.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Null (no-op) graphics implementation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../GraphicsInterface.js'; import type { ColorInterface } from '../ColorInterface.js'; import type { TextInterface } from '../TextInterface.js'; @@ -44,9 +36,6 @@ export class GraphicsNull implements GraphicsInterface { return this.color; } setZoom(_z: number): void {} - getZoom(): number { - return 1; - } setColor(c: ColorInterface): void { this.color = c; } @@ -74,12 +63,6 @@ export class GraphicsNull implements GraphicsInterface { setFont(_name: string, _size: number, _isItalic?: boolean, _isBold?: boolean): void { this.fontSize = _size; } - getFontSize(): number { - return this.fontSize; - } - setFontSize(size: number): void { - this.fontSize = size; - } getFontAscent(): number { const ctx = this.getContext(); if (!ctx) return this.fontSize * 0.8; diff --git a/src/i18n/AccessResources.ts b/src/i18n/AccessResources.ts index 0377f76..fb60917 100644 --- a/src/i18n/AccessResources.ts +++ b/src/i18n/AccessResources.ts @@ -1,11 +1,3 @@ -/** - * @file AccessResources.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Thin wrapper around loaded locale bundles, mirroring Java's AccessResources. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { getString } from './i18n.js'; export class AccessResources { diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index e3fba8c..e95e55f 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -1,12 +1,3 @@ -/** - * @file i18n.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Internationalization module — locale loading, string lookup, - * change notification, and localStorage persistence. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - type Bundle = Record; /** All locales whose bundles ship with FidoCadJS. */ diff --git a/src/layers/LayerDesc.ts b/src/layers/LayerDesc.ts index 6469b19..0a195ec 100644 --- a/src/layers/LayerDesc.ts +++ b/src/layers/LayerDesc.ts @@ -1,11 +1,3 @@ -/** - * @file LayerDesc.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Layer descriptor model (colour, visibility, alpha, description) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { ColorInterface } from '../graphic/ColorInterface.js'; export class LayerDesc { @@ -24,14 +16,34 @@ export class LayerDesc { this.alphaValue = a; } - getColor(): ColorInterface | null { return this.layerColor; } - getAlpha(): number { return this.alphaValue; } - isVisible(): boolean { return this.isVisibleFlag; } - isModified(): boolean { return this.isModifiedFlag; } - getDescription(): string { return this.layerDescription; } - setDescription(s: string): void { this.layerDescription = s; } - setVisible(v: boolean): void { this.isVisibleFlag = v; } - setModified(v: boolean): void { this.isModifiedFlag = v; } - setColor(c: ColorInterface): void { this.layerColor = c; } - setAlpha(a: number): void { this.alphaValue = a; } + getColor(): ColorInterface | null { + return this.layerColor; + } + getAlpha(): number { + return this.alphaValue; + } + isVisible(): boolean { + return this.isVisibleFlag; + } + isModified(): boolean { + return this.isModifiedFlag; + } + getDescription(): string { + return this.layerDescription; + } + setDescription(s: string): void { + this.layerDescription = s; + } + setVisible(v: boolean): void { + this.isVisibleFlag = v; + } + setModified(v: boolean): void { + this.isModifiedFlag = v; + } + setColor(c: ColorInterface): void { + this.layerColor = c; + } + setAlpha(a: number): void { + this.alphaValue = a; + } } diff --git a/src/layers/StandardLayers.ts b/src/layers/StandardLayers.ts index 9075a43..081feb5 100644 --- a/src/layers/StandardLayers.ts +++ b/src/layers/StandardLayers.ts @@ -1,11 +1,3 @@ -/** - * @file StandardLayers.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Standard layer definitions factory - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { ColorCanvas } from '../graphic/canvas/ColorCanvas.js'; import { LayerDesc } from './LayerDesc.js'; import { getString } from '../i18n/i18n.js'; @@ -13,37 +5,51 @@ import { getString } from '../i18n/i18n.js'; // Layer colors decoded from Java's StandardLayers.java packed-int Color values. // Each tuple is [r, g, b, alpha]. const LAYER_DEFAULTS: [number, number, number, number][] = [ - [ 0, 0, 0, 1.00], // 0 Circuit (black) - [ 0, 0, 128, 1.00], // 1 Bottom copper (navy) - [255, 0, 0, 1.00], // 2 Top copper (red) - [ 0, 128, 128, 1.00], // 3 Silkscreen (teal) - [255, 200, 0, 1.00], // 4 Other 1 (orange) - [127, 255, 0, 1.00], // 5 Other 2 (chartreuse) -8388864 - [ 0, 255, 255, 1.00], // 6 Other 3 (cyan) -16711681 - [ 0, 128, 0, 1.00], // 7 Other 4 (dark green) -16744448 - [154, 205, 50, 1.00], // 8 Other 5 (yellow-green) -6632142 - [255, 20, 147, 1.00], // 9 Other 6 (deep pink) -60269 - [181, 155, 12, 1.00], // 10 Other 7 (olive) -4875508 - [ 1, 128, 255, 1.00], // 11 Other 8 (medium blue) -16678657 + [0, 0, 0, 1.0], // 0 Circuit (black) + [0, 0, 128, 1.0], // 1 Bottom copper (navy) + [255, 0, 0, 1.0], // 2 Top copper (red) + [0, 128, 128, 1.0], // 3 Silkscreen (teal) + [255, 200, 0, 1.0], // 4 Other 1 (orange) + [127, 255, 0, 1.0], // 5 Other 2 (chartreuse) -8388864 + [0, 255, 255, 1.0], // 6 Other 3 (cyan) -16711681 + [0, 128, 0, 1.0], // 7 Other 4 (dark green) -16744448 + [154, 205, 50, 1.0], // 8 Other 5 (yellow-green) -6632142 + [255, 20, 147, 1.0], // 9 Other 6 (deep pink) -60269 + [181, 155, 12, 1.0], // 10 Other 7 (olive) -4875508 + [1, 128, 255, 1.0], // 11 Other 8 (medium blue) -16678657 [225, 225, 225, 0.95], // 12 Other 9 (light gray) -1973791 - [162, 162, 162, 0.90], // 13 Other 10 (medium gray) -6118750 - [ 95, 95, 95, 0.90], // 14 Other 11 (dark gray) -10526881 - [ 0, 0, 0, 1.00], // 15 Other 12 (black) + [162, 162, 162, 0.9], // 13 Other 10 (medium gray) -6118750 + [95, 95, 95, 0.9], // 14 Other 11 (dark gray) -10526881 + [0, 0, 0, 1.0], // 15 Other 12 (black) ]; const LAYER_KEYS = [ - 'Circuit_l', 'Bottom_copper', 'Top_copper', 'Silkscreen', - 'Other_1', 'Other_2', 'Other_3', 'Other_4', - 'Other_5', 'Other_6', 'Other_7', 'Other_8', - 'Other_9', 'Other_10', 'Other_11', 'Other_12', + 'Circuit_l', + 'Bottom_copper', + 'Top_copper', + 'Silkscreen', + 'Other_1', + 'Other_2', + 'Other_3', + 'Other_4', + 'Other_5', + 'Other_6', + 'Other_7', + 'Other_8', + 'Other_9', + 'Other_10', + 'Other_11', + 'Other_12', ]; export class StandardLayers { private constructor() {} static createStandardLayers(): LayerDesc[] { - return LAYER_DEFAULTS.map(([r, g, b, a], i) => - new LayerDesc(new ColorCanvas(r, g, b), true, getString(LAYER_KEYS[i] ?? ''), a)); + return LAYER_DEFAULTS.map( + ([r, g, b, a], i) => + new LayerDesc(new ColorCanvas(r, g, b), true, getString(LAYER_KEYS[i] ?? ''), a), + ); } static createEditingLayerArray(): LayerDesc[] { diff --git a/src/librarymodel/Category.ts b/src/librarymodel/Category.ts index 6d9f411..899285e 100644 --- a/src/librarymodel/Category.ts +++ b/src/librarymodel/Category.ts @@ -1,13 +1,3 @@ -/** - * @file Category.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Represents a category of macros within a library. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Port of fidocadj.librarymodel.Category (Kohta Ozaki, 2014). - */ - import type { MacroDesc } from '../primitives/MacroDesc.js'; import type { Library } from './Library.js'; @@ -29,26 +19,35 @@ export class Category { this.macros = []; } - getName(): string { return this.name; } - setName(name: string): void { this.name = name; } + getName(): string { + return this.name; + } + setName(name: string): void { + this.name = name; + } - getParentLibrary(): Library { return this.parentLibrary; } - setParentLibrary(lib: Library): void { this.parentLibrary = lib; } + getParentLibrary(): Library { + return this.parentLibrary; + } - addMacro(macro: MacroDesc): void { this.macros.push(macro); } + addMacro(macro: MacroDesc): void { + this.macros.push(macro); + } removeMacro(macro: MacroDesc): void { const idx = this.macros.indexOf(macro); if (idx >= 0) this.macros.splice(idx, 1); } - getAllMacros(): MacroDesc[] { return this.macros; } - - isHidden(): boolean { return this.hidden; } + getAllMacros(): MacroDesc[] { + return this.macros; + } - static isValidName(_name: string): boolean { return true; } + isHidden(): boolean { + return this.hidden; + } containsMacroKey(key: string): boolean { - return this.macros.some(m => plainKey(m) === key); + return this.macros.some((m) => plainKey(m) === key); } } diff --git a/src/librarymodel/LibUtils.ts b/src/librarymodel/LibUtils.ts index 056764d..c058780 100644 --- a/src/librarymodel/LibUtils.ts +++ b/src/librarymodel/LibUtils.ts @@ -1,19 +1,8 @@ -/** - * @file LibUtils.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief Static utility functions for library text processing and key validation. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Browser port of fidocadj.globals.LibUtils (phylum2, Davide Bucci, 2012-2023). - */ - import { MacroDesc } from '../primitives/MacroDesc.js'; const STD_FILENAMES = new Set(['', 'FCDstdlib', 'elettrotecnica', 'EY_Libraries', 'IHRAM']); export class LibUtils { - private constructor() {} /** Serialize macros to FCL format text. */ @@ -61,8 +50,10 @@ export class LibUtils { /** Returns true if the key already exists or contains ']'. */ static checkKey(libref: Map, tlib: string, key: string): boolean { for (const md of libref.values()) { - if (md.filename.toLowerCase() === tlib.toLowerCase() && - md.key.toLowerCase() === key.trim().toLowerCase()) { + if ( + md.filename.toLowerCase() === tlib.toLowerCase() && + md.key.toLowerCase() === key.trim().toLowerCase() + ) { return true; } } @@ -70,10 +61,16 @@ export class LibUtils { } /** Returns true if the key is a duplicate in the library. */ - static checkKeyDuplicate(libref: Map, tlib: string, fullKey: string): boolean { + static checkKeyDuplicate( + libref: Map, + tlib: string, + fullKey: string, + ): boolean { for (const md of libref.values()) { - if (md.filename.toLowerCase() === tlib.toLowerCase() && - md.key.toLowerCase() === fullKey.trim().toLowerCase()) { + if ( + md.filename.toLowerCase() === tlib.toLowerCase() && + md.key.toLowerCase() === fullKey.trim().toLowerCase() + ) { return true; } } @@ -89,8 +86,10 @@ export class LibUtils { static enumGroups(libref: Map, prefix: string): string[] { const groups: string[] = []; for (const md of libref.values()) { - if (md.filename.trim().toLowerCase() === prefix.trim().toLowerCase() && - !groups.includes(md.category)) { + if ( + md.filename.trim().toLowerCase() === prefix.trim().toLowerCase() && + !groups.includes(md.category) + ) { groups.push(md.category); } } diff --git a/src/librarymodel/Library.ts b/src/librarymodel/Library.ts index a320e3a..32e3579 100644 --- a/src/librarymodel/Library.ts +++ b/src/librarymodel/Library.ts @@ -1,13 +1,3 @@ -/** - * @file Library.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Represents a single component library (.fcl file). - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Port of fidocadj.librarymodel.Library (Kohta Ozaki, 2014). - */ - import type { Category } from './Category.js'; export class Library { @@ -23,33 +13,47 @@ export class Library { this.categories = []; } - getName(): string { return this.libraryName; } - setName(name: string): void { this.libraryName = name; } + getName(): string { + return this.libraryName; + } + setName(name: string): void { + this.libraryName = name; + } - getFilename(): string { return this.filename; } + getFilename(): string { + return this.filename; + } - getAllCategories(): Category[] { return this.categories; } + getAllCategories(): Category[] { + return this.categories; + } getCategory(name: string): Category | null { - return this.categories.find(c => c.getName() === name) ?? null; + return this.categories.find((c) => c.getName() === name) ?? null; } - addCategory(category: Category): void { this.categories.push(category); } + addCategory(category: Category): void { + this.categories.push(category); + } removeCategory(category: Category): void { const idx = this.categories.indexOf(category); if (idx >= 0) this.categories.splice(idx, 1); } - isStdLib(): boolean { return this.isStd; } - - isHidden(): boolean { return false; } + isStdLib(): boolean { + return this.isStd; + } - static isValidName(_name: string): boolean { return true; } + isHidden(): boolean { + return false; + } containsMacroKey(key: string): boolean { - return this.categories.some(c => c.containsMacroKey(key)); + return this.categories.some((c) => c.containsMacroKey(key)); } - toString(): string { return this.libraryName; } + toString(): string { + return this.libraryName; + } } diff --git a/src/librarymodel/LibraryFolder.ts b/src/librarymodel/LibraryFolder.ts index 543bfd9..94357c2 100644 --- a/src/librarymodel/LibraryFolder.ts +++ b/src/librarymodel/LibraryFolder.ts @@ -1,23 +1,3 @@ -/** - * @file LibraryFolder.ts - * @author Dante Loi - * @date 2026-06-02 - * @brief Filesystem-backed storage for user libraries via the File System - * Access API, with the chosen directory handle persisted in IndexedDB. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * @details - * Lets the user pick a real filesystem folder (e.g. a Dropbox/iCloud/Drive - * synced folder) where user libraries are written as `.fcl` files, so - * they are portable across browsers and devices. The directory *handle* itself - * is browser-local (stored in IndexedDB, as handles are structured-cloneable), - * but the library *files* live in the real filesystem. - * - * Only Chromium-based browsers expose `showDirectoryPicker`; elsewhere - * {@link LibraryFolder.isSupported} returns false and all operations are - * no-ops, leaving the localStorage backend in charge. - */ - // ── Minimal typings for File System Access API extras not in lib.dom ────────── type PermissionMode = 'read' | 'readwrite'; diff --git a/src/librarymodel/LibraryModel.ts b/src/librarymodel/LibraryModel.ts index 41aed9f..42afbad 100644 --- a/src/librarymodel/LibraryModel.ts +++ b/src/librarymodel/LibraryModel.ts @@ -1,46 +1,41 @@ -/** - * @file LibraryModel.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief Central model for managing component libraries with full CRUD support. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Port of fidocadj.librarymodel.LibraryModel (Kohta Ozaki, Davide Bucci, 2014-2023). - * Browser adaptation: user libraries are persisted to localStorage instead of files. - */ - import type { DrawingModel } from '../circuit/model/DrawingModel.js'; import { MacroDesc } from '../primitives/MacroDesc.js'; import { Library } from './Library.js'; import { Category } from './Category.js'; import { LibUtils } from './LibUtils.js'; import { UserLibraryStorage } from './UserLibraryStorage.js'; -import type { LibraryListener } from './event/LibraryListener.js'; -import { AddEvent } from './event/AddEvent.js'; -import { RemoveEvent } from './event/RemoveEvent.js'; -import { RenameEvent } from './event/RenameEvent.js'; -import { KeyChangeEvent } from './event/KeyChangeEvent.js'; // ── Exception classes ──────────────────────────────────────────────────────── export class LibraryException extends Error { - constructor(message: string) { super(message); this.name = 'LibraryException'; } + constructor(message: string) { + super(message); + this.name = 'LibraryException'; + } } export class IllegalLibraryAccessException extends LibraryException { - constructor(message: string) { super(message); this.name = 'IllegalLibraryAccessException'; } + constructor(message: string) { + super(message); + this.name = 'IllegalLibraryAccessException'; + } } export class IllegalNameException extends LibraryException { - constructor(message: string) { super(message); this.name = 'IllegalNameException'; } + constructor(message: string) { + super(message); + this.name = 'IllegalNameException'; + } } export class IllegalKeyException extends LibraryException { - constructor(message: string) { super(message); this.name = 'IllegalKeyException'; } + constructor(message: string) { + super(message); + this.name = 'IllegalKeyException'; + } } export class LibraryModel { - private readonly listeners: LibraryListener[] = []; private readonly drawingModel: DrawingModel; private readonly libraries: Library[] = []; private masterLibrary!: Map; @@ -50,25 +45,16 @@ export class LibraryModel { this.updateLibraries(); } - // ── Listener management ──────────────────────────────────────────────────── + // ── Accessors ───────────────────────────────────────────────────────────── - addLibraryListener(listener: LibraryListener): void { - this.listeners.push(listener); + getAllLibraries(): Library[] { + return this.libraries; } - removeLibraryListener(listener: LibraryListener): void { - const idx = this.listeners.indexOf(listener); - if (idx >= 0) this.listeners.splice(idx, 1); + getAllMacros(): Map { + return this.masterLibrary; } - // ── Accessors ───────────────────────────────────────────────────────────── - - getAllLibraries(): Library[] { return this.libraries; } - - getAllMacros(): Map { return this.masterLibrary; } - - getDrawingModel(): DrawingModel { return this.drawingModel; } - // ── Static helpers ──────────────────────────────────────────────────────── static getPlainMacroKey(macro: MacroDesc): string { @@ -88,66 +74,77 @@ export class LibraryModel { remove(target: Library | Category | MacroDesc): void { if (target instanceof Library) { - if (target.isStdLib()) throw new IllegalLibraryAccessException("A standard library can't be removed."); + if (target.isStdLib()) + throw new IllegalLibraryAccessException("A standard library can't be removed."); const idx = this.libraries.indexOf(target); if (idx >= 0) this.libraries.splice(idx, 1); this.synchronizeMasterLibrary(); UserLibraryStorage.deleteUserLibrary(target.getFilename()); - this.fireRemoved(null, target); } else if (target instanceof Category) { const parentLib = target.getParentLibrary(); - if (parentLib.isStdLib()) throw new IllegalLibraryAccessException("A category in a standard library can't be removed."); + if (parentLib.isStdLib()) + throw new IllegalLibraryAccessException( + "A category in a standard library can't be removed.", + ); parentLib.removeCategory(target); this.synchronizeMasterLibrary(); this.save(); - this.fireRemoved(parentLib, target); } else { const category = this.getParentNode(target) as Category | null; - if (!category) throw new IllegalLibraryAccessException("Macro has no parent category."); - if (category.getParentLibrary().isStdLib()) throw new IllegalLibraryAccessException("A macro in a standard library can't be removed."); + if (!category) throw new IllegalLibraryAccessException('Macro has no parent category.'); + if (category.getParentLibrary().isStdLib()) + throw new IllegalLibraryAccessException( + "A macro in a standard library can't be removed.", + ); category.removeMacro(target); this.synchronizeMasterLibrary(); this.save(); - this.fireRemoved(category, target); } } // ── Rename operations ───────────────────────────────────────────────────── rename(target: Library | Category | MacroDesc, newName: string): void { - if (!newName || newName.trim().length === 0) throw new IllegalNameException('Name must not be empty.'); + if (!newName || newName.trim().length === 0) + throw new IllegalNameException('Name must not be empty.'); if (target instanceof Library) { - if (target.isStdLib()) throw new IllegalLibraryAccessException("A standard library can't be renamed."); - const oldName = target.getName(); + if (target.isStdLib()) + throw new IllegalLibraryAccessException("A standard library can't be renamed."); target.setName(newName); this.synchronizeMacros(target); this.synchronizeMasterLibrary(); this.save(); - this.fireRenamed(null, target, oldName); } else if (target instanceof Category) { - if (target.getParentLibrary().isStdLib()) throw new IllegalLibraryAccessException("A category in a standard library can't be renamed."); - const oldName = target.getName(); + if (target.getParentLibrary().isStdLib()) + throw new IllegalLibraryAccessException( + "A category in a standard library can't be renamed.", + ); target.setName(newName); this.synchronizeMacros(target.getParentLibrary()); this.synchronizeMasterLibrary(); this.save(); - this.fireRenamed(target.getParentLibrary(), target, oldName); } else { - if (this.isStdLib(target)) throw new IllegalLibraryAccessException("A macro in a standard library can't be renamed."); - const oldName = target.name; + if (this.isStdLib(target)) + throw new IllegalLibraryAccessException( + "A macro in a standard library can't be renamed.", + ); target.name = newName; this.save(); - this.fireRenamed(this.getParentNode(target), target, oldName); } } // ── Key change ──────────────────────────────────────────────────────────── changeKey(macro: MacroDesc, newKey: string): void { - if (!newKey || newKey.trim().length === 0) throw new IllegalKeyException('Key must not be empty.'); - if (LibUtils.checkKeyInvalidChars(newKey)) throw new IllegalKeyException('Key must not contain spaces, dots, or ].'); - if (this.isStdLib(macro)) throw new IllegalLibraryAccessException("A macro in a standard library can't have its key changed."); + if (!newKey || newKey.trim().length === 0) + throw new IllegalKeyException('Key must not be empty.'); + if (LibUtils.checkKeyInvalidChars(newKey)) + throw new IllegalKeyException('Key must not contain spaces, dots, or ].'); + if (this.isStdLib(macro)) + throw new IllegalLibraryAccessException( + "A macro in a standard library can't have its key changed.", + ); const category = this.getParentNode(macro) as Category | null; if (!category) throw new IllegalLibraryAccessException('Macro has no parent category.'); @@ -157,21 +154,18 @@ export class LibraryModel { throw new IllegalKeyException('New key already exists in the library.'); } - const oldKey = LibraryModel.getPlainMacroKey(macro); macro.key = newFullKey; this.save(); - this.fireKeyChanged(category, macro, oldKey); } // ── Copy operations ─────────────────────────────────────────────────────── copy(source: MacroDesc | Category, dest: Category | Library): void { if (source instanceof MacroDesc && dest instanceof Category) { - const newMacro = this.copyMacro(source, dest); + this.copyMacro(source, dest); this.synchronizeMacros(dest.getParentLibrary()); this.synchronizeMasterLibrary(); this.save(); - this.fireAdded(dest, newMacro); } else if (source instanceof Category && dest instanceof Library) { const newCategory = new Category(source.getName(), dest, false); for (const macro of source.getAllMacros()) { @@ -181,7 +175,6 @@ export class LibraryModel { this.synchronizeMacros(dest); this.synchronizeMasterLibrary(); this.save(); - this.fireAdded(dest, newCategory); } } @@ -194,7 +187,10 @@ export class LibraryModel { retries--; } if (retries <= 0) throw new LibraryException('Key generation failed.'); - newMacro.key = LibraryModel.createMacroKey(destCategory.getParentLibrary().getFilename(), newPlainKey); + newMacro.key = LibraryModel.createMacroKey( + destCategory.getParentLibrary().getFilename(), + newPlainKey, + ); destCategory.addMacro(newMacro); return newMacro; } @@ -217,7 +213,11 @@ export class LibraryModel { if (saved.has(fn)) continue; saved.add(fn); try { - UserLibraryStorage.saveUserLibrary(fn, this.masterLibrary, library.getName().trim()); + UserLibraryStorage.saveUserLibrary( + fn, + this.masterLibrary, + library.getName().trim(), + ); } catch (e) { console.error(`Error saving library "${fn}":`, e); } @@ -226,7 +226,6 @@ export class LibraryModel { forceUpdate(): void { this.updateLibraries(); - this.fireChanged(); } getParentNode(node: unknown): unknown { @@ -308,33 +307,13 @@ export class LibraryModel { } private cloneMacro(macro: MacroDesc): MacroDesc { - return new MacroDesc(macro.key, macro.name, macro.description, - macro.category, macro.library, macro.filename); - } - - // ── Event firing ───────────────────────────────────────────────────────── - - private fireChanged(): void { - for (const l of this.listeners) l.libraryLoaded(); - } - - fireAdded(parentNode: unknown, addedNode: unknown): void { - const e = new AddEvent(parentNode, addedNode); - for (const l of this.listeners) l.libraryNodeAdded(e); - } - - fireRemoved(parentNode: unknown, removedNode: unknown): void { - const e = new RemoveEvent(parentNode, removedNode); - for (const l of this.listeners) l.libraryNodeRemoved(e); - } - - fireRenamed(parentNode: unknown, renamedNode: unknown, oldName: string): void { - const e = new RenameEvent(parentNode, renamedNode, oldName); - for (const l of this.listeners) l.libraryNodeRenamed(e); - } - - fireKeyChanged(parentNode: unknown, changedNode: unknown, oldKey: string): void { - const e = new KeyChangeEvent(parentNode, changedNode, oldKey); - for (const l of this.listeners) l.libraryNodeKeyChanged(e); + return new MacroDesc( + macro.key, + macro.name, + macro.description, + macro.category, + macro.library, + macro.filename, + ); } } diff --git a/src/librarymodel/UserLibraryStorage.ts b/src/librarymodel/UserLibraryStorage.ts index 4cf48d3..1994808 100644 --- a/src/librarymodel/UserLibraryStorage.ts +++ b/src/librarymodel/UserLibraryStorage.ts @@ -1,14 +1,3 @@ -/** - * @file UserLibraryStorage.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief localStorage persistence layer for user-created component libraries. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Browser port of the file-based library persistence in fidocadj.globals.LibUtils. - * User libraries are stored as FCL-formatted strings keyed by library filename prefix. - */ - import type { ParserActions } from '../circuit/controllers/ParserActions.js'; import { MacroDesc } from '../primitives/MacroDesc.js'; import { LibUtils } from './LibUtils.js'; diff --git a/src/librarymodel/event/AddEvent.ts b/src/librarymodel/event/AddEvent.ts deleted file mode 100644 index 56cb57b..0000000 --- a/src/librarymodel/event/AddEvent.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @file AddEvent.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Event data for a library node addition. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -export class AddEvent { - constructor( - public readonly parentNode: unknown, - public readonly addedNode: unknown, - ) {} -} diff --git a/src/librarymodel/event/KeyChangeEvent.ts b/src/librarymodel/event/KeyChangeEvent.ts deleted file mode 100644 index e68693f..0000000 --- a/src/librarymodel/event/KeyChangeEvent.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file KeyChangeEvent.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Event data for a macro key change. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -export class KeyChangeEvent { - constructor( - public readonly parentNode: unknown, - public readonly changedNode: unknown, - public readonly oldKey: string, - ) {} -} diff --git a/src/librarymodel/event/LibraryListener.ts b/src/librarymodel/event/LibraryListener.ts deleted file mode 100644 index 3b08d14..0000000 --- a/src/librarymodel/event/LibraryListener.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @file LibraryListener.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Observer interface for library state-change events. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -import type { AddEvent } from './AddEvent.js'; -import type { RemoveEvent } from './RemoveEvent.js'; -import type { RenameEvent } from './RenameEvent.js'; -import type { KeyChangeEvent } from './KeyChangeEvent.js'; - -export interface LibraryListener { - libraryLoaded(): void; - libraryNodeRenamed(e: RenameEvent): void; - libraryNodeRemoved(e: RemoveEvent): void; - libraryNodeAdded(e: AddEvent): void; - libraryNodeKeyChanged(e: KeyChangeEvent): void; -} diff --git a/src/librarymodel/event/LibraryListenerAdapter.ts b/src/librarymodel/event/LibraryListenerAdapter.ts deleted file mode 100644 index c57a381..0000000 --- a/src/librarymodel/event/LibraryListenerAdapter.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file LibraryListenerAdapter.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Default no-op adapter for LibraryListener. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -import type { LibraryListener } from './LibraryListener.js'; -import type { AddEvent } from './AddEvent.js'; -import type { RemoveEvent } from './RemoveEvent.js'; -import type { RenameEvent } from './RenameEvent.js'; -import type { KeyChangeEvent } from './KeyChangeEvent.js'; - -export class LibraryListenerAdapter implements LibraryListener { - libraryLoaded(): void {} - libraryNodeRenamed(_e: RenameEvent): void {} - libraryNodeRemoved(_e: RemoveEvent): void {} - libraryNodeAdded(_e: AddEvent): void {} - libraryNodeKeyChanged(_e: KeyChangeEvent): void {} -} diff --git a/src/librarymodel/event/RemoveEvent.ts b/src/librarymodel/event/RemoveEvent.ts deleted file mode 100644 index 1fc0f8e..0000000 --- a/src/librarymodel/event/RemoveEvent.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @file RemoveEvent.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Event data for a library node removal. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -export class RemoveEvent { - constructor( - public readonly parentNode: unknown, - public readonly removedNode: unknown, - ) {} -} diff --git a/src/librarymodel/event/RenameEvent.ts b/src/librarymodel/event/RenameEvent.ts deleted file mode 100644 index 365e0bd..0000000 --- a/src/librarymodel/event/RenameEvent.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file RenameEvent.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Event data for a library node rename. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -export class RenameEvent { - constructor( - public readonly parentNode: unknown, - public readonly renamedNode: unknown, - public readonly oldName: string, - ) {} -} diff --git a/src/macropicker/MacroPicker.ts b/src/macropicker/MacroPicker.ts index b8e96c0..c85e16c 100644 --- a/src/macropicker/MacroPicker.ts +++ b/src/macropicker/MacroPicker.ts @@ -1,15 +1,3 @@ -/** - * @file MacroPicker.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief HTML-based component library tree browser with context menu for library management. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Browser port of fidocadj.macropicker.MacroTree (Swing). Renders a collapsible - * tree: Library → Category → Macro. Supports right-click context menus for - * rename, remove, copy, paste, and change-key operations on user libraries. - */ - import { getString } from '../i18n/i18n.js'; import type { LibraryModel } from '../librarymodel/LibraryModel.js'; import { Library } from '../librarymodel/Library.js'; diff --git a/src/macropicker/OperationPermissions.ts b/src/macropicker/OperationPermissions.ts index 3f6b689..5af6609 100644 --- a/src/macropicker/OperationPermissions.ts +++ b/src/macropicker/OperationPermissions.ts @@ -1,14 +1,3 @@ -/** - * @file OperationPermissions.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief Permission model describing which context menu operations are available - * for the currently selected library tree node. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Browser port of fidocadj.macropicker.OperationPermissions (Kohta Ozaki, 2014). - */ - export class OperationPermissions { copyAvailable: boolean = false; pasteAvailable: boolean = false; diff --git a/src/primitives/Arrow.ts b/src/primitives/Arrow.ts index cd899f7..ef91070 100644 --- a/src/primitives/Arrow.ts +++ b/src/primitives/Arrow.ts @@ -1,11 +1,3 @@ -/** - * @file Arrow.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Arrow line-end style definitions - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { MapCoordinates } from '../geom/MapCoordinates.js'; import { GeometricDistances } from '../geom/GeometricDistances.js'; @@ -27,29 +19,55 @@ export class Arrow { private static readonly roundTolerance = 1e-5; - atLeastOneArrow(): boolean { return this.arrowStart || this.arrowEnd; } + atLeastOneArrow(): boolean { + return this.arrowStart || this.arrowEnd; + } createArrowTokens(): string { const arrows = (this.arrowStart ? 0x01 : 0x00) | (this.arrowEnd ? 0x02 : 0x00); const al = this.arrowLength; const ahw = this.arrowHalfWidth; - const alStr = Math.abs(al - Math.round(al)) < Arrow.roundTolerance - ? String(Math.round(al)) : String(al); - const ahwStr = Math.abs(ahw - Math.round(ahw)) < Arrow.roundTolerance - ? String(Math.round(ahw)) : String(ahw); + const alStr = + Math.abs(al - Math.round(al)) < Arrow.roundTolerance + ? String(Math.round(al)) + : String(al); + const ahwStr = + Math.abs(ahw - Math.round(ahw)) < Arrow.roundTolerance + ? String(Math.round(ahw)) + : String(ahw); return `${arrows} ${this.arrowStyle} ${alStr} ${ahwStr}`; } - isArrowStart(): boolean { return this.arrowStart; } - setArrowStart(v: boolean): void { this.arrowStart = v; } - isArrowEnd(): boolean { return this.arrowEnd; } - setArrowEnd(v: boolean): void { this.arrowEnd = v; } - getArrowStyle(): number { return this.arrowStyle; } - setArrowStyle(s: number): void { this.arrowStyle = s; } - getArrowLength(): number { return this.arrowLength; } - setArrowLength(v: number): void { this.arrowLength = v; } - getArrowHalfWidth(): number { return this.arrowHalfWidth; } - setArrowHalfWidth(v: number): void { this.arrowHalfWidth = v; } + isArrowStart(): boolean { + return this.arrowStart; + } + setArrowStart(v: boolean): void { + this.arrowStart = v; + } + isArrowEnd(): boolean { + return this.arrowEnd; + } + setArrowEnd(v: boolean): void { + this.arrowEnd = v; + } + getArrowStyle(): number { + return this.arrowStyle; + } + setArrowStyle(s: number): void { + this.arrowStyle = s; + } + getArrowLength(): number { + return this.arrowLength; + } + setArrowLength(v: number): void { + this.arrowLength = v; + } + getArrowHalfWidth(): number { + return this.arrowHalfWidth; + } + setArrowHalfWidth(v: number): void { + this.arrowHalfWidth = v; + } parseTokens(tokens: string[], startIndex: number): number { let i = startIndex; @@ -64,38 +82,57 @@ export class Arrow { prepareCoordinateMapping(coordSys: MapCoordinates): number { this.m = coordSys; - this.h = Math.abs(coordSys.mapXi(this.arrowHalfWidth, this.arrowHalfWidth, false) - - coordSys.mapXi(0, 0, false)); - this.l = Math.abs(coordSys.mapXi(this.arrowLength, this.arrowLength, false) - - coordSys.mapXi(0, 0, false)); + this.h = Math.abs( + coordSys.mapXi(this.arrowHalfWidth, this.arrowHalfWidth, false) - + coordSys.mapXi(0, 0, false), + ); + this.l = Math.abs( + coordSys.mapXi(this.arrowLength, this.arrowLength, false) - coordSys.mapXi(0, 0, false), + ); if (this.arrowHalfWidth < 0) this.h = -this.h; if (this.arrowLength < 0) this.l = -this.l; return this.h; } - getControlsForArrow(v: unknown[]): unknown[] { - // Populated in Phase 4 when ParameterDescription is ported. - return v; - } - - drawArrowPixels(g: GraphicsInterface, x: number, y: number, xc: number, yc: number, - tl: number, th: number, as: number): PointG { - this.h = th; this.l = tl; this.arrowStyle = as; + drawArrowPixels( + g: GraphicsInterface, + x: number, + y: number, + xc: number, + yc: number, + tl: number, + th: number, + as: number, + ): PointG { + this.h = th; + this.l = tl; + this.arrowStyle = as; return this.drawArrow(g, x, y, xc, yc); } - isInArrow(xs: number, ys: number, x: number, y: number, xc: number, yc: number, - pBase: PointG | null): boolean { + isInArrow( + xs: number, + ys: number, + x: number, + y: number, + xc: number, + yc: number, + pBase: PointG | null, + ): boolean { const p = this._calcPoints(x, y, xc, yc); const xp = [x, Math.round(p[1].x), Math.round(p[2].x)]; const yp = [y, Math.round(p[1].y), Math.round(p[2].y)]; - if (pBase) { pBase.x = Math.round(p[0].x); pBase.y = Math.round(p[0].y); } + if (pBase) { + pBase.x = Math.round(p[0].x); + pBase.y = Math.round(p[0].y); + } return GeometricDistances.pointInPolygon(xp, yp, 3, xs, ys); } private _calcPoints(x: number, y: number, xc: number, yc: number): PointPr[] { const alpha = this._angle(x, y, xc, yc); - const ca = Math.cos(alpha), sa = Math.sin(alpha); + const ca = Math.cos(alpha), + sa = Math.sin(alpha); const p: PointPr[] = new Array(5); p[0] = new PointPr(x - this.l * ca, y - this.l * sa); p[1] = new PointPr(p[0].x - this.h * sa, p[0].y + this.h * ca); @@ -132,9 +169,16 @@ export class Arrow { if ((this.arrowStyle & Arrow.flagEmpty) === 0) g.fillPolygon(pp); else g.drawPolygon(pp); if ((this.arrowStyle & Arrow.flagLimiter) !== 0 && p[3] && p[4]) { - g.drawLine(Math.round(p[3].x), Math.round(p[3].y), - Math.round(p[4].x), Math.round(p[4].y)); - if (this.m) { this.m.trackPoint(p[3].x, p[3].y); this.m.trackPoint(p[4].x, p[4].y); } + g.drawLine( + Math.round(p[3].x), + Math.round(p[3].y), + Math.round(p[4].x), + Math.round(p[4].y), + ); + if (this.m) { + this.m.trackPoint(p[3].x, p[3].y); + this.m.trackPoint(p[4].x, p[4].y); + } } return new PointG(Math.trunc(p[0].x), Math.trunc(p[0].y)); } diff --git a/src/primitives/GraphicPrimitive.ts b/src/primitives/GraphicPrimitive.ts index 0524a03..3c3a582 100644 --- a/src/primitives/GraphicPrimitive.ts +++ b/src/primitives/GraphicPrimitive.ts @@ -1,11 +1,3 @@ -/** - * @file GraphicPrimitive.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Abstract base class for all graphic primitives - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import { MapCoordinates } from '../geom/MapCoordinates.js'; import type { ExportInterface } from '../export/ExportInterface.js'; @@ -89,9 +81,6 @@ export abstract class GraphicPrimitive { this.virtualPoint = Array.from({ length: npoints }, () => new PointG()); } - getMacroFont(): string { - return this.macroFont; - } getMacroFontSize(): number { return this.macroFontSize; } @@ -506,14 +495,6 @@ export abstract class GraphicPrimitive { return true; } - // Phase 4: getControls/setControls use ParameterDescription - getControls(): unknown[] { - return []; - } - setControls(_v: unknown[]): number { - return 0; - } - needsHoles(): boolean { return false; } diff --git a/src/primitives/MacroDesc.ts b/src/primitives/MacroDesc.ts index 4ffaab2..699ffcf 100644 --- a/src/primitives/MacroDesc.ts +++ b/src/primitives/MacroDesc.ts @@ -1,11 +1,3 @@ -/** - * @file MacroDesc.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Macro descriptor holding the FCD source of a library component - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - export class MacroDesc { name: string; key: string; @@ -15,8 +7,14 @@ export class MacroDesc { filename: string; level: number = 0; - constructor(key: string, name: string, description: string, - category: string, library: string, filename: string) { + constructor( + key: string, + name: string, + description: string, + category: string, + library: string, + filename: string, + ) { this.key = key; this.name = name; this.description = description; @@ -27,9 +25,12 @@ export class MacroDesc { toString(): string { switch (this.level) { - case 1: return this.category.trim(); - case 2: return this.library.trim(); - default: return this.name.trim(); + case 1: + return this.category.trim(); + case 2: + return this.library.trim(); + default: + return this.name.trim(); } } } diff --git a/src/primitives/PrimitiveAdvText.ts b/src/primitives/PrimitiveAdvText.ts index 3ab5d61..5dfedc4 100644 --- a/src/primitives/PrimitiveAdvText.ts +++ b/src/primitives/PrimitiveAdvText.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveAdvText.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Advanced text primitive with font, size, style, and orientation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/primitives/PrimitiveBezier.ts b/src/primitives/PrimitiveBezier.ts index cd3ec4e..47de12b 100644 --- a/src/primitives/PrimitiveBezier.ts +++ b/src/primitives/PrimitiveBezier.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveBezier.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Cubic Bézier curve primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import type { ShapeInterface } from '../graphic/ShapeInterface.js'; diff --git a/src/primitives/PrimitiveComplexCurve.ts b/src/primitives/PrimitiveComplexCurve.ts index e414cfe..896a1f2 100644 --- a/src/primitives/PrimitiveComplexCurve.ts +++ b/src/primitives/PrimitiveComplexCurve.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveComplexCurve.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Complex (multi-segment) curve primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import type { PolygonInterface } from '../graphic/PolygonInterface.js'; diff --git a/src/primitives/PrimitiveConnection.ts b/src/primitives/PrimitiveConnection.ts index 6869f24..a222c9d 100644 --- a/src/primitives/PrimitiveConnection.ts +++ b/src/primitives/PrimitiveConnection.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveConnection.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Connection dot primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/primitives/PrimitiveLine.ts b/src/primitives/PrimitiveLine.ts index e6745b2..dfe52f8 100644 --- a/src/primitives/PrimitiveLine.ts +++ b/src/primitives/PrimitiveLine.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveLine.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Straight line primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/primitives/PrimitiveMacro.ts b/src/primitives/PrimitiveMacro.ts index 577580b..5757738 100644 --- a/src/primitives/PrimitiveMacro.ts +++ b/src/primitives/PrimitiveMacro.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveMacro.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Macro (library component) primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; @@ -78,16 +70,6 @@ export class PrimitiveMacro extends GraphicPrimitive { PrimitiveMacro.exportFn = backend.export; } - /** Returns true when a backend (or all legacy hooks) is wired. */ - static isReady(): boolean { - if (PrimitiveMacro.backend) return true; - return ( - PrimitiveMacro.parserFn !== null && - PrimitiveMacro.drawFn !== null && - PrimitiveMacro.exportFn !== null - ); - } - /** Asserts that the relevant injection hook is wired, throwing a descriptive error if not. */ private static assertReady(operation: 'parse' | 'draw' | 'export'): void { if (PrimitiveMacro.backend) return; // backend covers all ops @@ -185,9 +167,6 @@ export class PrimitiveMacro extends GraphicPrimitive { getMacroDesc(): string | null { return this.macroDesc; } - setMacroDesc(d: string): void { - this.macroDesc = d; - } /** Look up a macro by key, set name and description, and parse the sub-circuit. */ initializeFromKey(key: string): void { diff --git a/src/primitives/PrimitiveOval.ts b/src/primitives/PrimitiveOval.ts index 1f3df0b..92779d3 100644 --- a/src/primitives/PrimitiveOval.ts +++ b/src/primitives/PrimitiveOval.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveOval.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Oval / ellipse primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/primitives/PrimitivePCBLine.ts b/src/primitives/PrimitivePCBLine.ts index cb82946..13fb581 100644 --- a/src/primitives/PrimitivePCBLine.ts +++ b/src/primitives/PrimitivePCBLine.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitivePCBLine.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief PCB trace line primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/primitives/PrimitivePCBPad.ts b/src/primitives/PrimitivePCBPad.ts index cc26e5a..30b7d6a 100644 --- a/src/primitives/PrimitivePCBPad.ts +++ b/src/primitives/PrimitivePCBPad.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitivePCBPad.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief PCB pad primitive (oval, rect, or rounded-rect) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/primitives/PrimitivePolygon.ts b/src/primitives/PrimitivePolygon.ts index 143a8cd..05b694c 100644 --- a/src/primitives/PrimitivePolygon.ts +++ b/src/primitives/PrimitivePolygon.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitivePolygon.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Multi-point polygon primitive - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import type { PolygonInterface } from '../graphic/PolygonInterface.js'; diff --git a/src/primitives/PrimitiveRectangle.ts b/src/primitives/PrimitiveRectangle.ts index 21d1f40..ff8b6e1 100644 --- a/src/primitives/PrimitiveRectangle.ts +++ b/src/primitives/PrimitiveRectangle.ts @@ -1,11 +1,3 @@ -/** - * @file PrimitiveRectangle.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Rectangle primitive (empty or filled) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { GraphicsInterface } from '../graphic/GraphicsInterface.js'; import type { ExportInterface } from '../export/ExportInterface.js'; import { GraphicPrimitive } from './GraphicPrimitive.js'; diff --git a/src/settings/SettingsManager.ts b/src/settings/SettingsManager.ts index cb4cf02..84debbf 100644 --- a/src/settings/SettingsManager.ts +++ b/src/settings/SettingsManager.ts @@ -1,11 +1,3 @@ -/** - * @file SettingsManager.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Persistent app settings with localStorage backing - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { EditorFacade } from '../circuit/EditorFacade.js'; import { Globals } from '../globals/Globals.js'; import { Toast } from '../ui/Toast.js'; diff --git a/src/ui/ConfirmDialog.ts b/src/ui/ConfirmDialog.ts index 744eec9..4eba99f 100644 --- a/src/ui/ConfirmDialog.ts +++ b/src/ui/ConfirmDialog.ts @@ -1,11 +1,3 @@ -/** - * @file ConfirmDialog.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief Reusable modal confirmation dialog for yes/no decisions. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { getString } from '../i18n/i18n.js'; function escapeHtml(s: string): string { diff --git a/src/ui/ContextMenu.ts b/src/ui/ContextMenu.ts index 8f30d81..f3ae7bc 100644 --- a/src/ui/ContextMenu.ts +++ b/src/ui/ContextMenu.ts @@ -1,11 +1,3 @@ -/** - * @file ContextMenu.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Right-click context menu DOM component - * @copyright Copyright (C) 2026 Dante Loi — GPL v3 - */ - export interface ContextMenuItem { label?: string; enabled?: boolean; diff --git a/src/ui/DashStyleDropdown.ts b/src/ui/DashStyleDropdown.ts index 5f0b634..c773067 100644 --- a/src/ui/DashStyleDropdown.ts +++ b/src/ui/DashStyleDropdown.ts @@ -1,13 +1,3 @@ -/** - * @file DashStyleDropdown.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Reusable dash style selector dropdown with SVG previews. - * Mirrors FidoCadJ's DashStyleComboBox which renders each option - * as a stroked line preview rather than a numeric index. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { Globals } from '../globals/Globals.js'; export class DashStyleDropdown { diff --git a/src/ui/DialogAbout.ts b/src/ui/DialogAbout.ts index 80f8796..53de65e 100644 --- a/src/ui/DialogAbout.ts +++ b/src/ui/DialogAbout.ts @@ -1,12 +1,3 @@ -/** - * @file DialogAbout.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief About dialog — version, license, credits. - * Mirrors FidoCadJ's DialogAbout. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { getString } from '../i18n/i18n.js'; /** diff --git a/src/ui/DialogLayer.ts b/src/ui/DialogLayer.ts index e66b00e..a929561 100644 --- a/src/ui/DialogLayer.ts +++ b/src/ui/DialogLayer.ts @@ -1,12 +1,3 @@ -/** - * @file DialogLayer.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Layer edit dialog — edit per-layer color, name, alpha, and visibility. - * Mirrors FidoCadJ's DialogLayer. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { EditorFacade } from '../circuit/EditorFacade.js'; import { getString } from '../i18n/i18n.js'; diff --git a/src/ui/DialogSymbolize.ts b/src/ui/DialogSymbolize.ts index b84a5de..1a3f01f 100644 --- a/src/ui/DialogSymbolize.ts +++ b/src/ui/DialogSymbolize.ts @@ -1,15 +1,3 @@ -/** - * @file DialogSymbolize.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief "Symbol-o-matic" dialog for creating new library macros from selected primitives. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Browser port of fidocadj.dialogs.DialogSymbolize (Phylum2, Davide Bucci, 2012-2023). - * Shows selected primitives in a preview panel with a draggable origin crosshair. - * On OK, builds a MacroDesc and adds it to the library model. - */ - import { getString } from '../i18n/i18n.js'; import type { EditorFacade } from '../circuit/EditorFacade.js'; import { DrawingModel } from '../circuit/model/DrawingModel.js'; diff --git a/src/ui/ExportDialog.ts b/src/ui/ExportDialog.ts index 9bb5411..5c29f08 100644 --- a/src/ui/ExportDialog.ts +++ b/src/ui/ExportDialog.ts @@ -1,12 +1,3 @@ -/** - * @file ExportDialog.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Export dialog — format selection, filename, and options. - * Mirrors FidoCadJ's DialogExport. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { EditorFacade } from '../circuit/EditorFacade.js'; import type { ExportBitmapOptions } from '../export/ExportBitmapOptions.js'; import { defaultBitmapOptions, DPI_PRESETS } from '../export/ExportBitmapOptions.js'; diff --git a/src/ui/InPlaceTextEditor.ts b/src/ui/InPlaceTextEditor.ts index 0c2bca9..4e8be14 100644 --- a/src/ui/InPlaceTextEditor.ts +++ b/src/ui/InPlaceTextEditor.ts @@ -1,11 +1,3 @@ -/** - * @file InPlaceTextEditor.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief In-place text editing overlay for the canvas - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { PrimitiveAdvText } from '../primitives/PrimitiveAdvText.js'; import { MapCoordinates } from '../geom/MapCoordinates.js'; import { LayerDesc } from '../layers/LayerDesc.js'; diff --git a/src/ui/LayerDropdown.ts b/src/ui/LayerDropdown.ts index 4d772bb..cc05df8 100644 --- a/src/ui/LayerDropdown.ts +++ b/src/ui/LayerDropdown.ts @@ -1,11 +1,3 @@ -/** - * @file LayerDropdown.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Reusable layer selector dropdown with color swatches - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { getString } from '../i18n/i18n.js'; import type { LayerDesc } from '../layers/LayerDesc.js'; diff --git a/src/ui/MenuBar.ts b/src/ui/MenuBar.ts index ecbe5e9..66f2b56 100644 --- a/src/ui/MenuBar.ts +++ b/src/ui/MenuBar.ts @@ -1,11 +1,3 @@ -/** - * @file MenuBar.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Application menu bar - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { EditorFacade } from '../circuit/EditorFacade.js'; import { showOptionsDialog } from './OptionsDialog.js'; import { showExportDialog, executeExport } from './ExportDialog.js'; diff --git a/src/ui/OptionsDialog.ts b/src/ui/OptionsDialog.ts index 18af46d..c9a975e 100644 --- a/src/ui/OptionsDialog.ts +++ b/src/ui/OptionsDialog.ts @@ -1,11 +1,3 @@ -/** - * @file OptionsDialog.ts - * @author Dante Loi - * @date 2026-04-23 - * @brief Tabbed options/preferences dialog for FidoCadTS - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { EditorFacade } from '../circuit/EditorFacade.js'; import { SettingsManager, type AppSettings } from '../settings/SettingsManager.js'; import { LibraryFolder } from '../librarymodel/LibraryFolder.js'; diff --git a/src/ui/PromptDialog.ts b/src/ui/PromptDialog.ts index f82fe8f..b5461a5 100644 --- a/src/ui/PromptDialog.ts +++ b/src/ui/PromptDialog.ts @@ -1,11 +1,3 @@ -/** - * @file PromptDialog.ts - * @author Dante Loi - * @date 2026-04-27 - * @brief Reusable modal text-input dialog for rename and other single-value prompts. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { getString } from '../i18n/i18n.js'; function escapeHtml(s: string): string { diff --git a/src/ui/PropertiesPanelController.ts b/src/ui/PropertiesPanelController.ts index 2557e91..0e05e61 100644 --- a/src/ui/PropertiesPanelController.ts +++ b/src/ui/PropertiesPanelController.ts @@ -1,11 +1,3 @@ -/** - * @file PropertiesPanelController.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Properties sidebar form builder extracted from app.ts - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { getString } from '../i18n/i18n.js'; import type { EditorFacade } from '../circuit/EditorFacade.js'; import type { GraphicPrimitive } from '../primitives/GraphicPrimitive.js'; diff --git a/src/ui/Toast.ts b/src/ui/Toast.ts index cd1f5db..7c0861c 100644 --- a/src/ui/Toast.ts +++ b/src/ui/Toast.ts @@ -1,14 +1,3 @@ -/** - * @file Toast.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Non-modal toast notification helper - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Lightweight, auto-dismissing notification bar. Used to surface - * localStorage failures and other non-fatal errors to the user. - */ - export class Toast { private static readonly DURATION_MS = 4000; diff --git a/src/ui/ToolbarController.ts b/src/ui/ToolbarController.ts index a6a1806..259d276 100644 --- a/src/ui/ToolbarController.ts +++ b/src/ui/ToolbarController.ts @@ -1,11 +1,3 @@ -/** - * @file ToolbarController.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Toolbar UI builder extracted from app.ts - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { ElementsEdtActions } from '../circuit/controllers/ElementsEdtActions.js'; import type { EditorFacade } from '../circuit/EditorFacade.js'; import { LayerDropdown } from './LayerDropdown.js'; diff --git a/src/undo/UndoState.ts b/src/undo/UndoState.ts deleted file mode 100644 index 02301b0..0000000 --- a/src/undo/UndoState.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @file UndoState.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Undo state snapshot container - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -export class UndoState { - text: string = ''; - isModified: boolean = false; - fileName: string = ''; - libraryOperation: boolean = false; - libraryDir: string = ''; - - toString(): string { - return `text=${this.text}\nfileName=${this.fileName}` - + `\nOperation on a library: ${this.libraryOperation}` - + `\nlibraryDir=${this.libraryDir}`; - } -} diff --git a/src/vendor/mathjax-shim.ts b/src/vendor/mathjax-shim.ts index 482a1d4..4065820 100644 --- a/src/vendor/mathjax-shim.ts +++ b/src/vendor/mathjax-shim.ts @@ -1,14 +1,3 @@ -/** - * @file mathjax-shim.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Typed wrapper around the vendored MathJax SVG engine. - * Isolates the untyped bundle so the rest of the codebase has full TS - * types. The bundle (src/vendor/mathjax/mathjax.mjs) is generated from - * scripts/mathjax-svg-entry.mjs by scripts/vendor-mathjax.sh. - * @copyright Copyright 2026 Dante Loi - GPL v3 (wraps MathJax, Apache-2.0) - */ - // Vendored JS bundle — no types available, suppressed inline. // @ts-expect-error untyped vendored module import { tex2mathgeom as _tex2mathgeom, tex2svg as _tex2svg } from './mathjax/mathjax.mjs'; diff --git a/test/circuit/align-distribute.test.ts b/test/circuit/align-distribute.test.ts index 960fd4f..3b47bd2 100644 --- a/test/circuit/align-distribute.test.ts +++ b/test/circuit/align-distribute.test.ts @@ -1,12 +1,3 @@ -/** - * @file align-distribute.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for align-center and distribute operations - * (extending the existing EditorActions alignment suite). - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, vi } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { EditorActions } from '../../src/circuit/controllers/EditorActions.js'; diff --git a/test/circuit/controllers/add-elements.test.ts b/test/circuit/controllers/add-elements.test.ts index 7fe34e5..a9f23dd 100644 --- a/test/circuit/controllers/add-elements.test.ts +++ b/test/circuit/controllers/add-elements.test.ts @@ -1,11 +1,3 @@ -/** - * @file add-elements.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for AddElements — primitive creation for each drawing tool - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { DrawingModel } from '../../../src/circuit/model/DrawingModel.js'; import { AddElements } from '../../../src/circuit/controllers/AddElements.js'; diff --git a/test/circuit/controllers/editor-actions.test.ts b/test/circuit/controllers/editor-actions.test.ts index de773db..920fd08 100644 --- a/test/circuit/controllers/editor-actions.test.ts +++ b/test/circuit/controllers/editor-actions.test.ts @@ -1,22 +1,3 @@ -/** - * @file editor-actions.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for EditorActions — selection-driven mutators. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * EditorActions had zero direct coverage before this file. It exposes the - * alignment + transform + delete operations that the keyboard shortcuts - * and menu items call into. Each method: - * - is a no-op when nothing is selected (no undo state, no model change) - * - pushes exactly one undo state when it does mutate - * - leaves unselected primitives untouched - * - * The "rotateAllSelected", "mirrorAllSelected" and "moveAllSelected" paths - * already have indirect coverage via undo-actions.test.ts and the keyboard - * shortcut tests, so this file focuses on alignment + delete. - */ - import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../../src/circuit/controllers/ParserActions.js'; diff --git a/test/circuit/controllers/library-loader.test.ts b/test/circuit/controllers/library-loader.test.ts index bbeb551..5dccbc2 100644 --- a/test/circuit/controllers/library-loader.test.ts +++ b/test/circuit/controllers/library-loader.test.ts @@ -1,12 +1,3 @@ -/** - * @file library-loader.test.ts - * @author Dante Loi - * @date 2026-06-11 - * @brief Unit tests for LibraryLoader — locale-aware URL resolution, prefix - * plumbing, and per-library failure isolation. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach, afterEach, vi, type Mock } from 'vitest'; import { loadStandardLibraries } from '../../../src/circuit/controllers/LibraryLoader.js'; import type { ParserActions } from '../../../src/circuit/controllers/ParserActions.js'; diff --git a/test/circuit/controllers/selection-actions.test.ts b/test/circuit/controllers/selection-actions.test.ts index e8b4ce6..f23d7f3 100644 --- a/test/circuit/controllers/selection-actions.test.ts +++ b/test/circuit/controllers/selection-actions.test.ts @@ -1,11 +1,3 @@ -/** - * @file selection-actions.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for SelectionActions — selection queries - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { DrawingModel } from '../../../src/circuit/model/DrawingModel.js'; import { SelectionActions } from '../../../src/circuit/controllers/SelectionActions.js'; diff --git a/test/circuit/copy-all-as-primitives.test.ts b/test/circuit/copy-all-as-primitives.test.ts index 3174d8d..0ded745 100644 --- a/test/circuit/copy-all-as-primitives.test.ts +++ b/test/circuit/copy-all-as-primitives.test.ts @@ -1,14 +1,3 @@ -/** - * @file copy-all-as-primitives.test.ts - * @author Dante Loi - * @date 2026-06-05 - * @brief Tests for MacroVectorizer.vectorizeAllToString — the payload behind the - * "Copy all as primitives" Edit-menu action. Verifies the whole drawing is - * flattened (no MC macro references remain, including nested macros) while - * the live model is left untouched. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; diff --git a/test/circuit/image-background.test.ts b/test/circuit/image-background.test.ts index 2e762da..ea41d35 100644 --- a/test/circuit/image-background.test.ts +++ b/test/circuit/image-background.test.ts @@ -1,12 +1,3 @@ -/** - * @file image-background.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for ImageAsCanvas — attach/detach, state serialisation, - * position/scale/alpha accessors, and FJC IMG token round-trip. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { ImageAsCanvas } from '../../src/circuit/ImageAsCanvas.js'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; diff --git a/test/circuit/keyboard-shortcuts.test.ts b/test/circuit/keyboard-shortcuts.test.ts index d44a23f..b5de743 100644 --- a/test/circuit/keyboard-shortcuts.test.ts +++ b/test/circuit/keyboard-shortcuts.test.ts @@ -1,11 +1,3 @@ -/** - * @file keyboard-shortcuts.test.ts - * @author Dante Loi - * @date 2026-05-08 - * @brief Tests for all keyboard shortcuts in CircuitPanel - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach, beforeAll, vi } from 'vitest'; import { CircuitPanel } from '../../src/circuit/CircuitPanel.js'; import { ElementsEdtActions } from '../../src/circuit/controllers/ElementsEdtActions.js'; diff --git a/test/circuit/listener-leaks.test.ts b/test/circuit/listener-leaks.test.ts index 44db50e..371c053 100644 --- a/test/circuit/listener-leaks.test.ts +++ b/test/circuit/listener-leaks.test.ts @@ -1,11 +1,3 @@ -/** - * @file listener-leaks.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Tests for Phase 1 listener-leak fixes (AbortController pattern) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, vi, afterEach } from 'vitest'; describe('CircuitPanel listener leak prevention', () => { diff --git a/test/circuit/macro-rotation.test.ts b/test/circuit/macro-rotation.test.ts index 32cb2d8..ba9fe58 100644 --- a/test/circuit/macro-rotation.test.ts +++ b/test/circuit/macro-rotation.test.ts @@ -1,13 +1,3 @@ -/** - * @file macro-rotate-repro.test.ts - * @author Dante Loi - * @date 2026-06-02 - * @brief Regression test: rotating macros must apply a rigid transform that - * preserves the macro's shape and proportions, including for nested - * (macro-in-macro) instances whose sub-macro is mirrored. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; diff --git a/test/circuit/model/drawing-model.test.ts b/test/circuit/model/drawing-model.test.ts index cef0df1..b32e4bc 100644 --- a/test/circuit/model/drawing-model.test.ts +++ b/test/circuit/model/drawing-model.test.ts @@ -1,11 +1,3 @@ -/** - * @file drawing-model.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for DrawingModel — the core data model - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { DrawingModel } from '../../../src/circuit/model/DrawingModel.js'; import { StandardLayers } from '../../../src/layers/StandardLayers.js'; diff --git a/test/circuit/placement.test.ts b/test/circuit/placement.test.ts index 8d62cdc..8929308 100644 --- a/test/circuit/placement.test.ts +++ b/test/circuit/placement.test.ts @@ -1,11 +1,3 @@ -/** - * @file placement.test.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Integration tests: tool selection + canvas clicks place primitives correctly - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { CircuitPanel } from '../../src/circuit/CircuitPanel.js'; import { ElementsEdtActions } from '../../src/circuit/controllers/ElementsEdtActions.js'; diff --git a/test/circuit/ruler.test.ts b/test/circuit/ruler.test.ts index d5806fa..b610d83 100644 --- a/test/circuit/ruler.test.ts +++ b/test/circuit/ruler.test.ts @@ -1,11 +1,3 @@ -/** - * @file ruler.test.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Tests for the on-canvas measuring Ruler (FidoCadJ port) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { Ruler } from '../../src/circuit/Ruler.js'; import { MapCoordinates } from '../../src/geom/MapCoordinates.js'; diff --git a/test/circuit/views/export.test.ts b/test/circuit/views/export.test.ts index 057f4f9..3bc935c 100644 --- a/test/circuit/views/export.test.ts +++ b/test/circuit/views/export.test.ts @@ -1,21 +1,3 @@ -/** - * @file export.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for the Export view — verifies the per-layer + PCB-pad - * + macro pass call ordering against a recording mock ExportInterface. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Strategy: build a DrawingModel by parsing FCD, hand it to an Export view, - * and call exportDrawing() with a mock that records every method - * invocation. Then assert the recorded sequence matches the documented - * passes: - * 1) For each layer 0..N, every primitive on that layer (except macros - * and pcb pads in their dedicated passes). - * 2) A second pass that emits PCB pad holes (drawOnlyPads=true). - * 3) Macros recurse through (1) then (2) themselves. - */ - import { describe, it, expect } from 'vitest'; import { Export, registerExportHooks } from '../../../src/circuit/views/Export.js'; import { DrawingModel } from '../../../src/circuit/model/DrawingModel.js'; diff --git a/test/e2e/app-loads.test.ts b/test/e2e/app-loads.test.ts index f0d788d..c7131b4 100644 --- a/test/e2e/app-loads.test.ts +++ b/test/e2e/app-loads.test.ts @@ -1,11 +1,3 @@ -/** - * @file app-loads.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — App initialisation, rendering, and basic smoke checks - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, getToolbarButtonLabels, getCoordsDisplay, primitiveCount } from './utils'; diff --git a/test/e2e/clipboard.test.ts b/test/e2e/clipboard.test.ts index ad9a6ce..f727a04 100644 --- a/test/e2e/clipboard.test.ts +++ b/test/e2e/clipboard.test.ts @@ -1,11 +1,3 @@ -/** - * @file clipboard.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Copy, cut, duplicate via API - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, grantClipboardPermissions, canvasBox, pressKey, primitiveCount } from './utils'; diff --git a/test/e2e/drawing-tools.test.ts b/test/e2e/drawing-tools.test.ts index a788acb..0985270 100644 --- a/test/e2e/drawing-tools.test.ts +++ b/test/e2e/drawing-tools.test.ts @@ -1,11 +1,3 @@ -/** - * @file drawing-tools.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Drawing tool operations for all primitive types - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, diff --git a/test/e2e/edge-cases.test.ts b/test/e2e/edge-cases.test.ts index a307de5..1397866 100644 --- a/test/e2e/edge-cases.test.ts +++ b/test/e2e/edge-cases.test.ts @@ -1,11 +1,3 @@ -/** - * @file edge-cases.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Edge cases, regression guards, and stress tests - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, diff --git a/test/e2e/export-bitmap-render.test.ts b/test/e2e/export-bitmap-render.test.ts index d2bbc25..53dea91 100644 --- a/test/e2e/export-bitmap-render.test.ts +++ b/test/e2e/export-bitmap-render.test.ts @@ -1,14 +1,3 @@ -/** - * @file export-bitmap-render.test.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief E2E — the PNG/JPG bitmap export (renderToOffscreen) renders filled - * primitives, not just strokes. Regression test for a white-on-white - * bug where the direct background fill desynced the GraphicsCanvas - * colour cache, making black text and math fills invisible. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { PNG } from 'pngjs'; import pixelmatch from 'pixelmatch'; diff --git a/test/e2e/export-png.test.ts b/test/e2e/export-png.test.ts index d6b8198..7b06a6b 100644 --- a/test/e2e/export-png.test.ts +++ b/test/e2e/export-png.test.ts @@ -1,20 +1,3 @@ -/** - * @file export-png.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief E2E — PNG export through the full browser stack. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Verifies that the canvas-to-blob path works in a real browser: - * - the blob is a valid PNG (magic-number check), - * - non-empty circuits produce non-trivially large blobs, - * - the download has the right filename + MIME type. - * - * Uses Playwright's `evaluate` to invoke the canvas.toBlob path directly - * since the dialog flow is exercised by export-dialog.test.ts at the - * unit level. - */ - import { test, expect } from '@playwright/test'; import { gotoApp, loadCircuit, clearCircuit } from './utils'; diff --git a/test/e2e/export-svg-pixel-parity.test.ts b/test/e2e/export-svg-pixel-parity.test.ts index faefcc4..fc8b8c4 100644 --- a/test/e2e/export-svg-pixel-parity.test.ts +++ b/test/e2e/export-svg-pixel-parity.test.ts @@ -1,29 +1,3 @@ -/** - * @file export-svg-pixel-parity.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Rasterised pixel comparison: TS SVG output vs Java PNG reference. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * For each .fcd fixture: - * 1. The TS port exports SVG. - * 2. Playwright (Chromium) renders the SVG at scale 2 (matches Java's - * `-c r2` PNG reference). - * 3. The rendered PNG is pixel-diffed against test/export/fixtures/png-ref/. - * 4. Failures dump the TS render, Java reference, and diff PNG to - * test-results/pixel-parity/ for triage. - * - * Tolerances are intentionally generous (default 5 %) — Chromium's text - * metrics differ from AWT's, so text-heavy fixtures are not expected to - * be sub-pixel identical. The point is to catch *categorical* drift - * (missing element, wrong colour, wrong placement) — not the last - * sub-pixel. - * - * Fixtures known to diverge significantly are listed in - * `KNOWN_DIVERGENT` and skipped. As port-side bugs get fixed, fixtures - * should be removed from that list one-by-one. - */ - import { test, expect } from '@playwright/test'; import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs'; import { join, dirname, basename } from 'node:path'; diff --git a/test/e2e/export.test.ts b/test/e2e/export.test.ts index 7ff8a26..f744583 100644 --- a/test/e2e/export.test.ts +++ b/test/e2e/export.test.ts @@ -1,11 +1,3 @@ -/** - * @file export.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Export to SVG, PGF, and TikZ formats - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, loadCircuit, exportSVG, exportPGF, exportTikZ, clearCircuit } from './utils'; @@ -18,137 +10,137 @@ SA 50 50 0 `; test.describe('Export — SVG', () => { - test.beforeEach(async ({ page }) => { - await gotoApp(page); - await loadCircuit(page, TEST_FCD); - }); - - test('exportSVG produces valid XML wrapper', async ({ page }) => { - const svg = await exportSVG(page); - expect(svg).toContain(''); - }); - - test('exportSVG contains line element', async ({ page }) => { - const svg = await exportSVG(page); - expect(svg).toContain(' { - const svg = await exportSVG(page); - expect(svg).toContain(' { - const svg = await exportSVG(page); - expect(svg).toContain(' { - const svg = await exportSVG(page); - expect(svg).toContain(' { - await clearCircuit(page); - const svg = await exportSVG(page); - expect(svg).toContain(''); - // No drawing elements - expect(svg).not.toContain(' { + await gotoApp(page); + await loadCircuit(page, TEST_FCD); + }); + + test('exportSVG produces valid XML wrapper', async ({ page }) => { + const svg = await exportSVG(page); + expect(svg).toContain(''); + }); + + test('exportSVG contains line element', async ({ page }) => { + const svg = await exportSVG(page); + expect(svg).toContain(' { + const svg = await exportSVG(page); + expect(svg).toContain(' { + const svg = await exportSVG(page); + expect(svg).toContain(' { + const svg = await exportSVG(page); + expect(svg).toContain(' { + await clearCircuit(page); + const svg = await exportSVG(page); + expect(svg).toContain(''); + // No drawing elements + expect(svg).not.toContain(' { - test.beforeEach(async ({ page }) => { - await gotoApp(page); - await loadCircuit(page, TEST_FCD); - }); - - test('exportPGF produces valid PGF wrapper', async ({ page }) => { - const pgf = await exportPGF(page); - expect(pgf).toContain('\\begin{pgfpicture}'); - expect(pgf).toContain('\\end{pgfpicture}'); - }); - - test('exportPGF contains line command', async ({ page }) => { - const pgf = await exportPGF(page); - expect(pgf).toContain('\\pgfline'); - }); - - test('exportPGF contains rect command', async ({ page }) => { - const pgf = await exportPGF(page); - expect(pgf).toContain('\\pgflineto'); - }); - - test('exportPGF contains ellipse command', async ({ page }) => { - const pgf = await exportPGF(page); - expect(pgf).toContain('\\pgfellipse'); - }); - - test('empty circuit exports valid PGF wrapper only', async ({ page }) => { - await clearCircuit(page); - const pgf = await exportPGF(page); - expect(pgf).toContain('\\begin{pgfpicture}'); - expect(pgf).toContain('\\end{pgfpicture}'); - expect(pgf).not.toContain('\\pgfpathlineto'); - }); + test.beforeEach(async ({ page }) => { + await gotoApp(page); + await loadCircuit(page, TEST_FCD); + }); + + test('exportPGF produces valid PGF wrapper', async ({ page }) => { + const pgf = await exportPGF(page); + expect(pgf).toContain('\\begin{pgfpicture}'); + expect(pgf).toContain('\\end{pgfpicture}'); + }); + + test('exportPGF contains line command', async ({ page }) => { + const pgf = await exportPGF(page); + expect(pgf).toContain('\\pgfline'); + }); + + test('exportPGF contains rect command', async ({ page }) => { + const pgf = await exportPGF(page); + expect(pgf).toContain('\\pgflineto'); + }); + + test('exportPGF contains ellipse command', async ({ page }) => { + const pgf = await exportPGF(page); + expect(pgf).toContain('\\pgfellipse'); + }); + + test('empty circuit exports valid PGF wrapper only', async ({ page }) => { + await clearCircuit(page); + const pgf = await exportPGF(page); + expect(pgf).toContain('\\begin{pgfpicture}'); + expect(pgf).toContain('\\end{pgfpicture}'); + expect(pgf).not.toContain('\\pgfpathlineto'); + }); }); test.describe('Export — TikZ', () => { - test.beforeEach(async ({ page }) => { - await gotoApp(page); - await loadCircuit(page, TEST_FCD); - }); - - test('exportTikZ produces valid TikZ wrapper', async ({ page }) => { - const tikz = await exportTikZ(page); - expect(tikz).toContain('\\begin{tikzpicture}'); - expect(tikz).toContain('\\end{tikzpicture}'); - }); - - test('exportTikZ contains draw command for line', async ({ page }) => { - const tikz = await exportTikZ(page); - expect(tikz).toContain('\\draw'); - }); - - test('exportTikZ contains fill command for connection', async ({ page }) => { - const tikz = await exportTikZ(page); - expect(tikz).toContain('\\fill'); - }); - - test('empty circuit exports valid TikZ wrapper only', async ({ page }) => { - await clearCircuit(page); - const tikz = await exportTikZ(page); - expect(tikz).toContain('\\begin{tikzpicture}'); - expect(tikz).toContain('\\end{tikzpicture}'); - }); + test.beforeEach(async ({ page }) => { + await gotoApp(page); + await loadCircuit(page, TEST_FCD); + }); + + test('exportTikZ produces valid TikZ wrapper', async ({ page }) => { + const tikz = await exportTikZ(page); + expect(tikz).toContain('\\begin{tikzpicture}'); + expect(tikz).toContain('\\end{tikzpicture}'); + }); + + test('exportTikZ contains draw command for line', async ({ page }) => { + const tikz = await exportTikZ(page); + expect(tikz).toContain('\\draw'); + }); + + test('exportTikZ contains fill command for connection', async ({ page }) => { + const tikz = await exportTikZ(page); + expect(tikz).toContain('\\fill'); + }); + + test('empty circuit exports valid TikZ wrapper only', async ({ page }) => { + await clearCircuit(page); + const tikz = await exportTikZ(page); + expect(tikz).toContain('\\begin{tikzpicture}'); + expect(tikz).toContain('\\end{tikzpicture}'); + }); }); test.describe('Export — Round-trip consistency', () => { - test.beforeEach(async ({ page }) => { - await gotoApp(page); - await loadCircuit(page, TEST_FCD); - }); - - test('exportSVG is deterministic (same FCD → same SVG)', async ({ page }) => { - const svg1 = await exportSVG(page); - const svg2 = await exportSVG(page); - expect(svg1).toBe(svg2); - }); - - test('exportPGF is deterministic', async ({ page }) => { - const pgf1 = await exportPGF(page); - const pgf2 = await exportPGF(page); - expect(pgf1).toBe(pgf2); - }); - - test('exportTikZ is deterministic', async ({ page }) => { - const tikz1 = await exportTikZ(page); - const tikz2 = await exportTikZ(page); - expect(tikz1).toBe(tikz2); - }); + test.beforeEach(async ({ page }) => { + await gotoApp(page); + await loadCircuit(page, TEST_FCD); + }); + + test('exportSVG is deterministic (same FCD → same SVG)', async ({ page }) => { + const svg1 = await exportSVG(page); + const svg2 = await exportSVG(page); + expect(svg1).toBe(svg2); + }); + + test('exportPGF is deterministic', async ({ page }) => { + const pgf1 = await exportPGF(page); + const pgf2 = await exportPGF(page); + expect(pgf1).toBe(pgf2); + }); + + test('exportTikZ is deterministic', async ({ page }) => { + const tikz1 = await exportTikZ(page); + const tikz2 = await exportTikZ(page); + expect(tikz1).toBe(tikz2); + }); }); diff --git a/test/e2e/file-operations.test.ts b/test/e2e/file-operations.test.ts index a161802..ca3d91a 100644 --- a/test/e2e/file-operations.test.ts +++ b/test/e2e/file-operations.test.ts @@ -1,11 +1,3 @@ -/** - * @file file-operations.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — File new/open/save, circuit load, and code view - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, diff --git a/test/e2e/grid-snap.test.ts b/test/e2e/grid-snap.test.ts index 06b4d1d..06611a0 100644 --- a/test/e2e/grid-snap.test.ts +++ b/test/e2e/grid-snap.test.ts @@ -1,11 +1,3 @@ -/** - * @file grid-snap.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Grid visibility and snap-to-grid toggles - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp } from './utils'; diff --git a/test/e2e/keyboard-e2e.test.ts b/test/e2e/keyboard-e2e.test.ts index 0b77657..454e3ac 100644 --- a/test/e2e/keyboard-e2e.test.ts +++ b/test/e2e/keyboard-e2e.test.ts @@ -1,11 +1,3 @@ -/** - * @file keyboard-e2e.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Keyboard shortcuts exercised through the full browser stack - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, diff --git a/test/e2e/layer-dialog.test.ts b/test/e2e/layer-dialog.test.ts index 1f3774e..0905593 100644 --- a/test/e2e/layer-dialog.test.ts +++ b/test/e2e/layer-dialog.test.ts @@ -1,15 +1,3 @@ -/** - * @file layer-dialog.test.ts - * @author Dante Loi - * @date 2026-06-02 - * @brief E2E — Layer dialog only writes layers the user actually changed - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Regression: the dialog used to mark every layer "modified" on OK, so the - * file header listed all 16 FJC L definitions even when a single layer (or - * none) was changed. Only modified-from-default layers must be serialized. - */ - import { test, expect } from '@playwright/test'; import { gotoApp, loadCircuit, getCircuitText } from './utils'; diff --git a/test/e2e/macro-library.test.ts b/test/e2e/macro-library.test.ts index aad7553..620d3e1 100644 --- a/test/e2e/macro-library.test.ts +++ b/test/e2e/macro-library.test.ts @@ -1,11 +1,3 @@ -/** - * @file macro-library.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Macro library panel, macro placement, and library operations - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, diff --git a/test/e2e/math-render.test.ts b/test/e2e/math-render.test.ts index 015c2cf..e22ddf8 100644 --- a/test/e2e/math-render.test.ts +++ b/test/e2e/math-render.test.ts @@ -1,13 +1,3 @@ -/** - * @file math-render.test.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief E2E — LaTeX math renders through the real browser stack: typeset - * glyphs painted on the canvas (renderTeX) and embedded as paths in SVG - * export. Replaces the removed KaTeX-overlay behaviour. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, loadCircuit, exportSVG, settle } from './utils'; diff --git a/test/e2e/menu-bar.test.ts b/test/e2e/menu-bar.test.ts index 851bf0d..065d074 100644 --- a/test/e2e/menu-bar.test.ts +++ b/test/e2e/menu-bar.test.ts @@ -1,11 +1,3 @@ -/** - * @file menu-bar.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Menu bar dropdown interactions and commands - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, loadCircuit, primitiveCount, pressKey } from './utils'; diff --git a/test/e2e/selection-and-transform.test.ts b/test/e2e/selection-and-transform.test.ts index 22adf90..1afd213 100644 --- a/test/e2e/selection-and-transform.test.ts +++ b/test/e2e/selection-and-transform.test.ts @@ -1,11 +1,3 @@ -/** - * @file selection-and-transform.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Selection, move, rotate, mirror, nudge, and delete - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, diff --git a/test/e2e/undo-redo.test.ts b/test/e2e/undo-redo.test.ts index 0dc7aa0..5cb1777 100644 --- a/test/e2e/undo-redo.test.ts +++ b/test/e2e/undo-redo.test.ts @@ -1,11 +1,3 @@ -/** - * @file undo-redo.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Undo/redo state tracking and keyboard shortcuts - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, pressKey } from './utils'; diff --git a/test/e2e/utils.ts b/test/e2e/utils.ts index 95b52ff..b374d70 100644 --- a/test/e2e/utils.ts +++ b/test/e2e/utils.ts @@ -1,11 +1,3 @@ -/** - * @file utils.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief Shared helpers for FidoCadJS Playwright E2E tests - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import type { Page, BrowserContext } from '@playwright/test'; /** Navigate to the app and wait for it to fully initialise. diff --git a/test/e2e/zoom-pan.test.ts b/test/e2e/zoom-pan.test.ts index 8d89030..b31d2f0 100644 --- a/test/e2e/zoom-pan.test.ts +++ b/test/e2e/zoom-pan.test.ts @@ -1,11 +1,3 @@ -/** - * @file zoom-pan.test.ts - * @author Dante Loi - * @date 2026-05-10 - * @brief E2E — Zoom (in/out, wheel, fit) and pan operations - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { test, expect } from '@playwright/test'; import { gotoApp, pressKey, getZoomPercent, loadCircuit, canvasBox, settle } from './utils'; diff --git a/test/export/export-adversarial.test.ts b/test/export/export-adversarial.test.ts index a04563a..bcf9590 100644 --- a/test/export/export-adversarial.test.ts +++ b/test/export/export-adversarial.test.ts @@ -1,12 +1,3 @@ -/** - * @file export-adversarial.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Adversarial inputs to the SVG exporter — extreme coords, infinite - * values, very long text, XML-injection-shaped strings. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { ExportSVG } from '../../src/export/ExportSVG.js'; import { DimensionG } from '../../src/graphic/DimensionG.js'; diff --git a/test/export/export-bitmap.test.ts b/test/export/export-bitmap.test.ts index d05a8f2..006b900 100644 --- a/test/export/export-bitmap.test.ts +++ b/test/export/export-bitmap.test.ts @@ -1,16 +1,3 @@ -/** - * @file export-bitmap.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for ExportBitmap — offscreen rendering, DPI/pixel sizing, - * B&W post-processing, and split-layers export. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Note: jsdom canvas is largely a no-op renderer, so we test dimensions, - * option plumbing, blob creation, and split-layer counts. Visual fidelity - * (actual pixel content, B&W threshold) is covered by E2E tests. - */ - import { describe, it, expect, vi } from 'vitest'; import { renderToOffscreen, diff --git a/test/export/export-dialog.test.ts b/test/export/export-dialog.test.ts index ac0ba84..8d27c7f 100644 --- a/test/export/export-dialog.test.ts +++ b/test/export/export-dialog.test.ts @@ -1,17 +1,3 @@ -/** - * @file export-dialog.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Unit tests for ExportDialog.executeExport — verify that each - * format selection routes to the correct exporter and the resulting - * download is wired up properly. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * The dialog UI itself is exercised by E2E. Here we drive the dispatcher - * directly with a stub EditorFacade so we can pin the contract without - * involving the browser. - */ - import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { executeExport } from '../../src/ui/ExportDialog.js'; import { defaultBitmapOptions } from '../../src/export/ExportBitmapOptions.js'; diff --git a/test/export/export-math.test.ts b/test/export/export-math.test.ts index 9532c72..e901db7 100644 --- a/test/export/export-math.test.ts +++ b/test/export/export-math.test.ts @@ -1,12 +1,3 @@ -/** - * @file export-math.test.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Verifies that LaTeX math in TY text primitives is exported as typeset - * glyph geometry (MathJax) rather than the literal $...$ source. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; diff --git a/test/export/export-pdf.test.ts b/test/export/export-pdf.test.ts index 587b79e..af98a4f 100644 --- a/test/export/export-pdf.test.ts +++ b/test/export/export-pdf.test.ts @@ -1,11 +1,3 @@ -/** - * @file export-pdf.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Tests for ExportPDF — content-stream and PDF document structure. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { ExportPDF } from '../../src/export/ExportPDF.js'; import { DimensionG } from '../../src/graphic/DimensionG.js'; diff --git a/test/export/export-pgf.fixtures.test.ts b/test/export/export-pgf.fixtures.test.ts index 65c805a..3152c5e 100644 --- a/test/export/export-pgf.fixtures.test.ts +++ b/test/export/export-pgf.fixtures.test.ts @@ -1,11 +1,3 @@ -/** - * @file export-pgf.fixtures.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Golden-snapshot tests for ExportPGF against the fixture corpus. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { listFixtures, diff --git a/test/export/export-pgf.test.ts b/test/export/export-pgf.test.ts index 1076581..10bcf82 100644 --- a/test/export/export-pgf.test.ts +++ b/test/export/export-pgf.test.ts @@ -1,11 +1,3 @@ -/** - * @file export-pgf.test.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Tests for ExportPGF — PGF command generation for LaTeX - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { ExportPGF } from '../../src/export/ExportPGF.js'; import { DimensionG } from '../../src/graphic/DimensionG.js'; diff --git a/test/export/export-svg.fixtures.test.ts b/test/export/export-svg.fixtures.test.ts index 48c82bc..d6f0f92 100644 --- a/test/export/export-svg.fixtures.test.ts +++ b/test/export/export-svg.fixtures.test.ts @@ -1,20 +1,3 @@ -/** - * @file export-svg.fixtures.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Golden-snapshot tests for ExportSVG against the fixture corpus. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Strategy: - * 1. Walk every .fcd under test/export/fixtures/fcd/. - * 2. Run the TS-port export and compare against test/export/fixtures/ts/.svg. - * 3. Missing or stale snapshots fail with a clear message instructing the - * developer to run `UPDATE_SNAPSHOTS=1 npm run test:run` after review. - * - * Java parity is exercised by the separate `export-svg.java-parity.test.ts`, - * which records the known deltas in expected-deltas.json. - */ - import { describe, it, expect } from 'vitest'; import { listFixtures, diff --git a/test/export/export-svg.java-parity.test.ts b/test/export/export-svg.java-parity.test.ts index f83fc9e..af5280d 100644 --- a/test/export/export-svg.java-parity.test.ts +++ b/test/export/export-svg.java-parity.test.ts @@ -1,25 +1,3 @@ -/** - * @file export-svg.java-parity.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Parity checks: TS SVG output matches Java reference modulo the - * documented deltas in expected-deltas.json. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * What this catches: - * - The TS port stops emitting an element type that Java emits. - * - The TS port's coordinate offset drifts from Java's. - * - Layer-color mapping diverges. - * - Mirror/rotation/opacity contracts regress past the Phase 1 fixes. - * - * What this does NOT catch: - * - Cosmetic differences (whitespace, float formatting, attribute order) — - * those are listed in expected-deltas.json and explicitly tolerated. - * - * Skips silently if test/export/fixtures/java/.svg is missing - * (developer hasn't run scripts/regen-export-fixtures.sh yet). - */ - import { describe, it, expect } from 'vitest'; import { listFixtures, loadFixtureFacade, readJavaSnapshot } from './fixtures/helpers.js'; diff --git a/test/export/export-svg.test.ts b/test/export/export-svg.test.ts index d42eb38..a0bae8d 100644 --- a/test/export/export-svg.test.ts +++ b/test/export/export-svg.test.ts @@ -1,11 +1,3 @@ -/** - * @file export-svg.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for ExportSVG — SVG element generation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { ExportSVG } from '../../src/export/ExportSVG.js'; import { DimensionG } from '../../src/graphic/DimensionG.js'; diff --git a/test/export/export-tikz.fixtures.test.ts b/test/export/export-tikz.fixtures.test.ts index 749b5c1..7ad159e 100644 --- a/test/export/export-tikz.fixtures.test.ts +++ b/test/export/export-tikz.fixtures.test.ts @@ -1,15 +1,3 @@ -/** - * @file export-tikz.fixtures.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Golden-snapshot tests for ExportTikZ against the fixture corpus. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * NOTE: Java FidoCadJ does not emit TikZ directly (its CLI's "pgf" format - * outputs the PGF dialect). The Java-parity test is therefore not run for - * TikZ — only TS-snapshot stability. - */ - import { describe, it, expect } from 'vitest'; import { listFixtures, diff --git a/test/export/export-tikz.test.ts b/test/export/export-tikz.test.ts index 3796f72..6e4bf3d 100644 --- a/test/export/export-tikz.test.ts +++ b/test/export/export-tikz.test.ts @@ -1,11 +1,3 @@ -/** - * @file export-tikz.test.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Tests for ExportTikZ — TikZ command generation for LaTeX - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { ExportTikZ } from '../../src/export/ExportTikZ.js'; import { DimensionG } from '../../src/graphic/DimensionG.js'; diff --git a/test/export/fixtures/helpers.ts b/test/export/fixtures/helpers.ts index c44d650..53c861a 100644 --- a/test/export/fixtures/helpers.ts +++ b/test/export/fixtures/helpers.ts @@ -1,18 +1,3 @@ -/** - * @file helpers.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Shared helpers for fixture-based exporter tests. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Each helper centralises one job: - * - listing the .fcd fixtures, - * - parsing a fixture into a DrawingModel, - * - exporting via ExportFacade, - * - reading committed Java/TS snapshots, - * - normalising whitespace and version banners for diff stability. - */ - import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs'; import { join, dirname, basename } from 'node:path'; import { fileURLToPath } from 'node:url'; diff --git a/test/export/latex-escape.test.ts b/test/export/latex-escape.test.ts index b6db43e..ce19c4b 100644 --- a/test/export/latex-escape.test.ts +++ b/test/export/latex-escape.test.ts @@ -1,13 +1,5 @@ -/** - * @file latex-escape.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Tests for Phase 1.7: LaTeX special-character escaping - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; -import { escapeLatex } from '../../src/export/AbstractExport.js'; +import { escapeLatex } from '../../src/export/LatexEscape.js'; describe('escapeLatex', () => { it('escapes backslash', () => { diff --git a/test/geom/drawing-size.test.ts b/test/geom/drawing-size.test.ts index 0c1d46a..af23121 100644 --- a/test/geom/drawing-size.test.ts +++ b/test/geom/drawing-size.test.ts @@ -1,15 +1,3 @@ -/** - * @file drawing-size.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for DrawingSize — bounding-box computation used by - * the export pipeline. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * DrawingSize had zero direct coverage. Its `getImageSize` is what the - * ExportFacade's coordinate-offset fix (Phase 1) relies on. - */ - import { describe, it, expect } from 'vitest'; import { DrawingSize } from '../../src/geom/DrawingSize.js'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; diff --git a/test/geom/geometric-distances.test.ts b/test/geom/geometric-distances.test.ts index 9b73a3e..581c12c 100644 --- a/test/geom/geometric-distances.test.ts +++ b/test/geom/geometric-distances.test.ts @@ -1,11 +1,3 @@ -/** - * @file geometric-distances.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Unit tests for GeometricDistances — point/shape distance helpers. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { GeometricDistances } from '../../src/geom/GeometricDistances.js'; diff --git a/test/geom/map-coordinates.test.ts b/test/geom/map-coordinates.test.ts index deef4cc..caca05c 100644 --- a/test/geom/map-coordinates.test.ts +++ b/test/geom/map-coordinates.test.ts @@ -1,11 +1,3 @@ -/** - * @file map-coordinates.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for MapCoordinates — mapping, snapping, zoom, orientation, push/pop - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { MapCoordinates } from '../../src/geom/MapCoordinates.js'; diff --git a/test/globals/globals.test.ts b/test/globals/globals.test.ts index 3ec49d7..0f63b46 100644 --- a/test/globals/globals.test.ts +++ b/test/globals/globals.test.ts @@ -1,11 +1,3 @@ -/** - * @file globals.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for Globals static utility functions - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { Globals } from '../../src/globals/Globals.js'; diff --git a/test/graphic/color-canvas.test.ts b/test/graphic/color-canvas.test.ts index e05619f..830b5b2 100644 --- a/test/graphic/color-canvas.test.ts +++ b/test/graphic/color-canvas.test.ts @@ -1,12 +1,3 @@ -/** - * @file color-canvas.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Verify ColorCanvas.getRGB returns clean 24-bit values - * suitable for toString(16) → CSS hex colors. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { ColorCanvas } from '../../src/graphic/canvas/ColorCanvas.js'; diff --git a/test/graphic/dash-scaling.test.ts b/test/graphic/dash-scaling.test.ts index 6ea9fa2..2d7e92f 100644 --- a/test/graphic/dash-scaling.test.ts +++ b/test/graphic/dash-scaling.test.ts @@ -1,12 +1,3 @@ -/** - * @file dash-scaling.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Verify that applyStroke scales dash patterns with line width - * so dashed strokes look consistent at every zoom level. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, vi, beforeEach } from 'vitest'; import { GraphicsCanvas } from '../../src/graphic/canvas/GraphicsCanvas.js'; import { Globals } from '../../src/globals/Globals.js'; diff --git a/test/graphic/math-layout.test.ts b/test/graphic/math-layout.test.ts index e89c46c..402a29a 100644 --- a/test/graphic/math-layout.test.ts +++ b/test/graphic/math-layout.test.ts @@ -1,12 +1,3 @@ -/** - * @file math-layout.test.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Tests for MathLayout — $/$$ splitting, MathJax geometry rendering, - * left-to-right segment positioning, and literal-text fallback. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { splitMathSegments, layoutMath } from '../../src/graphic/MathLayout.js'; diff --git a/test/i18n/routing.test.ts b/test/i18n/routing.test.ts index 8c4d896..ecfbadd 100644 --- a/test/i18n/routing.test.ts +++ b/test/i18n/routing.test.ts @@ -1,13 +1,3 @@ -/** - * @file routing.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Tests for the i18n routing layer: loadLocale, setLocale, getString - * fallback to English, locale-change subscribers, and localStorage - * persistence. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { loadLocale, diff --git a/test/layers/layer-desc.test.ts b/test/layers/layer-desc.test.ts index 6cb19ea..85b1998 100644 --- a/test/layers/layer-desc.test.ts +++ b/test/layers/layer-desc.test.ts @@ -1,11 +1,3 @@ -/** - * @file layer-desc.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for LayerDesc — layer description model - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { LayerDesc } from '../../src/layers/LayerDesc.js'; import { StandardLayers } from '../../src/layers/StandardLayers.js'; @@ -67,7 +59,7 @@ describe('LayerDesc', () => { it('StandardLayers layers have descriptions', () => { const layers = StandardLayers.createStandardLayers(); - const hasDesc = layers.some(l => l.getDescription().length > 0); + const hasDesc = layers.some((l) => l.getDescription().length > 0); expect(hasDesc).toBe(true); }); }); diff --git a/test/librarymodel/library-model.test.ts b/test/librarymodel/library-model.test.ts index fd94dcc..f8af26f 100644 --- a/test/librarymodel/library-model.test.ts +++ b/test/librarymodel/library-model.test.ts @@ -1,17 +1,8 @@ -/** - * @file library-model.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Tests for LibraryModel, Library, and Category. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -import { describe, it, expect, vi } from 'vitest'; +import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; import { StandardLayers } from '../../src/layers/StandardLayers.js'; import { LibraryModel } from '../../src/librarymodel/LibraryModel.js'; -import type { LibraryListener } from '../../src/librarymodel/event/LibraryListener.js'; const SAMPLE_FCL = `[FIDOLIB Test Library] {Passive} @@ -42,7 +33,7 @@ describe('LibraryModel', () => { const libs = lm.getAllLibraries(); expect(libs.length).toBeGreaterThanOrEqual(1); - const testLib = libs.find(l => l.getFilename() === 'testlib'); + const testLib = libs.find((l) => l.getFilename() === 'testlib'); expect(testLib).toBeDefined(); }); @@ -57,65 +48,46 @@ describe('LibraryModel', () => { const lm = new LibraryModel(model); const libs = lm.getAllLibraries(); - const testLib = libs.find(l => l.getFilename() === 'testlib')!; - const categories = testLib.getAllCategories().map(c => c.getName()); + const testLib = libs.find((l) => l.getFilename() === 'testlib')!; + const categories = testLib.getAllCategories().map((c) => c.getName()); expect(categories).toContain('Active'); }); it('category contains correct macros', () => { const model = makePopulatedModel(); const lm = new LibraryModel(model); - const testLib = lm.getAllLibraries().find(l => l.getFilename() === 'testlib')!; + const testLib = lm.getAllLibraries().find((l) => l.getFilename() === 'testlib')!; - const allMacros = testLib.getAllCategories().flatMap(c => c.getAllMacros()); - const keys = allMacros.map(m => m.key); - expect(keys.some(k => k.includes('u001'))).toBe(true); - }); - - it('forceUpdate fires libraryLoaded on all listeners', () => { - const model = makePopulatedModel(); - const lm = new LibraryModel(model); - - const listener: LibraryListener = { - libraryLoaded: vi.fn(), - libraryNodeRenamed: vi.fn(), - libraryNodeRemoved: vi.fn(), - libraryNodeAdded: vi.fn(), - libraryNodeKeyChanged: vi.fn(), - }; - lm.addLibraryListener(listener); - lm.forceUpdate(); - - expect(listener.libraryLoaded).toHaveBeenCalledOnce(); - }); - - it('removeLibraryListener stops receiving events', () => { - const model = makePopulatedModel(); - const lm = new LibraryModel(model); - - const listener: LibraryListener = { - libraryLoaded: vi.fn(), - libraryNodeRenamed: vi.fn(), - libraryNodeRemoved: vi.fn(), - libraryNodeAdded: vi.fn(), - libraryNodeKeyChanged: vi.fn(), - }; - lm.addLibraryListener(listener); - lm.removeLibraryListener(listener); - lm.forceUpdate(); - - expect(listener.libraryLoaded).not.toHaveBeenCalled(); + const allMacros = testLib.getAllCategories().flatMap((c) => c.getAllMacros()); + const keys = allMacros.map((m) => m.key); + expect(keys.some((k) => k.includes('u001'))).toBe(true); }); }); describe('LibraryModel static helpers', () => { it('getPlainMacroKey strips library prefix', () => { - const macro = { key: 'testlib.r001', name: '', description: '', category: '', library: '', filename: '', level: 0 }; + const macro = { + key: 'testlib.r001', + name: '', + description: '', + category: '', + library: '', + filename: '', + level: 0, + }; expect(LibraryModel.getPlainMacroKey(macro)).toBe('r001'); }); it('getPlainMacroKey works for unprefixed keys', () => { - const macro = { key: '000', name: '', description: '', category: '', library: '', filename: '', level: 0 }; + const macro = { + key: '000', + name: '', + description: '', + category: '', + library: '', + filename: '', + level: 0, + }; expect(LibraryModel.getPlainMacroKey(macro)).toBe('000'); }); @@ -128,7 +100,7 @@ describe('Library', () => { it('containsMacroKey finds macro in any category', () => { const model = makePopulatedModel(); const lm = new LibraryModel(model); - const testLib = lm.getAllLibraries().find(l => l.getFilename() === 'testlib')!; + const testLib = lm.getAllLibraries().find((l) => l.getFilename() === 'testlib')!; // 'u001' is the plain key for testlib.u001 expect(testLib.containsMacroKey('u001')).toBe(true); expect(testLib.containsMacroKey('doesnotexist')).toBe(false); diff --git a/test/macropicker/macro-picker.test.ts b/test/macropicker/macro-picker.test.ts index 3ad8640..90b5491 100644 --- a/test/macropicker/macro-picker.test.ts +++ b/test/macropicker/macro-picker.test.ts @@ -1,12 +1,3 @@ -/** - * @file macro-picker.test.ts - * @author Dante Loi - * @date 2026-06-11 - * @brief Unit tests for MacroPicker — tree building, expand/collapse, search - * filtering, and macro selection wiring. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach, vi } from 'vitest'; import { loadLocale } from '../../src/i18n/i18n.js'; import { MacroPicker } from '../../src/macropicker/MacroPicker.js'; diff --git a/test/macropicker/preview-connection.test.ts b/test/macropicker/preview-connection.test.ts index bc0eea8..1bd840b 100644 --- a/test/macropicker/preview-connection.test.ts +++ b/test/macropicker/preview-connection.test.ts @@ -1,17 +1,3 @@ -/** - * @file preview-connection.test.ts - * @author Dante Loi - * @date 2026-05-20 - * @brief Regression test: connection dots must stay visible after a background clear - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Guards Bug 1 — the macro preview cleared its background by writing to the - * raw 2D context, desyncing GraphicsCanvas.currentColor. Because connection - * dots live on layer 0 (black) and selectLayer() skips setColor() when it - * believes the colour is already black, the dot was filled white-on-white. - * Clearing through the graphics API keeps the colour state in sync so the - * dot's fill uses the layer colour. - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; diff --git a/test/parser/parser-adversarial.test.ts b/test/parser/parser-adversarial.test.ts index 746565f..2f94325 100644 --- a/test/parser/parser-adversarial.test.ts +++ b/test/parser/parser-adversarial.test.ts @@ -1,23 +1,3 @@ -/** - * @file parser-adversarial.test.ts - * @author Dante Loi - * @date 2026-05-15 - * @brief Adversarial / fuzz tests for ParserActions — malformed FCD must - * never crash the parser. The contract is: either parse it (best - * effort) or throw a defined Error. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * The tests are hand-rolled rather than property-based (no fast-check - * dependency) but cover the same shapes: - * - syntactically malformed input (empty, truncated, mixed line endings) - * - boundary integer values (Number.MAX_SAFE_INTEGER, negative coords) - * - long lines (>10 000 tokens) - * - Unicode (BMP + surrogate pairs + emoji) in text fields - * - NUL bytes - * - embedded markup that could escape an XML/LaTeX context - * - macro recursion at the depth limit (16) - */ - import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; diff --git a/test/parser/parser-global-state.test.ts b/test/parser/parser-global-state.test.ts index 09199c0..ae043ca 100644 --- a/test/parser/parser-global-state.test.ts +++ b/test/parser/parser-global-state.test.ts @@ -1,12 +1,3 @@ -/** - * @file parser-global-state.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Tests for Phase 1.5 fix: macroExpansionDepth moved from module - * scope to instance field on ParserActions. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; diff --git a/test/parser/primitive-round-trip.test.ts b/test/parser/primitive-round-trip.test.ts index 6a7090b..946913c 100644 --- a/test/parser/primitive-round-trip.test.ts +++ b/test/parser/primitive-round-trip.test.ts @@ -1,15 +1,3 @@ -/** - * @file primitive-round-trip.test.ts - * @author Dante Loi - * @date 2026-04-24 - * @brief Round-trip parse/serialize tests for all 11 primitive types and ParserActions. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * Strategy: parse a string S, call getText(false) → T1, parse T1, call getText(false) → T2. - * Assert T1 === T2 (stability). This proves the serialized form is self-consistent without - * comparing against Java-generated byte output (which may use different float formatting). - */ - import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; @@ -190,8 +178,10 @@ describe('PrimitiveComplexCurve (CV/CP)', () => { }); it('is stable from test size file (open curve)', () => { - assertStable('[FIDOCAD]\nFJC C 1.5\nFJC A 0.35\nFJC B 0.25\n' + - 'CP 1 45 45 70 30 45 60 80 50 75 65 95 30 20 30 65 75 0\n'); + assertStable( + '[FIDOCAD]\nFJC C 1.5\nFJC A 0.35\nFJC B 0.25\n' + + 'CP 1 45 45 70 30 45 60 80 50 75 65 95 30 20 30 65 75 0\n', + ); }); it('roundtrips a short CV', () => { @@ -323,7 +313,9 @@ describe('PrimitiveAdvText (TY/TE)', () => { }); it('is stable from test size file', () => { - assertStable('[FIDOCAD]\nFJC C 1.5\nFJC B 0.25\nTY 35 35 4 3 0 0 0 Helvetica This is a test\n'); + assertStable( + '[FIDOCAD]\nFJC C 1.5\nFJC B 0.25\nTY 35 35 4 3 0 0 0 Helvetica This is a test\n', + ); }); }); @@ -619,7 +611,7 @@ LI 10 0 20 0 0 pa.readLibraryString(libContent, 'electronics'); const lib = model.getLibrary(); // Key format: prefix.macroname (lowercased) - const hasLED = [...lib.keys()].some(k => k.includes('rled')); + const hasLED = [...lib.keys()].some((k) => k.includes('rled')); expect(hasLED).toBe(true); }); }); diff --git a/test/primitives/complex-curve-fixes.test.ts b/test/primitives/complex-curve-fixes.test.ts index ef95a8f..9ed397b 100644 --- a/test/primitives/complex-curve-fixes.test.ts +++ b/test/primitives/complex-curve-fixes.test.ts @@ -1,13 +1,3 @@ -/** - * @file complex-curve-fixes.test.ts - * @author Dante Loi - * @date 2026-05-14 - * @brief Tests for Phase 1 fixes in PrimitiveComplexCurve: - * 1.3 — off-by-one insertion at boundaries - * 1.4 — stale-reference in hit-test after mutation - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { PrimitiveComplexCurve } from '../../src/primitives/PrimitiveComplexCurve.js'; diff --git a/test/primitives/primitive-edge-cases.test.ts b/test/primitives/primitive-edge-cases.test.ts index 37b840b..3ec6ef4 100644 --- a/test/primitives/primitive-edge-cases.test.ts +++ b/test/primitives/primitive-edge-cases.test.ts @@ -1,11 +1,3 @@ -/** - * @file primitive-edge-cases.test.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Edge-case and unit tests for individual primitives (toString, parseTokens) - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect } from 'vitest'; import { PrimitiveLine } from '../../src/primitives/PrimitiveLine.js'; import { PrimitiveBezier } from '../../src/primitives/PrimitiveBezier.js'; @@ -56,7 +48,25 @@ describe('PrimitiveLine', () => { describe('PrimitiveBezier', () => { it('toString produces BE token format', () => { - const bz = new PrimitiveBezier(50, 5, 20, 60, 70, 35, 50, 70, 0, false, false, 0, 3, 2, 0, FONT, FSIZE); + const bz = new PrimitiveBezier( + 50, + 5, + 20, + 60, + 70, + 35, + 50, + 70, + 0, + false, + false, + 0, + 3, + 2, + 0, + FONT, + FSIZE, + ); const s = bz.toString(false); expect(s).toContain('BE'); expect(s).toContain('50'); diff --git a/test/settings/settings-manager.test.ts b/test/settings/settings-manager.test.ts index 6a1b1e7..6d466f6 100644 --- a/test/settings/settings-manager.test.ts +++ b/test/settings/settings-manager.test.ts @@ -1,11 +1,3 @@ -/** - * @file settings-manager.test.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Tests for SettingsManager validation, defaults, and persistence - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach, vi } from 'vitest'; import { SettingsManager } from '../../src/settings/SettingsManager.js'; @@ -18,12 +10,24 @@ function ensureLocalStorage(): Storage { // Create a simple in-memory polyfill const store = new Map(); const polyfill = { - getItem(key: string): string | null { return store.get(key) ?? null; }, - setItem(key: string, value: string): void { store.set(key, value); }, - removeItem(key: string): void { store.delete(key); }, - clear(): void { store.clear(); }, - get length(): number { return store.size; }, - key(_index: number): string | null { return null; }, + getItem(key: string): string | null { + return store.get(key) ?? null; + }, + setItem(key: string, value: string): void { + store.set(key, value); + }, + removeItem(key: string): void { + store.delete(key); + }, + clear(): void { + store.clear(); + }, + get length(): number { + return store.size; + }, + key(_index: number): string | null { + return null; + }, }; vi.stubGlobal('localStorage', polyfill); return polyfill as unknown as Storage; @@ -98,12 +102,15 @@ describe('SettingsManager', () => { }); it('ignores invalid field types', () => { - localStorage.setItem('fidocadts.settings.v1', JSON.stringify({ - gridSizeX: 'not-a-number', - snapToGrid: 42, - backgroundColor: 'invalid-color', - gridSizeY: -5, - })); + localStorage.setItem( + 'fidocadts.settings.v1', + JSON.stringify({ + gridSizeX: 'not-a-number', + snapToGrid: 42, + backgroundColor: 'invalid-color', + gridSizeY: -5, + }), + ); (SettingsManager as any).instance = null; const sm = SettingsManager.getInstance(); const s = sm.getSettings(); @@ -114,11 +121,14 @@ describe('SettingsManager', () => { }); it('clamps numeric values to valid ranges', () => { - localStorage.setItem('fidocadts.settings.v1', JSON.stringify({ - gridSizeX: 2000, - gridSizeY: 0, - strokeSize: 0.001, - })); + localStorage.setItem( + 'fidocadts.settings.v1', + JSON.stringify({ + gridSizeX: 2000, + gridSizeY: 0, + strokeSize: 0.001, + }), + ); (SettingsManager as any).instance = null; const sm = SettingsManager.getInstance(); const s = sm.getSettings(); @@ -128,23 +138,26 @@ describe('SettingsManager', () => { }); it('accepts valid settings from storage', () => { - localStorage.setItem('fidocadts.settings.v1', JSON.stringify({ - gridSizeX: 10, - gridSizeY: 20, - snapToGrid: false, - antiAlias: true, - strokeSize: 1.0, - connectionSize: 3.0, - pcbLineWidth: 8, - pcbPadWidth: 6, - pcbPadHeight: 7, - pcbPadDrill: 3, - backgroundColor: '#abcdef', - gridColor: '#112233', - selectionLTRColor: '#445566', - selectionRTLColor: '#778899', - renderTeX: true, - })); + localStorage.setItem( + 'fidocadts.settings.v1', + JSON.stringify({ + gridSizeX: 10, + gridSizeY: 20, + snapToGrid: false, + antiAlias: true, + strokeSize: 1.0, + connectionSize: 3.0, + pcbLineWidth: 8, + pcbPadWidth: 6, + pcbPadHeight: 7, + pcbPadDrill: 3, + backgroundColor: '#abcdef', + gridColor: '#112233', + selectionLTRColor: '#445566', + selectionRTLColor: '#778899', + renderTeX: true, + }), + ); (SettingsManager as any).instance = null; const sm = SettingsManager.getInstance(); const s = sm.getSettings(); diff --git a/test/setup.ts b/test/setup.ts index 52b3c99..c4aac4c 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -1,8 +1,3 @@ -/** - * @file setup.ts - * @brief Global test setup — stubs for jsdom environment - */ - import { beforeAll } from 'vitest'; beforeAll(() => { diff --git a/test/ui/confirm-dialog.test.ts b/test/ui/confirm-dialog.test.ts index fbf4661..1b116aa 100644 --- a/test/ui/confirm-dialog.test.ts +++ b/test/ui/confirm-dialog.test.ts @@ -1,12 +1,3 @@ -/** - * @file confirm-dialog.test.ts - * @author Dante Loi - * @date 2026-06-11 - * @brief Unit tests for ConfirmDialog — resolution paths, DOM lifecycle, and - * HTML escaping of caller-supplied strings. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { loadLocale } from '../../src/i18n/i18n.js'; import { ConfirmDialog } from '../../src/ui/ConfirmDialog.js'; diff --git a/test/ui/menubar-open-libraries.test.ts b/test/ui/menubar-open-libraries.test.ts index 04f184d..83df098 100644 --- a/test/ui/menubar-open-libraries.test.ts +++ b/test/ui/menubar-open-libraries.test.ts @@ -1,17 +1,3 @@ -/** - * @file menubar-open-libraries.test.ts - * @author Dante Loi - * @date 2026-06-03 - * @brief Guards that File → Open defers parsing until the component libraries - * are loaded. - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Macros are expanded once, at parse time, against the in-memory - * library map. If a circuit opened from disk is parsed before the - * libraries finish loading, every macro reference is dropped (or left - * permanently unresolved). The open path must therefore await the - * libraries-ready promise before calling loadCircuit. - */ - import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { loadLocale } from '../../src/i18n/i18n.js'; import { MenuBar } from '../../src/ui/MenuBar.js'; diff --git a/test/ui/prompt-dialog.test.ts b/test/ui/prompt-dialog.test.ts index e927c4a..0226977 100644 --- a/test/ui/prompt-dialog.test.ts +++ b/test/ui/prompt-dialog.test.ts @@ -1,12 +1,3 @@ -/** - * @file prompt-dialog.test.ts - * @author Dante Loi - * @date 2026-06-11 - * @brief Unit tests for PromptDialog — resolution paths, validation gating, - * and HTML escaping of the default value. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { loadLocale } from '../../src/i18n/i18n.js'; import { PromptDialog } from '../../src/ui/PromptDialog.js'; diff --git a/test/ui/properties-batch.test.ts b/test/ui/properties-batch.test.ts index 2ee0fcd..862f751 100644 --- a/test/ui/properties-batch.test.ts +++ b/test/ui/properties-batch.test.ts @@ -1,13 +1,3 @@ -/** - * @file properties-batch.test.ts - * @author Dante Loi - * @date 2026-05-20 - * @brief Tests for batch (multi-selection) layer editing in the properties panel - * @copyright Copyright 2026 Dante Loi - GPL v3 - * @details Guards Bug 3 — changing the layer with several elements selected must - * apply to every selected element, not just the first. - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { loadLocale } from '../../src/i18n/i18n.js'; import { PropertiesPanelController } from '../../src/ui/PropertiesPanelController.js'; diff --git a/test/ui/toolbar-controller.test.ts b/test/ui/toolbar-controller.test.ts index 0472c21..12ad0ca 100644 --- a/test/ui/toolbar-controller.test.ts +++ b/test/ui/toolbar-controller.test.ts @@ -1,13 +1,3 @@ -/** - * @file toolbar-controller.test.ts - * @author Dante Loi - * @date 2026-06-11 - * @brief Unit tests for ToolbarController — tool buttons, zoom dropdown sync - * (including the keyboard-zoom regression), grid/snap/library toggles, - * and the coordinates display. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - import { describe, it, expect, beforeEach, vi } from 'vitest'; import { loadLocale } from '../../src/i18n/i18n.js'; import { ToolbarController } from '../../src/ui/ToolbarController.js'; diff --git a/test/undo/undo-actions.test.ts b/test/undo/undo-actions.test.ts index 350138b..5b313d0 100644 --- a/test/undo/undo-actions.test.ts +++ b/test/undo/undo-actions.test.ts @@ -1,14 +1,3 @@ -/** - * @file undo-actions.test.ts - * @author Dante Loi - * @date 2026-05-09 - * @brief Tests for UndoActions — stack management and basic correctness - * @copyright Copyright 2026 Dante Loi - GPL v3 - * - * UndoActions uses a PRE-SAVE approach: state is captured BEFORE each mutation - * so that a single undo() restores the model to its state before that operation. - */ - import { describe, it, expect, beforeEach } from 'vitest'; import { DrawingModel } from '../../src/circuit/model/DrawingModel.js'; import { ParserActions } from '../../src/circuit/controllers/ParserActions.js'; @@ -79,20 +68,59 @@ describe('UndoActions', () => { it('addPrimitive pushes undo state', () => { expect(undo.canUndo()).toBe(false); - const line = new PrimitiveLine(10, 20, 30, 40, 0, false, false, 0, 3, 2, 0, - model.getTextFont(), model.getTextFontSize()); + const line = new PrimitiveLine( + 10, + 20, + 30, + 40, + 0, + false, + false, + 0, + 3, + 2, + 0, + model.getTextFont(), + model.getTextFontSize(), + ); model.addPrimitive(line, false, undo); expect(undo.canUndo()).toBe(true); }); it('multiple adds create multiple undo entries', () => { - const line1 = new PrimitiveLine(0, 0, 10, 10, 0, false, false, 0, 3, 2, 0, - model.getTextFont(), model.getTextFontSize()); + const line1 = new PrimitiveLine( + 0, + 0, + 10, + 10, + 0, + false, + false, + 0, + 3, + 2, + 0, + model.getTextFont(), + model.getTextFontSize(), + ); model.addPrimitive(line1, false, undo); - const line2 = new PrimitiveLine(20, 20, 30, 30, 0, false, false, 0, 3, 2, 0, - model.getTextFont(), model.getTextFontSize()); + const line2 = new PrimitiveLine( + 20, + 20, + 30, + 30, + 0, + false, + false, + 0, + 3, + 2, + 0, + model.getTextFont(), + model.getTextFontSize(), + ); model.addPrimitive(line2, false, undo); expect(model.getPrimitiveVector().length).toBe(2); diff --git a/test/undo/undo-state.test.ts b/test/undo/undo-state.test.ts deleted file mode 100644 index c45eda8..0000000 --- a/test/undo/undo-state.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file undo-state.test.ts - * @author Dante Loi - * @date 2026-06-11 - * @brief Unit tests for UndoState — defaults and the diagnostic toString. - * @copyright Copyright 2026 Dante Loi - GPL v3 - */ - -import { describe, it, expect } from 'vitest'; -import { UndoState } from '../../src/undo/UndoState.js'; - -describe('UndoState', () => { - it('starts as an empty, unmodified snapshot', () => { - const s = new UndoState(); - expect(s.text).toBe(''); - expect(s.isModified).toBe(false); - expect(s.fileName).toBe(''); - expect(s.libraryOperation).toBe(false); - expect(s.libraryDir).toBe(''); - }); - - it('toString reports text, file name, and library fields', () => { - const s = new UndoState(); - s.text = '[FIDOCAD]\nLI 0 0 10 10 0'; - s.fileName = 'board.fcd'; - s.libraryOperation = true; - s.libraryDir = '/libs'; - const out = s.toString(); - expect(out).toContain('text=[FIDOCAD]\nLI 0 0 10 10 0'); - expect(out).toContain('fileName=board.fcd'); - expect(out).toContain('Operation on a library: true'); - expect(out).toContain('libraryDir=/libs'); - }); -});