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:
@@ -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