Rebrand Pinscope to Periscope across product and codebase.
Rename the core package to periscopex, update UI/docs/Docker/deploy defaults to periscope.michelebigi.it, and keep legacy version/storage key aliases so existing projects keep working. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+4
-4
@@ -1,8 +1,8 @@
|
||||
@AGENTS.md
|
||||
|
||||
# Pinscope Frontend
|
||||
# Periscope Frontend
|
||||
|
||||
Next.js 16 app (App Router, Turbopack) providing a web UI for Pinscope schematic validation. Talks to the FastAPI backend at `localhost:8000`.
|
||||
Next.js 16 app (App Router, Turbopack) providing a web UI for Periscope schematic validation. Talks to the FastAPI backend at `localhost:8000`.
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -38,7 +38,7 @@ credit state only through `useCredits()` — both are inert in this repo.
|
||||
|
||||
| Path | Purpose |
|
||||
|---|---|
|
||||
| `src/lib/types.ts` | TS types mirroring `pinscopex/models.py` |
|
||||
| `src/lib/types.ts` | TS types mirroring `periscopex/models.py` |
|
||||
| `src/lib/api.ts` | All data fetching — single integration point with backend |
|
||||
| `src/lib/mock-data.ts` | Pipeline step definitions for progress UI |
|
||||
| `src/components/report/` | Report viewer components + power tree React Flow graph + derating table + finding comments |
|
||||
@@ -97,6 +97,6 @@ Requires the backend running at `localhost:8000` (or set `NEXT_PUBLIC_API_URL`).
|
||||
|
||||
- Keep all data fetching in `src/lib/api.ts` — don't scatter fetch calls across components
|
||||
- Report filters persist in URL search params (`?status=ERROR&component=U3&q=decoupling`)
|
||||
- When modifying types, keep `src/lib/types.ts` in sync with `backend/pinscopex/models.py`
|
||||
- When modifying types, keep `src/lib/types.ts` in sync with `backend/periscopex/models.py`
|
||||
- Use `font-mono` for technical values: designators (U1), MPNs, pin names, component values
|
||||
- Status colors: emerald = PASS, amber = WARNING, rose = ERROR, blue = accent/active
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
What's new in Pinscope.
|
||||
What's new in Periscope.
|
||||
|
||||
## 2.29.0 — 2026-09-13 — Rebrand to Periscope
|
||||
|
||||
Product name, package (`periscopex`), Docker containers, deploy script, and default site URL are now **Periscope** (`https://periscope.michelebigi.it`). Deterministic finding IDs use the `PE-*` prefix. Existing project.json `pinscope_version` and browser storage keys are still read.
|
||||
|
||||
- [Changed] Brand Pinscope → Periscope across UI, docs, and APIs.
|
||||
- [Changed] `backend/pinscopex` → `backend/periscopex`; rule IDs `PS-*` → `PE-*`.
|
||||
- [Changed] Default host / update script → `periscope.michelebigi.it` / `update-periscope.sh`.
|
||||
|
||||
## 2.28.12 — 2026-09-13 — Antenna templates: IFA / meander / stub + .kicad_mod
|
||||
|
||||
@@ -99,7 +107,7 @@ Dedicated Placement job builds the routing-first topology plan without touching
|
||||
Routing-first floorplan foundation without inventing millimetres: domains and satellite role hints after graph build, plus deterministic crystal CL and NC-pin checks.
|
||||
|
||||
- [New] `functional_groups.json` from `graph_build` (domains, IC groups, `role_hint`, attached `layout_rules`).
|
||||
- [New] `crystal_cl_check` (PS-XTAL-*) and `nc_pin_check` (PS-NC-001) in the deterministic suite.
|
||||
- [New] `crystal_cl_check` (PS-XTAL-*) and `nc_pin_check` (PE-NC-001) in the deterministic suite.
|
||||
- [Docs] Piano §0c Placement F1/F2; packing mm stays Layout F2.
|
||||
|
||||
## 2.27.1 — 2026-09-12 — DeepSeek roadmap integrations
|
||||
@@ -113,7 +121,7 @@ Close the open P0/P2 items from the growth plan: offline smoke on `simple_projec
|
||||
|
||||
## 2.27.0 — 2026-09-11 — Local multi-user auth
|
||||
|
||||
Self-host Pinscope accounts (email + password) so several people can share a project. Invite collaborators by email from the project page — same flow as cloud, without Clerk.
|
||||
Self-host Periscope accounts (email + password) so several people can share a project. Invite collaborators by email from the project page — same flow as cloud, without Clerk.
|
||||
|
||||
- [New] `AUTH_JWT_SECRET` enables register/login; first user is admin and inherits `users/local` projects.
|
||||
- [New] `/sign-in` and `/sign-up`; sidebar account menu. Set `NEXT_PUBLIC_AUTH_MODE=local` on the frontend build.
|
||||
@@ -166,38 +174,38 @@ A pipeline run with `.kicad_pcb` + stackup samples routed **signal** nets (power
|
||||
|
||||
`layout_rules` `keepout` flags a foreign net whose track endpoint is inside the KiCad courtyard. Own net and missing courtyard skip. No invented analog/digital classes.
|
||||
|
||||
- [New] `PS-PLC-004` WARNING. Tests use `X1` / `/HFXIN` / `GND` from `simple_project`.
|
||||
- [New] `PE-PLC-004` WARNING. Tests use `X1` / `/HFXIN` / `GND` from `simple_project`.
|
||||
|
||||
## 2.24.0 — 2026-09-10 — Crystal load caps and track path
|
||||
|
||||
Load caps on XIN/XOUT use the same `max_distance_mm` as decoupling. If the PCB has segments on the net, the limit is shortest-path length, not a guessed “loop is too big” ratio.
|
||||
|
||||
- [New] Crystals (`X1` / C9 / C10 on `simple_project`) run `PS-PLC-001` when `layout_rules` has millimetres.
|
||||
- [New] Crystals (`X1` / C9 / C10 on `simple_project`) run `PE-PLC-001` when `layout_rules` has millimetres.
|
||||
- [New] Detour tracks: path along segments vs the same `max_distance_mm`. No segments → euclidean pad distance.
|
||||
|
||||
## 2.23.0 — 2026-09-10 — same_layer decoupling
|
||||
|
||||
If `layout_rules` sets `same_layer: true`, a decoupling cap on the opposite copper from the IC is a WARNING. A via inside the courtyard (calculated) is enough. Unset flag → skip.
|
||||
|
||||
- [New] `PS-PLC-003` WARNING when every placed cap on the net is on F vs B opposite the IC.
|
||||
- [New] `PE-PLC-003` WARNING when every placed cap on the net is on F vs B opposite the IC.
|
||||
|
||||
## 2.22.0 — 2026-09-10 — Thermal vias vs min_via_count
|
||||
|
||||
Via count is calculated inside the KiCad courtyard. The limit is the `min_via_count` parameter from `layout_rules`. No courtyard or no count → skip. No pad radius default.
|
||||
|
||||
- [New] `PS-PLC-002` when vias in courtyard < `min_via_count`. `simple_project` has no PCB so it stays silent.
|
||||
- [New] `PE-PLC-002` when vias in courtyard < `min_via_count`. `simple_project` has no PCB so it stays silent.
|
||||
|
||||
## 2.21.0 — 2026-09-10 — Layout SI skew (datasheet mm only)
|
||||
|
||||
Intra-pair skew is measured on the PCB only when `layout_rules` `length_match` has a number. 3W, creepage, and CPWG are not guessed.
|
||||
|
||||
- [New] `PS-SI-001` ERROR when a named pair (_DP/_DM, _P/_N) exceeds that millimetre. No mm in the datasheet → skip.
|
||||
- [New] `PE-SI-001` ERROR when a named pair (_DP/_DM, _P/_N) exceeds that millimetre. No mm in the datasheet → skip.
|
||||
|
||||
## 2.20.0 — 2026-09-10 — Placement vs datasheet (PCB)
|
||||
|
||||
Decoupling distance is measured on the `.kicad_pcb` against `layout_rules`. No board → no `PS-PLC-001`. A null millimetre skips — no 3 mm default.
|
||||
Decoupling distance is measured on the `.kicad_pcb` against `layout_rules`. No board → no `PE-PLC-001`. A null millimetre skips — no 3 mm default.
|
||||
|
||||
- [New] `PS-PLC-001` when a decoupling cap is farther than datasheet `max_distance_mm`. Empty `layout_rules` and missing caps skip.
|
||||
- [New] `PE-PLC-001` when a decoupling cap is farther than datasheet `max_distance_mm`. Empty `layout_rules` and missing caps skip.
|
||||
|
||||
## 2.19.0 — 2026-09-10 — Finding review and ECO
|
||||
|
||||
@@ -219,14 +227,14 @@ The Impedance tab uses the closed-form engine from ImpedenceFinder (Hammerstad
|
||||
|
||||
Distributor lifecycle is a cached check, not a review scrape. Errata and layout_rules stay structured and skip when the catalog or the PDF has no number.
|
||||
|
||||
- [New] `PS-LF-001` EOL, `PS-LF-002` NRND, `PS-LF-003` explicit RoHS fail. Replacement only if the distributor lists it. Active / RoHS N/A / missing cache row are silent.
|
||||
- [New] `PS-ERRATA-001` when a catalogued workaround pull-up is missing. No URL → skip.
|
||||
- [New] `PS-INT-001` when `internal_features.pullup_pins` has no rail resistor.
|
||||
- [New] `PE-LF-001` EOL, `PE-LF-002` NRND, `PE-LF-003` explicit RoHS fail. Replacement only if the distributor lists it. Active / RoHS N/A / missing cache row are silent.
|
||||
- [New] `PE-ERRATA-001` when a catalogued workaround pull-up is missing. No URL → skip.
|
||||
- [New] `PE-INT-001` when `internal_features.pullup_pins` has no rail resistor.
|
||||
- [New] `layout_rules` closed kinds; non-numeric `max_distance_mm` is stored as null.
|
||||
|
||||
## 2.16.0 — 2026-09-10 — KiCad cad-bridge
|
||||
|
||||
Pinscope writes `pinscope-findings.json` next to the report so a KiCad 9/10 action plugin can pan to the symbol uuid on the right sheet.
|
||||
Periscope writes `periscope-findings.json` next to the report so a KiCad 9/10 action plugin can pan to the symbol uuid on the right sheet.
|
||||
|
||||
- [New] E2 cad-bridge JSON (`version`, `ref`, `pins`, `sheet`, `uuid`, `severity`). Layout rule ids target pcbnew; others target eeschema.
|
||||
- [New] `.kicad_sch` symbols keep `cad_uuid` + `cad_sheet` in `cad_index` (child sheet, not the empty root).
|
||||
@@ -236,17 +244,17 @@ Pinscope writes `pinscope-findings.json` next to the report so a KiCad 9/10 acti
|
||||
|
||||
Schema checks now compare regulator load to Iout_max, look at PG→EN when a sequence is declared, and treat DNP as a fitted-variant graph.
|
||||
|
||||
- [New] `PS-PWR-001` when specified IQ+I_load exceeds Iout_max, or an explicit series R/ferrite DCR drops >5% of the rail. Missing IQ and PCB traces are not guessed.
|
||||
- [New] `PS-SEQ-001` WARNING if `power_sequence` is in IC specs and upstream PG does not net to downstream EN.
|
||||
- [New] BOM `DNP`/`Fitted`/`Variant` on `bom_fields`. Fitted enable with only a DNP pull is `PS-DNP-001` ERROR; no DNP column skips the check.
|
||||
- [New] `PE-PWR-001` when specified IQ+I_load exceeds Iout_max, or an explicit series R/ferrite DCR drops >5% of the rail. Missing IQ and PCB traces are not guessed.
|
||||
- [New] `PE-SEQ-001` WARNING if `power_sequence` is in IC specs and upstream PG does not net to downstream EN.
|
||||
- [New] BOM `DNP`/`Fitted`/`Variant` on `bom_fields`. Fitted enable with only a DNP pull is `PE-DNP-001` ERROR; no DNP column skips the check.
|
||||
|
||||
## 2.14.0 — 2026-09-10 — Filtri e termico schema
|
||||
|
||||
Deterministic checks now match RC/LC/π/T filters and estimate LDO/resistor dissipation without inventing missing numbers.
|
||||
|
||||
- [New] Filter topology `PS-FLT-001` (fc INFO) / `PS-FLT-002` vs `adc_sample_rate` only when that spec exists. Ferrite DCR `PS-FLT-003` only with a datasheet limit.
|
||||
- [New] LDO `P = I_load×(Vin−Vout)` and `Tj = 25 + P·θJA`. Missing θJA is `PS-TH-001` INFO. `Iout_max` is not treated as load.
|
||||
- [New] Resistor `I²R` vs `power_rating_w` on LED paths and shunts with known ΔV (`PS-TH-003`).
|
||||
- [New] Filter topology `PE-FLT-001` (fc INFO) / `PE-FLT-002` vs `adc_sample_rate` only when that spec exists. Ferrite DCR `PE-FLT-003` only with a datasheet limit.
|
||||
- [New] LDO `P = I_load×(Vin−Vout)` and `Tj = 25 + P·θJA`. Missing θJA is `PE-TH-001` INFO. `Iout_max` is not treated as load.
|
||||
- [New] Resistor `I²R` vs `power_rating_w` on LED paths and shunts with known ΔV (`PE-TH-003`).
|
||||
|
||||
## 2.13.0 — 2026-09-10 — DC-bias C_eff stima
|
||||
|
||||
@@ -254,20 +262,20 @@ The derating table now shows an effective capacitance under DC bias for C0G/X7R/
|
||||
|
||||
- [New] `C_eff` column from an empirical V/Vrated table. Tantalum/electrolytic and unknown dielectrics are left blank.
|
||||
- [Improved] C0G/NP0 stays at nominal C; X7R at 50% of rated V is about 70% of C.
|
||||
- [New] Bulk C without a ~100 nF ceramic is `PS-ESR-001` INFO (no invented Z(f) target).
|
||||
- [New] Bulk C without a ~100 nF ceramic is `PE-ESR-001` INFO (no invented Z(f) target).
|
||||
|
||||
## 2.12.0 — 2026-09-10 — Pull-up sizing and LDO Cout
|
||||
|
||||
Deterministic schema checks now size I2C pull-ups, flag NRST pull-downs, and look at LDO VOUT capacitance — still WARNING, never a invented datasheet µF ERROR.
|
||||
|
||||
- [New] I2C pull-up value vs a wide NXP UM10204 band (`PS-I2C-002`). 4.7 kΩ is in-band; missing values are not sized.
|
||||
- [New] Active-low reset with a resistor to ground is `PS-RST-002`.
|
||||
- [New] Regulator VOUT needs Cout (`PS-DEC-001`); 100 nF-only on VOUT is `PS-DEC-002`. MCU VDD 100 nF is not flagged.
|
||||
- [New] I2C pull-up value vs a wide NXP UM10204 band (`PE-I2C-002`). 4.7 kΩ is in-band; missing values are not sized.
|
||||
- [New] Active-low reset with a resistor to ground is `PE-RST-002`.
|
||||
- [New] Regulator VOUT needs Cout (`PE-DEC-001`); 100 nF-only on VOUT is `PE-DEC-002`. MCU VDD 100 nF is not flagged.
|
||||
- [Improved] Pin-mux UART0 on the `simple_project` MSPM0 nets is covered in tests (SPI PICO/POCI already was).
|
||||
|
||||
## 2.11.0 — 2026-09-10 — DeepSeek V4.1 and re-analyze
|
||||
|
||||
Pinscope now defaults to DeepSeek-V4.1-Flash (`deepseek-flash`) for every LLM stage, shows API cost in dollars, and lets you replace the BOM and netlist on an existing project without deleting it.
|
||||
Periscope now defaults to DeepSeek-V4.1-Flash (`deepseek-flash`) for every LLM stage, shows API cost in dollars, and lets you replace the BOM and netlist on an existing project without deleting it.
|
||||
|
||||
- [New] Default model is `deepseek-flash` (native vision). Legacy `deepseek-v4-flash` / `deepseek-v4-flash-vision-exp` names still work; they route to V4.1.
|
||||
- [New] Replace BOM & netlist on a finished project and re-run the analysis. History, library cache, and prior spend stay on the same project.
|
||||
@@ -291,7 +299,7 @@ Chips and passives stay in a shared library after the first look, so the next bo
|
||||
|
||||
## 2.8.0 — 2026-08-27 — Automatic datasheets
|
||||
|
||||
Pinscope now finds datasheet PDFs on its own. You can still upload a file, but you no longer need DigiKey keys for the common case.
|
||||
Periscope now finds datasheet PDFs on its own. You can still upload a file, but you no longer need DigiKey keys for the common case.
|
||||
|
||||
- [New] Auto-fetch from LCSC (no API key) by manufacturer part number or LCSC code, with exact-MPN matching so a CH340E never silently becomes a CH340G.
|
||||
- [New] Direct Texas Instruments datasheet URLs (`ti.com/lit/ds/symlink/…`) as a second source for TI parts.
|
||||
@@ -300,7 +308,7 @@ Pinscope now finds datasheet PDFs on its own. You can still upload a file, but y
|
||||
|
||||
## 2.7.0 — 2026-08-27 — DeepSeek API
|
||||
|
||||
Pinscope now talks to DeepSeek by default. Extraction skills run locally; datasheet PDFs are converted to text (and page images on the vision model) because DeepSeek does not accept native PDF documents.
|
||||
Periscope now talks to DeepSeek by default. Extraction skills run locally; datasheet PDFs are converted to text (and page images on the vision model) because DeepSeek does not accept native PDF documents.
|
||||
|
||||
- [New] DeepSeek provider (`deepseek-v4-flash`, `deepseek-v4-pro`, `deepseek-v4-flash-vision-exp`) via the OpenAI-compatible Chat Completions API.
|
||||
- [New] Local skill runner: `skills/*/SKILL.md` is inlined and `validate.py` runs in-process — no Anthropic Console upload required.
|
||||
@@ -309,7 +317,7 @@ Pinscope now talks to DeepSeek by default. Extraction skills run locally; datash
|
||||
|
||||
## 2.6.0 — 2026-07-12 — Export Report to Excel
|
||||
|
||||
Download a project's findings as an Excel spreadsheet straight from the report — one click, ready to share, filter, or archive outside Pinscope.
|
||||
Download a project's findings as an Excel spreadsheet straight from the report — one click, ready to share, filter, or archive outside Periscope.
|
||||
|
||||
- [New] "Export Excel" button on the validation report. Every finding becomes a spreadsheet row — designator, part number, ID, severity, title, description, recommendation, and its datasheet source (page included) — sorted most-severe first.
|
||||
|
||||
@@ -321,7 +329,7 @@ Schematic review now works through every functional area of a component before f
|
||||
|
||||
## 2.5.0 — 2026-07-02 — Light Mode
|
||||
|
||||
Pinscope now has a light theme. Toggle between light and dark with the sun/moon button — in the sidebar next to your account menu, or in the header on the website.
|
||||
Periscope now has a light theme. Toggle between light and dark with the sun/moon button — in the sidebar next to your account menu, or in the header on the website.
|
||||
|
||||
- [New] Theme toggle. Switch between light and dark mode anywhere in the app; your choice is remembered on this device. Everything defaults to dark, exactly as before, until you flip it.
|
||||
- [Improved] Every status color — error, warning, and pass badges, finding cards, the progress view, billing — is tuned for both themes, so reports stay legible either way.
|
||||
@@ -331,14 +339,14 @@ Pinscope now has a light theme. Toggle between light and dark with the sun/moon
|
||||
|
||||
Two datasheet-grounded checks now run on every project, independent of the schematic review — catching a swapped-peripheral pin or an over-driven LED — plus a clear list of any components that had no datasheet to review against.
|
||||
|
||||
- [New] Pin-function feasibility check. Pinscope now flags when a net assigns an IC pin a peripheral function its silicon can't route — for example a `UART5_TX` net on a pin whose alternate-function table only offers `UART5_RX`. It's reported as an error straight from the datasheet's pin table and names the likely swap (TX↔RX, SDA↔SCL). It deliberately does not judge signal *direction* across an interface — a direct UART crosses TX↔RX while a transceiver runs straight through — so it only fires on physically impossible pin assignments, never on wiring style.
|
||||
- [New] LED forward-current check. For each LED, Pinscope computes the forward current from the supply rail, the series resistor, and the LED's rated forward voltage, and flags any channel whose current exceeds the LED's rated maximum. Each color of an RGB LED is checked separately, and a leg with no current-limiting resistor at all is called out as a caution.
|
||||
- [New] Pin-function feasibility check. Periscope now flags when a net assigns an IC pin a peripheral function its silicon can't route — for example a `UART5_TX` net on a pin whose alternate-function table only offers `UART5_RX`. It's reported as an error straight from the datasheet's pin table and names the likely swap (TX↔RX, SDA↔SCL). It deliberately does not judge signal *direction* across an interface — a direct UART crosses TX↔RX while a transceiver runs straight through — so it only fires on physically impossible pin assignments, never on wiring style.
|
||||
- [New] LED forward-current check. For each LED, Periscope computes the forward current from the supply rail, the series resistor, and the LED's rated forward voltage, and flags any channel whose current exceeds the LED's rated maximum. Each color of an RGB LED is checked separately, and a leg with no current-limiting resistor at all is called out as a caution.
|
||||
- [New] "Not reviewed" list on the report. Components with no datasheet on file — for instance a do-not-populate footprint that isn't in the BOM — are now called out explicitly, so a mis-wired pin on an unreviewed part shows up as a known gap instead of being silently absent.
|
||||
- [New] Findings from these automatic checks carry an "Automated check" badge, so they're easy to tell apart from datasheet-review findings.
|
||||
|
||||
## 2.3.3 — 2026-06-08 — Faster Reviews
|
||||
|
||||
Multi-chip designs now review several times faster — Pinscope works through ICs in parallel instead of one at a time.
|
||||
Multi-chip designs now review several times faster — Periscope works through ICs in parallel instead of one at a time.
|
||||
|
||||
- [Improved] Datasheet extraction and schematic review now process multiple ICs at once, so reports on multi-IC projects come back substantially faster. The findings are unchanged — only the wait is shorter.
|
||||
|
||||
@@ -364,7 +372,7 @@ EDIF 2.0.0 netlists upload alongside PADS-PCB, with a sub-design picker for file
|
||||
|
||||
## 2.3.0 — 2026-05-24 — LCSC Part Number Support
|
||||
|
||||
JLCPCB-style BOMs with LCSC part numbers (e.g. `C12044`) now work out of the box — Pinscope auto-detects the column, resolves each id to the real manufacturer part number, and shows you what it resolved to before the pipeline runs.
|
||||
JLCPCB-style BOMs with LCSC part numbers (e.g. `C12044`) now work out of the box — Periscope auto-detects the column, resolves each id to the real manufacturer part number, and shows you what it resolved to before the pipeline runs.
|
||||
|
||||
- [New] LCSC part numbers in the manufacturer part number column are auto-detected at BOM upload and converted to real MPNs. Works with JLCPCB / EasyEDA exports without any column renaming.
|
||||
- [New] Project setup now shows the LCSC → MPN mapping on each IC row in the datasheet step (e.g. `C12044 → TP4057-42-SOT26-R`), so you can see what each LCSC id became before the pipeline starts.
|
||||
@@ -378,7 +386,7 @@ Tabbed file upload guide with per-tool instructions, Xpedition coverage, and dir
|
||||
- [New] Documentation for exporting a PADS-PCB netlist from Siemens Xpedition Designer / DxDesigner (VX.2.x, including VX.2.14).
|
||||
- [Improved] File upload guide reorganized into tabs — KiCad, Altium, OrCAD/Allegro, Xpedition, EasyEDA, and Eagle each get their own panel.
|
||||
- [Improved] Netlist uploads now accept `.asc`, `.net`, `.NET`, and `.txt` directly — no more renaming required before upload.
|
||||
- [Improved] File guide now calls out the difference between the PADS-PCB schematic netlist Pinscope needs and the `!PADS-POWERPCB` PCB-layout dump that some EDA tools also save as `.asc`.
|
||||
- [Improved] File guide now calls out the difference between the PADS-PCB schematic netlist Periscope needs and the `!PADS-POWERPCB` PCB-layout dump that some EDA tools also save as `.asc`.
|
||||
|
||||
## 2.2.0 — 2026-05-20 — Cross-chip Datasheet Review
|
||||
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
# File Upload Guide
|
||||
|
||||
Pinscope needs two files from your EDA tool to review a schematic, and an optional KiCad board for layout:
|
||||
Periscope needs two files from your EDA tool to review a schematic, and an optional KiCad board for layout:
|
||||
|
||||
- A **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the circuit's connectivity. Pinscope accepts `.asc`, `.net`, `.NET`, `.txt` (PADS-PCB) and `.edn`, `.edif`, `.edf` (EDIF); the format is auto-detected from the file's first bytes.
|
||||
- A **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the circuit's connectivity. Periscope accepts `.asc`, `.net`, `.NET`, `.txt` (PADS-PCB) and `.edn`, `.edif`, `.edf` (EDIF); the format is auto-detected from the file's first bytes.
|
||||
- A **Bill of Materials** (CSV or XLSX) — mapping each reference designator to a manufacturer part number.
|
||||
- Optional: a **KiCad PCB** (`.kicad_pcb`) — placement, keepout, pair length, and net Z0. Schematic review still runs without it.
|
||||
|
||||
## Example files
|
||||
|
||||
New to Pinscope? Here's a complete set of files from [Phil's Lab](https://www.youtube.com/@PhilsLab)' KiCad 9 TI MSPM0 tutorial you can download and upload as a starter project:
|
||||
New to Periscope? Here's a complete set of files from [Phil's Lab](https://www.youtube.com/@PhilsLab)' KiCad 9 TI MSPM0 tutorial you can download and upload as a starter project:
|
||||
|
||||
- [TI-MSP-KICAD9-TUTORIAL.asc](/examples/TI-MSP-KICAD9-TUTORIAL.asc) — netlist
|
||||
- [TI-MSP-KICAD9-TUTORIAL.csv](/examples/TI-MSP-KICAD9-TUTORIAL.csv) — BOM
|
||||
- [TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf](/examples/TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf) — schematic (for your reference; Pinscope doesn't need this)
|
||||
- [TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf](/examples/TI-MSP-KICAD9-TUTORIAL-SCHEMATIC.pdf) — schematic (for your reference; Periscope doesn't need this)
|
||||
|
||||
The BOM below shows the shape Pinscope is looking for.
|
||||
The BOM below shows the shape Periscope is looking for.
|
||||
|
||||
## The BOM
|
||||
|
||||
Pinscope auto-detects BOM columns by header name. After upload you'll confirm which column holds designators and which holds part numbers, so the exact header names don't matter — only that these columns exist.
|
||||
Periscope auto-detects BOM columns by header name. After upload you'll confirm which column holds designators and which holds part numbers, so the exact header names don't matter — only that these columns exist.
|
||||
|
||||
**Required**
|
||||
|
||||
- **Designator / Reference** — one row per part, or grouped references like `C1,C2,C5` in a single row (Pinscope expands these automatically).
|
||||
- **Manufacturer Part Number (MPN)** — the full orderable part number. Pinscope uses this to look up datasheets, so `10uF 0805` on its own is **not** enough — it needs e.g. `GRM21BR61C106KE15L`.
|
||||
- **Designator / Reference** — one row per part, or grouped references like `C1,C2,C5` in a single row (Periscope expands these automatically).
|
||||
- **Manufacturer Part Number (MPN)** — the full orderable part number. Periscope uses this to look up datasheets, so `10uF 0805` on its own is **not** enough — it needs e.g. `GRM21BR61C106KE15L`.
|
||||
|
||||
**Recommended**
|
||||
|
||||
@@ -34,7 +34,7 @@ CSV and XLSX both work. For XLSX, the first worksheet is used.
|
||||
|
||||
## The Netlist
|
||||
|
||||
Pinscope accepts either a **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the upload form auto-detects which one you sent based on the file's first bytes, so you don't have to pick a format.
|
||||
Periscope accepts either a **PADS-PCB ASCII netlist** or an **EDIF 2.0.0 netlist** — the upload form auto-detects which one you sent based on the file's first bytes, so you don't have to pick a format.
|
||||
|
||||
### PADS-PCB ASCII
|
||||
|
||||
@@ -55,18 +55,18 @@ U1.24 C1.1
|
||||
*END*
|
||||
```
|
||||
|
||||
If your file starts with `*PADS-PCB*` and ends with `*END*`, you're good. Reference designators may contain spaces (e.g. `CV GND`) — Pinscope resolves them against your BOM.
|
||||
If your file starts with `*PADS-PCB*` and ends with `*END*`, you're good. Reference designators may contain spaces (e.g. `CV GND`) — Periscope resolves them against your BOM.
|
||||
|
||||
**Heads up — two different `.asc` files exist.** PADS (and tools that interop with PADS, like Xpedition) use the `.asc` extension for two unrelated things:
|
||||
|
||||
- The **schematic-exported netlist** starts with `*PADS-PCB*` and lists `*PART*` / `*NET*` sections. **This is what Pinscope wants.**
|
||||
- The **full PCB layout dump** starts with `!PADS-POWERPCB-V…` and contains routing/footprint geometry. Pinscope cannot parse this.
|
||||
- The **schematic-exported netlist** starts with `*PADS-PCB*` and lists `*PART*` / `*NET*` sections. **This is what Periscope wants.**
|
||||
- The **full PCB layout dump** starts with `!PADS-POWERPCB-V…` and contains routing/footprint geometry. Periscope cannot parse this.
|
||||
|
||||
If your upload errors with "No components found", check the first line of the file.
|
||||
|
||||
### EDIF 2.0.0
|
||||
|
||||
EDIF is a vendor-neutral s-expression format. The first form is `(edif …`, with an `(edifVersion 2 0 0)` declaration near the top, libraries that define each cell's pin list, and a design library that lists `(instance …)` and `(net …)` forms. Pinscope has been verified against **Siemens xDX Designer / DxDesigner** exports; other EDIF 2.0.0 exporters (OrCAD, Altium, KiCad, Eagle) follow the same grammar and should work, but haven't been broadly tested. If your EDIF file doesn't parse, [contact us](/contact) and send a snippet — most fixes are small.
|
||||
EDIF is a vendor-neutral s-expression format. The first form is `(edif …`, with an `(edifVersion 2 0 0)` declaration near the top, libraries that define each cell's pin list, and a design library that lists `(instance …)` and `(net …)` forms. Periscope has been verified against **Siemens xDX Designer / DxDesigner** exports; other EDIF 2.0.0 exporters (OrCAD, Altium, KiCad, Eagle) follow the same grammar and should work, but haven't been broadly tested. If your EDIF file doesn't parse, [contact us](/contact) and send a snippet — most fixes are small.
|
||||
|
||||
If your tool exports both PADS-PCB and EDIF, PADS-PCB is the path more users have validated; use EDIF when it's the only option.
|
||||
|
||||
@@ -127,17 +127,17 @@ Works in **Xpedition Designer / DxDesigner** (VX.2.x, including VX.2.14). Xpedit
|
||||
3. **Tools → PCB Interface** → choose the **PADS** template (`pads2007.cfg` or equivalent).
|
||||
4. Run the export. The output's first line should be `*PADS-PCB*` with `*PART*` and `*NET*` sections below. The file extension (`.txt`, `.net`, or `.asc`) doesn't matter — upload it as-is.
|
||||
|
||||
If your project is locked to the integrated Xpedition flow, **export EDIF instead** — DxDesigner's EDIF Exporter runs against any project type and Pinscope accepts the resulting `.edn` as an equivalent input. See **Netlist (EDIF alternative)** below.
|
||||
If your project is locked to the integrated Xpedition flow, **export EDIF instead** — DxDesigner's EDIF Exporter runs against any project type and Periscope accepts the resulting `.edn` as an equivalent input. See **Netlist (EDIF alternative)** below.
|
||||
|
||||
**Netlist (EDIF alternative)**
|
||||
|
||||
1. Open the schematic in Xpedition Designer / DxDesigner.
|
||||
2. **File → Export → EDIF…** (older builds: **Tools → Run Tool → Edif Exporter**).
|
||||
3. In the export dialog:
|
||||
- **EDIF version**: **2.0.0** (Pinscope only supports 2.0.0)
|
||||
- **EDIF version**: **2.0.0** (Periscope only supports 2.0.0)
|
||||
- **EDIF level**: **0** (the default)
|
||||
- **Output format**: **Netlist view** — make sure cells, instances, nets, and the `viewMap` back-annotation block are all included
|
||||
- **Designator source**: include back-annotated designators (otherwise instances export as templates like `U?` / `R?` and Pinscope drops them)
|
||||
- **Designator source**: include back-annotated designators (otherwise instances export as templates like `U?` / `R?` and Periscope drops them)
|
||||
4. Save as `<design>.edn` and upload it as the netlist. The file should start with `(edif …)` and contain `(edifVersion 2 0 0)` near the top.
|
||||
|
||||
If neither PADS nor EDIF works for your project setup, send us the BOM and schematic PDF via [Contact](/contact) — we can usually help unblock the export.
|
||||
@@ -178,10 +178,10 @@ For both EasyEDA Standard and EasyEDA Pro.
|
||||
|
||||
- **"No components found"** — your netlist is missing the `*PART*` section. Re-export specifically in PADS-PCB format (not Spice, Protel, or a generic text netlist). If the first line is `!PADS-POWERPCB-V…`, you uploaded the PCB layout dump instead of the schematic netlist — re-export from the schematic side.
|
||||
- **"No ground net found"** — your netlist has no net named `GND`, `VSS`, `AGND`, `DGND`, or similar. If you exported a sub-sheet, re-export the top sheet instead.
|
||||
- **Unresolved parts after the pipeline runs** — a BOM row has no MPN, or the MPN wasn't found on DigiKey. Add the MPN, or rely on Pinscope's value fallback (fills in from the `Value` / `Comment` column).
|
||||
- **Unresolved parts after the pipeline runs** — a BOM row has no MPN, or the MPN wasn't found on DigiKey. Add the MPN, or rely on Periscope's value fallback (fills in from the `Value` / `Comment` column).
|
||||
|
||||
## The KiCad PCB (optional)
|
||||
|
||||
Drop the KiCad **project folder** (or a zip of it) on Schematic — Pinscope takes the sheets and the `.kicad_pcb` if it is there. You can also add the board later.
|
||||
Drop the KiCad **project folder** (or a zip of it) on Schematic — Periscope takes the sheets and the `.kicad_pcb` if it is there. You can also add the board later.
|
||||
|
||||
Still stuck? [Contact us](/contact) with your netlist and BOM attached and we'll take a look.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Pinscope Privacy Policy
|
||||
# Periscope Privacy Policy
|
||||
|
||||
**Last updated: April 5, 2026**
|
||||
|
||||
This Privacy Policy explains how Faradworks, Inc. ("Faradworks," "we," "us," and "our") collects, uses, and discloses information in connection with the Pinscope website (pinscope.ai), platform, and related services (the "Service").
|
||||
This Privacy Policy explains how Faradworks, Inc. ("Faradworks," "we," "us," and "our") collects, uses, and discloses information in connection with the Periscope website (periscope.michelebigi.it), platform, and related services (the "Service").
|
||||
|
||||
This Privacy Policy is intended for free users and self-serve paid users. Enterprise customers typically use the Service under a separate agreement and (if applicable) a data processing agreement ("DPA"), which may include additional privacy and security terms.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Pinscope Terms of Service
|
||||
# Periscope Terms of Service
|
||||
|
||||
**Last updated: April 5, 2026**
|
||||
|
||||
These Terms of Service ("Terms") govern access to and use of the Pinscope platform (pinscope.ai) and related services (the "Service"). These Terms apply to free users and self-serve paid users. If you have a separate written agreement signed by Faradworks, Inc. (for example, an enterprise agreement), that agreement governs your use of the Service to the extent it conflicts with these Terms.
|
||||
These Terms of Service ("Terms") govern access to and use of the Periscope platform (periscope.michelebigi.it) and related services (the "Service"). These Terms apply to free users and self-serve paid users. If you have a separate written agreement signed by Faradworks, Inc. (for example, an enterprise agreement), that agreement governs your use of the Service to the extent it conflicts with these Terms.
|
||||
|
||||
These Terms incorporate Faradworks' [Privacy Policy](/privacy) and any policies referenced in the Service.
|
||||
|
||||
@@ -16,7 +16,7 @@ By creating an account, clicking to accept these Terms (for example, by clicking
|
||||
|
||||
**"Derived Data"** means technical artifacts generated solely to operate the Service, such as parsed text, indexes, embeddings, summaries, or extracted metadata.
|
||||
|
||||
**"Service"** means the Pinscope platform (pinscope.ai), including all features, APIs, and related services.
|
||||
**"Service"** means the Periscope platform (periscope.michelebigi.it), including all features, APIs, and related services.
|
||||
|
||||
**"Content"** refers collectively to Customer Content, Outputs, and Derived Data.
|
||||
|
||||
@@ -170,15 +170,15 @@ Plans may include limits on reviews, tokens, files, API spend, usage allocations
|
||||
|
||||
### 11.2 Prepaid service credits.
|
||||
|
||||
Certain Services, plans, or features may allow or require you to prepay for future eligible Pinscope review services for professional, business, or organizational use by purchasing prepaid service credits ("Usage Credits"). Usage Credits represent a prepaid, limited, revocable, non-transferable license to access eligible Pinscope review services up to the applicable credited amount and may be used only for eligible Pinscope review charges as described in the Service. Faradworks may also, in its sole discretion, provide free or promotional credits ("Promotional Credits"), which may be subject to additional restrictions or expiration dates stated when issued.
|
||||
Certain Services, plans, or features may allow or require you to prepay for future eligible Periscope review services for professional, business, or organizational use by purchasing prepaid service credits ("Usage Credits"). Usage Credits represent a prepaid, limited, revocable, non-transferable license to access eligible Periscope review services up to the applicable credited amount and may be used only for eligible Periscope review charges as described in the Service. Faradworks may also, in its sole discretion, provide free or promotional credits ("Promotional Credits"), which may be subject to additional restrictions or expiration dates stated when issued.
|
||||
|
||||
### 11.3 Credit characteristics and workspace scope.
|
||||
|
||||
Credits may be used only for eligible Pinscope review charges and may not be used for any other product or service unless Faradworks expressly states otherwise in the Service. Credits are not legal tender, are not currency, are not redeemable for cash, are not refundable except as required by law or expressly stated by Faradworks, do not constitute or confer any personal property right, and do not constitute a bank account, deposit account, stored-value account, digital wallet, payment instrument, or other monetary account. Credits are an internal service accounting mechanism that measures the amount of eligible Pinscope review services you have prepaid and are licensed to use. Any credit balance or similar amount displayed in the Service reflects only our record of remaining prepaid eligibility for future eligible review charges and does not represent money held on your behalf. Credits are non-transferable, may not be sold, assigned, gifted, or sublicensed, and may be used only by the workspace or account to which they are issued. If credits are issued to an organization or workspace, they belong to that workspace and may be consumed by authorized users acting within that workspace.
|
||||
Credits may be used only for eligible Periscope review charges and may not be used for any other product or service unless Faradworks expressly states otherwise in the Service. Credits are not legal tender, are not currency, are not redeemable for cash, are not refundable except as required by law or expressly stated by Faradworks, do not constitute or confer any personal property right, and do not constitute a bank account, deposit account, stored-value account, digital wallet, payment instrument, or other monetary account. Credits are an internal service accounting mechanism that measures the amount of eligible Periscope review services you have prepaid and are licensed to use. Any credit balance or similar amount displayed in the Service reflects only our record of remaining prepaid eligibility for future eligible review charges and does not represent money held on your behalf. Credits are non-transferable, may not be sold, assigned, gifted, or sublicensed, and may be used only by the workspace or account to which they are issued. If credits are issued to an organization or workspace, they belong to that workspace and may be consumed by authorized users acting within that workspace.
|
||||
|
||||
### 11.4 Credit purchases and application to charges.
|
||||
|
||||
Your order for Usage Credits constitutes an offer to purchase those Usage Credits. Faradworks may accept or reject any purchase request in its discretion. Credits are issued when Faradworks confirms the purchase or otherwise makes the credits available in your account or workspace. Credits are applied to eligible Pinscope review charges in the manner described in the Service. Faradworks may reserve, deduct, reverse, release, or adjust credits to reflect quoted charges, completed usage, failed runs, duplicate requests, fraud checks, refunds, chargebacks, or billing corrections. Credit pricing, minimum purchase amounts, maximum purchase amounts, and applicable taxes will be shown in the Service or at checkout. Fees are exclusive of taxes unless stated otherwise.
|
||||
Your order for Usage Credits constitutes an offer to purchase those Usage Credits. Faradworks may accept or reject any purchase request in its discretion. Credits are issued when Faradworks confirms the purchase or otherwise makes the credits available in your account or workspace. Credits are applied to eligible Periscope review charges in the manner described in the Service. Faradworks may reserve, deduct, reverse, release, or adjust credits to reflect quoted charges, completed usage, failed runs, duplicate requests, fraud checks, refunds, chargebacks, or billing corrections. Credit pricing, minimum purchase amounts, maximum purchase amounts, and applicable taxes will be shown in the Service or at checkout. Fees are exclusive of taxes unless stated otherwise.
|
||||
|
||||
### 11.5 Credit expiration, forfeiture, and promotional credits.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Pinscope",
|
||||
"short_name": "Pinscope",
|
||||
"name": "Periscope",
|
||||
"short_name": "Periscope",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon_io/android-chrome-192x192.png",
|
||||
|
||||
@@ -234,7 +234,7 @@ function ProjectsPanel() {
|
||||
// Stash the source project id and hand off to the dashboard, which
|
||||
// mounts the create-project dialog. The dialog fetches the full
|
||||
// Project on its end so we don't have to pass the entire object here.
|
||||
window.sessionStorage.setItem("pinscopex:cloneAsNewProjectId", projectId);
|
||||
window.sessionStorage.setItem("periscopex:cloneAsNewProjectId", projectId);
|
||||
router.push("/dashboard");
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useCredits } from "@/components/billing/credits-context";
|
||||
import { OnboardingSurvey } from "@/components/dashboard/onboarding-survey";
|
||||
|
||||
type ViewMode = "cards" | "table";
|
||||
const VIEW_STORAGE_KEY = "pinscopex:dashboard:view";
|
||||
const VIEW_STORAGE_KEY = "periscopex:dashboard:view";
|
||||
|
||||
export default function DashboardPage() {
|
||||
return (
|
||||
@@ -79,10 +79,10 @@ function DashboardContent() {
|
||||
// Admin handoff: "Rerun as new project" stashes a project ID here.
|
||||
const cloneId =
|
||||
typeof window !== "undefined"
|
||||
? window.sessionStorage.getItem("pinscopex:cloneAsNewProjectId")
|
||||
? window.sessionStorage.getItem("periscopex:cloneAsNewProjectId")
|
||||
: null;
|
||||
if (cloneId) {
|
||||
window.sessionStorage.removeItem("pinscopex:cloneAsNewProjectId");
|
||||
window.sessionStorage.removeItem("periscopex:cloneAsNewProjectId");
|
||||
fetchProject(cloneId)
|
||||
.then(setCloneAsNewProject)
|
||||
.catch(() => {
|
||||
|
||||
@@ -104,7 +104,7 @@ export default function FeedbackPage() {
|
||||
)}
|
||||
{expanded === t.ticket_id && t.admin_notes && (
|
||||
<div className="mt-3 rounded border border-emerald-500/20 bg-emerald-500/5 p-3">
|
||||
<p className="text-xs text-emerald-600 dark:text-emerald-400 mb-1">Pinscope Team:</p>
|
||||
<p className="text-xs text-emerald-600 dark:text-emerald-400 mb-1">Periscope Team:</p>
|
||||
<p className="text-sm text-muted-foreground">{t.admin_notes}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -46,6 +46,13 @@ import { ImpedancePanel } from "@/components/project/impedance-panel";
|
||||
import { TopologyPanel } from "@/components/project/topology-panel";
|
||||
import { PcbUploadButton } from "@/components/project/pcb-upload";
|
||||
import { PdfViewerSheet } from "@/components/pdf/pdf-viewer-sheet";
|
||||
import {
|
||||
deratingOverridesKey,
|
||||
deratingSettingsKey,
|
||||
legacyDeratingOverridesKey,
|
||||
legacyDeratingSettingsKey,
|
||||
migrateLocalKey,
|
||||
} from "@/lib/storage-keys";
|
||||
|
||||
export default function ProjectDetailPage({
|
||||
params,
|
||||
@@ -61,7 +68,10 @@ export default function ProjectDetailPage({
|
||||
const [deratingSettings, setDeratingSettings] = useState<DeratingSettings>(() => {
|
||||
if (typeof window === "undefined") return { ceramic: 50, tantalum: 50, electrolytic: 50 };
|
||||
try {
|
||||
const stored = localStorage.getItem(`pinscopex:derating-settings:${id}`);
|
||||
const stored = migrateLocalKey(
|
||||
deratingSettingsKey(id),
|
||||
legacyDeratingSettingsKey(id),
|
||||
);
|
||||
return stored ? JSON.parse(stored) : { ceramic: 50, tantalum: 50, electrolytic: 50 };
|
||||
} catch {
|
||||
return { ceramic: 50, tantalum: 50, electrolytic: 50 };
|
||||
@@ -70,7 +80,10 @@ export default function ProjectDetailPage({
|
||||
const [manualVoltages, setManualVoltages] = useState<Record<string, number>>(() => {
|
||||
if (typeof window === "undefined") return {};
|
||||
try {
|
||||
const stored = localStorage.getItem(`pinscopex:derating-overrides:${id}`);
|
||||
const stored = migrateLocalKey(
|
||||
deratingOverridesKey(id),
|
||||
legacyDeratingOverridesKey(id),
|
||||
);
|
||||
return stored ? JSON.parse(stored) : {};
|
||||
} catch {
|
||||
return {};
|
||||
@@ -83,12 +96,12 @@ export default function ProjectDetailPage({
|
||||
|
||||
// Persist derating settings to localStorage
|
||||
useEffect(() => {
|
||||
localStorage.setItem(`pinscopex:derating-settings:${id}`, JSON.stringify(deratingSettings));
|
||||
localStorage.setItem(deratingSettingsKey(id), JSON.stringify(deratingSettings));
|
||||
}, [deratingSettings, id]);
|
||||
|
||||
// Persist manual voltage overrides to localStorage
|
||||
useEffect(() => {
|
||||
localStorage.setItem(`pinscopex:derating-overrides:${id}`, JSON.stringify(manualVoltages));
|
||||
localStorage.setItem(deratingOverridesKey(id), JSON.stringify(manualVoltages));
|
||||
}, [manualVoltages, id]);
|
||||
|
||||
const reload = useCallback(() => {
|
||||
@@ -458,7 +471,7 @@ export default function ProjectDetailPage({
|
||||
</Card>
|
||||
<CollaboratorsSection projectId={id} />
|
||||
<SkippedComponentsSection skipped={project.skippedComponents} />
|
||||
<ReportVersionSection pinscopeVersion={project.pinscopeVersion} />
|
||||
<ReportVersionSection periscopeVersion={project.periscopeVersion} />
|
||||
</div>
|
||||
)}
|
||||
<PdfViewerSheet
|
||||
@@ -1268,11 +1281,11 @@ function SkippedComponentsSection({
|
||||
}
|
||||
|
||||
function ReportVersionSection({
|
||||
pinscopeVersion,
|
||||
periscopeVersion,
|
||||
}: {
|
||||
pinscopeVersion?: string | null;
|
||||
periscopeVersion?: string | null;
|
||||
}) {
|
||||
if (!pinscopeVersion) return null;
|
||||
if (!periscopeVersion) return null;
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -1281,9 +1294,9 @@ function ReportVersionSection({
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Generated with Pinscope
|
||||
Generated with Periscope
|
||||
</p>
|
||||
<span className="font-mono text-sm">v{pinscopeVersion}</span>
|
||||
<span className="font-mono text-sm">v{periscopeVersion}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -1299,7 +1312,7 @@ function PipelineErrorBanner({
|
||||
}) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const detail = message ?? "Unknown error — no details were recorded.";
|
||||
const shareText = `PinscopeX project ${projectId} failed: ${detail}`;
|
||||
const shareText = `PeriscopeX project ${projectId} failed: ${detail}`;
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { pageMetadata } from "@/lib/site";
|
||||
export const metadata = pageMetadata({
|
||||
title: "Changelog",
|
||||
description:
|
||||
"Recent updates and improvements to Pinscope — new EDA tool support, review accuracy improvements, and platform changes.",
|
||||
"Recent updates and improvements to Periscope — new EDA tool support, review accuracy improvements, and platform changes.",
|
||||
path: "/changelog",
|
||||
});
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export function Nav() {
|
||||
<div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-6">
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<Cpu className="h-5 w-5 text-blue-500" />
|
||||
<span className="text-sm font-semibold tracking-tight">Pinscope</span>
|
||||
<span className="text-sm font-semibold tracking-tight">Periscope</span>
|
||||
</Link>
|
||||
<nav className="hidden sm:flex items-center gap-6 text-sm text-muted-foreground">
|
||||
<Link href="/#features" className="hover:text-foreground transition-colors">
|
||||
|
||||
@@ -7,7 +7,7 @@ import { pageMetadata } from "@/lib/site";
|
||||
export const metadata = pageMetadata({
|
||||
title: "Contact",
|
||||
description:
|
||||
"Talk to the Pinscope team — questions, account help, or enterprise deployment.",
|
||||
"Talk to the Periscope team — questions, account help, or enterprise deployment.",
|
||||
path: "/contact",
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function ContactPage() {
|
||||
Get in touch
|
||||
</h1>
|
||||
<p className="mt-4 text-muted-foreground max-w-lg leading-relaxed animate-fade-up [animation-delay:100ms]">
|
||||
Have a question about Pinscope, need help with your account, or want to
|
||||
Have a question about Periscope, need help with your account, or want to
|
||||
discuss enterprise deployment? We’d love to hear from you.
|
||||
</p>
|
||||
</section>
|
||||
@@ -54,7 +54,7 @@ export default function ContactPage() {
|
||||
<div className="mx-auto max-w-6xl px-6 py-8 flex items-center justify-between text-xs text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<Cpu className="h-4 w-4 text-blue-500" />
|
||||
<span>Pinscope</span>
|
||||
<span>Periscope</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Link
|
||||
|
||||
@@ -6,7 +6,7 @@ import { pageMetadata } from "@/lib/site";
|
||||
export const metadata = pageMetadata({
|
||||
title: "File Upload Guide",
|
||||
description:
|
||||
"Step-by-step export instructions for KiCad, Altium, OrCAD, Cadence Allegro, Siemens Xpedition, EasyEDA, and Autodesk EAGLE — netlists, BOMs, and datasheets ready for Pinscope.",
|
||||
"Step-by-step export instructions for KiCad, Altium, OrCAD, Cadence Allegro, Siemens Xpedition, EasyEDA, and Autodesk EAGLE — netlists, BOMs, and datasheets ready for Periscope.",
|
||||
path: "/file-guide",
|
||||
});
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function LandingPage() {
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<Cpu className="h-5 w-5 text-blue-500" />
|
||||
<span className="text-sm font-semibold tracking-tight">
|
||||
Pinscope
|
||||
Periscope
|
||||
</span>
|
||||
</Link>
|
||||
<nav className="hidden sm:flex items-center gap-6 text-sm text-muted-foreground">
|
||||
@@ -169,7 +169,7 @@ export default function LandingPage() {
|
||||
<br className="hidden lg:block" /> first time
|
||||
</h1>
|
||||
<p className="mt-6 text-lg sm:text-xl text-muted-foreground max-w-xl leading-relaxed animate-fade-up [animation-delay:100ms]">
|
||||
Pinscope reviews your schematic against every datasheet and
|
||||
Periscope reviews your schematic against every datasheet and
|
||||
catches the errors that would otherwise surface at bring-up.
|
||||
</p>
|
||||
<div className="mt-8 flex flex-col items-start gap-4 animate-fade-up [animation-delay:200ms]">
|
||||
@@ -191,7 +191,7 @@ export default function LandingPage() {
|
||||
<div className="rounded-xl border border-border overflow-hidden bg-card/40">
|
||||
<Image
|
||||
src="/report.png"
|
||||
alt="Pinscope validation report"
|
||||
alt="Periscope validation report"
|
||||
width={2400}
|
||||
height={1500}
|
||||
className="w-full h-auto"
|
||||
@@ -328,7 +328,7 @@ export default function LandingPage() {
|
||||
Your designs stay yours
|
||||
</h2>
|
||||
<p className="mt-3 text-muted-foreground text-center max-w-lg mx-auto">
|
||||
Hardware IP is sensitive. Pinscope is built to keep it that way.
|
||||
Hardware IP is sensitive. Periscope is built to keep it that way.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mt-12">
|
||||
{[
|
||||
|
||||
@@ -6,7 +6,7 @@ import { pageMetadata } from "@/lib/site";
|
||||
export const metadata = pageMetadata({
|
||||
title: "Privacy Policy",
|
||||
description:
|
||||
"How Pinscope collects, stores, and protects the schematics, datasheets, and BOMs you upload.",
|
||||
"How Periscope collects, stores, and protects the schematics, datasheets, and BOMs you upload.",
|
||||
path: "/privacy",
|
||||
});
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function SignInPage() {
|
||||
<div className="mx-auto flex min-h-[70vh] max-w-md flex-col justify-center px-4 py-16">
|
||||
<h1 className="font-display text-3xl tracking-tight">Sign in</h1>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Pinscope account for this server. Invite collaborators from a project once they have an account.
|
||||
Periscope account for this server. Invite collaborators from a project once they have an account.
|
||||
</p>
|
||||
<form onSubmit={onSubmit} className="mt-8 flex flex-col gap-4">
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -6,7 +6,7 @@ import { pageMetadata } from "@/lib/site";
|
||||
export const metadata = pageMetadata({
|
||||
title: "Terms of Service",
|
||||
description:
|
||||
"Terms governing your use of Pinscope, including account, payment, and acceptable-use rules.",
|
||||
"Terms governing your use of Periscope, including account, payment, and acceptable-use rules.",
|
||||
path: "/terms",
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ImageResponse } from "next/og";
|
||||
|
||||
export const alt = "Pinscope — Agentic schematic validation";
|
||||
export const alt = "Periscope — Agentic schematic validation";
|
||||
export const size = { width: 1200, height: 630 };
|
||||
export const contentType = "image/png";
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function Image() {
|
||||
letterSpacing: "-0.01em",
|
||||
}}
|
||||
>
|
||||
Pinscope
|
||||
Periscope
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +97,7 @@ export default function Image() {
|
||||
KiCad · Altium · OrCAD · Cadence · Siemens · EasyEDA · EAGLE
|
||||
</div>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||
pinscope.ai
|
||||
periscope.michelebigi.it
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -152,7 +152,7 @@ function autoDetect(headers: string[], candidates: string[]): string | null {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// BOM classification — mirrors pinscopex/taxonomy.py REF_PREFIX_TO_TYPE
|
||||
// BOM classification — mirrors periscopex/taxonomy.py REF_PREFIX_TO_TYPE
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
interface IcMpnEntry {
|
||||
|
||||
@@ -63,7 +63,7 @@ export function OnboardingSurvey() {
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent showCloseButton={false} className="sm:max-w-sm">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Welcome to Pinscope</DialogTitle>
|
||||
<DialogTitle>Welcome to Periscope</DialogTitle>
|
||||
<DialogDescription>
|
||||
Two quick questions to help us improve the product.
|
||||
</DialogDescription>
|
||||
|
||||
@@ -73,7 +73,7 @@ export function Sidebar() {
|
||||
<div className="px-4 py-4 border-b border-border">
|
||||
<Link href="/dashboard" className="flex items-center gap-2">
|
||||
<Cpu className="h-5 w-5 text-blue-500" />
|
||||
<span className="text-sm font-semibold tracking-tight">Pinscope</span>
|
||||
<span className="text-sm font-semibold tracking-tight">Periscope</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ export function ChangelogTimeline({ content }: { content: string }) {
|
||||
</Link>
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<Cpu className="h-4 w-4 text-blue-500" />
|
||||
<span className="text-sm font-medium">Pinscope</span>
|
||||
<span className="text-sm font-medium">Periscope</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
@@ -104,7 +104,7 @@ export function ChangelogTimeline({ content }: { content: string }) {
|
||||
<main className="mx-auto max-w-3xl px-6 py-12">
|
||||
<div className="mb-12">
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">Changelog</h1>
|
||||
<p className="text-sm text-muted-foreground">What's new in Pinscope.</p>
|
||||
<p className="text-sm text-muted-foreground">What's new in Periscope.</p>
|
||||
</div>
|
||||
|
||||
<ol className="relative">
|
||||
|
||||
@@ -115,7 +115,7 @@ export function LegalPageShell({ children }: { children: ReactNode }) {
|
||||
</Link>
|
||||
<Link href="/" className="flex items-center gap-2">
|
||||
<Cpu className="h-4 w-4 text-blue-500" />
|
||||
<span className="text-sm font-medium">Pinscope</span>
|
||||
<span className="text-sm font-medium">Periscope</span>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -40,7 +40,7 @@ interface PdfViewerPanelProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const HIGHLIGHT_CLASS = "pinscope-quote-hl";
|
||||
const HIGHLIGHT_CLASS = "periscope-quote-hl";
|
||||
|
||||
// U+00B5 MICRO SIGN and U+03BC GREEK SMALL MU render identically but the
|
||||
// model and the PDF often disagree on which one — fold them together.
|
||||
|
||||
@@ -131,7 +131,7 @@ export function ImpedancePanel({
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "pinscope.kicad_dru";
|
||||
a.download = "periscope.kicad_dru";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
/**
|
||||
* Open-core seam: cloud/gateway replaces this with Clerk hooks.
|
||||
* Self-host local mode uses Pinscope JWT in localStorage.
|
||||
* Self-host local mode uses Periscope JWT in localStorage.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { authEnabled, localAuthEnabled, TOKEN_STORAGE_KEY } from "@/lib/auth";
|
||||
import { authEnabled, localAuthEnabled, TOKEN_STORAGE_KEY, readAuthToken } from "@/lib/auth";
|
||||
|
||||
export interface AppUser {
|
||||
id: string;
|
||||
@@ -36,12 +36,7 @@ const LOCAL_USER: AppUser = {
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? "";
|
||||
|
||||
function readStoredToken(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
return window.localStorage.getItem(TOKEN_STORAGE_KEY);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return readAuthToken();
|
||||
}
|
||||
|
||||
export function storeAuthToken(token: string | null) {
|
||||
@@ -52,7 +47,7 @@ export function storeAuthToken(token: string | null) {
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
window.dispatchEvent(new Event("pinscope-auth-changed"));
|
||||
window.dispatchEvent(new Event("periscope-auth-changed"));
|
||||
}
|
||||
|
||||
export function useOptionalAuth(): OptionalAuth {
|
||||
@@ -67,10 +62,10 @@ export function useOptionalAuth(): OptionalAuth {
|
||||
const sync = () => setToken(readStoredToken());
|
||||
sync();
|
||||
setReady(true);
|
||||
window.addEventListener("pinscope-auth-changed", sync);
|
||||
window.addEventListener("periscope-auth-changed", sync);
|
||||
window.addEventListener("storage", sync);
|
||||
return () => {
|
||||
window.removeEventListener("pinscope-auth-changed", sync);
|
||||
window.removeEventListener("periscope-auth-changed", sync);
|
||||
window.removeEventListener("storage", sync);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { fetchReport, fetchGraph } from "@/lib/api";
|
||||
|
||||
function describeLoadError(e: unknown): string {
|
||||
if (e instanceof TypeError) {
|
||||
return "Could not reach the Pinscope API. Check that the backend is running and that /api is proxied to it.";
|
||||
return "Could not reach the Periscope API. Check that the backend is running and that /api is proxied to it.";
|
||||
}
|
||||
return e instanceof Error ? e.message : "Failed to load report";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import {
|
||||
legacyReviewedFindingsKey,
|
||||
migrateLocalKey,
|
||||
reviewedFindingsKey,
|
||||
} from "@/lib/storage-keys";
|
||||
|
||||
/**
|
||||
* Lightweight hook that reads the reviewed-findings count from localStorage
|
||||
@@ -12,7 +17,10 @@ export function useReviewedCount(projectId: string): number {
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const stored = localStorage.getItem(`pinscopex:reviewed-findings:${projectId}`);
|
||||
const stored = migrateLocalKey(
|
||||
reviewedFindingsKey(projectId),
|
||||
legacyReviewedFindingsKey(projectId),
|
||||
);
|
||||
if (stored) {
|
||||
const arr: unknown[] = JSON.parse(stored);
|
||||
setCount(arr.length);
|
||||
|
||||
@@ -3,9 +3,14 @@
|
||||
import { useState, useEffect, useRef, useCallback, useMemo } from "react";
|
||||
import type { Finding } from "@/lib/types";
|
||||
import { getFindingKey } from "@/lib/utils";
|
||||
import {
|
||||
legacyReviewedFindingsKey,
|
||||
migrateLocalKey,
|
||||
reviewedFindingsKey,
|
||||
} from "@/lib/storage-keys";
|
||||
|
||||
function storageKey(projectId: string) {
|
||||
return `pinscopex:reviewed-findings:${projectId}`;
|
||||
return reviewedFindingsKey(projectId);
|
||||
}
|
||||
|
||||
export function useReviewedFindings(projectId: string, findings: Finding[]) {
|
||||
@@ -18,7 +23,10 @@ export function useReviewedFindings(projectId: string, findings: Finding[]) {
|
||||
const [reviewedIds, setReviewedIds] = useState<Set<string>>(() => {
|
||||
if (typeof window === "undefined") return new Set();
|
||||
try {
|
||||
const stored = localStorage.getItem(storageKey(projectId));
|
||||
const stored = migrateLocalKey(
|
||||
reviewedFindingsKey(projectId),
|
||||
legacyReviewedFindingsKey(projectId),
|
||||
);
|
||||
if (!stored) return new Set();
|
||||
const arr: string[] = JSON.parse(stored);
|
||||
// Load all stored keys as-is. Pruning here would wipe everything
|
||||
|
||||
@@ -29,7 +29,7 @@ import type {
|
||||
ValidationReport,
|
||||
} from "./types";
|
||||
|
||||
// Mirror of backend.pinscopex.utils.safe_mpn — used to match MPNs
|
||||
// Mirror of backend.periscopex.utils.safe_mpn — used to match MPNs
|
||||
// against datasheet filename stems returned by the server.
|
||||
export function safeMpn(mpn: string): string {
|
||||
return mpn.replace(/\//g, "_").replace(/:/g, "_");
|
||||
@@ -102,7 +102,10 @@ function mapProject(p: Record<string, unknown>): Project {
|
||||
lcscToMpn: (p.lcsc_to_mpn as Record<string, string> | null) ?? null,
|
||||
lcscPayloads: (p.lcsc_payloads as Record<string, LcscPayload> | null) ?? null,
|
||||
componentMpns: (p.component_mpns as ComponentMpnBuckets | null) ?? null,
|
||||
pinscopeVersion: (p.pinscope_version as string | null | undefined) ?? null,
|
||||
periscopeVersion:
|
||||
(p.periscope_version as string | null | undefined) ??
|
||||
(p.pinscope_version as string | null | undefined) ??
|
||||
null,
|
||||
netlistFormat: (p.netlist_format as Project["netlistFormat"]) ?? null,
|
||||
netlistSubdesigns: (p.netlist_subdesigns as string[] | null) ?? null,
|
||||
placementStatus: (p.placement_status as Project["placementStatus"]) ?? "draft",
|
||||
@@ -817,7 +820,7 @@ export async function downloadEcoCsv(projectId: string): Promise<void> {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "pinscope-eco.csv";
|
||||
a.download = "periscope-eco.csv";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
@@ -1125,7 +1128,7 @@ export interface LcscResolvePassiveResponse {
|
||||
mpn: string;
|
||||
safe_mpn: string;
|
||||
// Shape mirrors the persisted passive specs model in
|
||||
// backend/pinscopex/models.py — a discriminated union over `specs_type`.
|
||||
// backend/periscopex/models.py — a discriminated union over `specs_type`.
|
||||
// Keep loose at this layer; the wizard surfaces a small summary.
|
||||
model: Record<string, unknown>;
|
||||
cached: boolean;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Open-core auth switch.
|
||||
*
|
||||
* - Clerk: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY set (cloud build).
|
||||
* - Local: NEXT_PUBLIC_AUTH_MODE=local (self-host Pinscope accounts).
|
||||
* - Local: NEXT_PUBLIC_AUTH_MODE=local (self-host Periscope accounts).
|
||||
* - Off: neither — signed-in stub user "local", matching backend.
|
||||
*
|
||||
* NEXT_PUBLIC_* vars are inlined at build time.
|
||||
@@ -16,4 +16,23 @@ export const localAuthEnabled =
|
||||
process.env.NEXT_PUBLIC_AUTH_MODE === "local" &&
|
||||
!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
|
||||
|
||||
export const TOKEN_STORAGE_KEY = "pinscope_token";
|
||||
export const TOKEN_STORAGE_KEY = "periscope_token";
|
||||
/** Pre-rebrand localStorage key — read once and migrate. */
|
||||
export const LEGACY_TOKEN_STORAGE_KEY = "pinscope_token";
|
||||
|
||||
export function readAuthToken(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
const current = window.localStorage.getItem(TOKEN_STORAGE_KEY);
|
||||
if (current) return current;
|
||||
const legacy = window.localStorage.getItem(LEGACY_TOKEN_STORAGE_KEY);
|
||||
if (legacy) {
|
||||
window.localStorage.setItem(TOKEN_STORAGE_KEY, legacy);
|
||||
window.localStorage.removeItem(LEGACY_TOKEN_STORAGE_KEY);
|
||||
return legacy;
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
export const SITE_URL = (
|
||||
process.env.NEXT_PUBLIC_SITE_URL?.trim().replace(/\/$/, "") ||
|
||||
"https://pinscope.ai"
|
||||
"https://periscope.michelebigi.it"
|
||||
);
|
||||
|
||||
export const SITE_NAME = "Pinscope";
|
||||
export const SITE_NAME = "Periscope";
|
||||
|
||||
export const SITE_DESCRIPTION =
|
||||
"Pinscope reviews your schematic against every datasheet and catches the errors that would otherwise surface at bring-up. Works with KiCad, Altium, OrCAD, Cadence, Siemens, EasyEDA, and EAGLE.";
|
||||
"Periscope reviews your schematic against every datasheet and catches the errors that would otherwise surface at bring-up. Works with KiCad, Altium, OrCAD, Cadence, Siemens, EasyEDA, and EAGLE.";
|
||||
|
||||
export const SITE_TAGLINE = "Agentic schematic validation";
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/** Migrate localStorage keys from pre-rebrand `pinscopex:` prefix. */
|
||||
|
||||
export function migrateLocalKey(newKey: string, legacyKey: string): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
const current = localStorage.getItem(newKey);
|
||||
if (current != null) return current;
|
||||
const legacy = localStorage.getItem(legacyKey);
|
||||
if (legacy != null) {
|
||||
localStorage.setItem(newKey, legacy);
|
||||
localStorage.removeItem(legacyKey);
|
||||
return legacy;
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function reviewedFindingsKey(projectId: string) {
|
||||
return `periscopex:reviewed-findings:${projectId}`;
|
||||
}
|
||||
|
||||
export function legacyReviewedFindingsKey(projectId: string) {
|
||||
return `pinscopex:reviewed-findings:${projectId}`;
|
||||
}
|
||||
|
||||
export function deratingSettingsKey(projectId: string) {
|
||||
return `periscopex:derating-settings:${projectId}`;
|
||||
}
|
||||
|
||||
export function legacyDeratingSettingsKey(projectId: string) {
|
||||
return `pinscopex:derating-settings:${projectId}`;
|
||||
}
|
||||
|
||||
export function deratingOverridesKey(projectId: string) {
|
||||
return `periscopex:derating-overrides:${projectId}`;
|
||||
}
|
||||
|
||||
export function legacyDeratingOverridesKey(projectId: string) {
|
||||
return `pinscopex:derating-overrides:${projectId}`;
|
||||
}
|
||||
@@ -272,7 +272,7 @@ export interface Project {
|
||||
lcscToMpn?: Record<string, string> | null;
|
||||
lcscPayloads?: Record<string, LcscPayload> | null;
|
||||
componentMpns?: ComponentMpnBuckets | null;
|
||||
pinscopeVersion?: string | null;
|
||||
periscopeVersion?: string | null;
|
||||
// "pads" | "edif" | "kicad_*" — netlist the user uploaded. null on legacy projects.
|
||||
// projects predating EDIF support; treat null as PADS for rendering.
|
||||
netlistFormat?: "pads" | "edif" | "kicad_xml" | "kicad_sexp" | "kicad_sch" | null;
|
||||
|
||||
Reference in New Issue
Block a user