style: format notebook contracts

This commit is contained in:
Evandro Fonseca Junior committed 2026-08-30 23:42:26 -03:00
1 parent 8b858528f8
commit c66ff7fde6
3 files changed
+78 -22

No files matched your search

+12 -7
View File
@@ -1,4 +1,4 @@
import { portfolio, type PortfolioProject } from "@/lib/portfolio-data"; import { type PortfolioProject, portfolio } from "@/lib/portfolio-data";
import { selectNotebookProjects } from "./notebookProjects"; import { selectNotebookProjects } from "./notebookProjects";
const projectsFixture = portfolio.projects as PortfolioProject[]; const projectsFixture = portfolio.projects as PortfolioProject[];
@@ -13,7 +13,11 @@ describe("selectNotebookProjects", () => {
"sev-modaintima", "sev-modaintima",
"as-servicos", "as-servicos",
]); ]);
expect(result.every((project) => project.screenshotSrc.startsWith("/assets/prints/"))).toBe(true); expect(
result.every((project) =>
project.screenshotSrc.startsWith("/assets/prints/"),
),
).toBe(true);
}); });
it("omits a project when its screenshot mapping is missing", () => { it("omits a project when its screenshot mapping is missing", () => {
@@ -22,15 +26,16 @@ describe("selectNotebookProjects", () => {
{ ...projectsFixture[0], id: "unknown-live-project" }, { ...projectsFixture[0], id: "unknown-live-project" },
]); ]);
expect(result.some((project) => project.id === "unknown-live-project")).toBe(false); expect(
result.some((project) => project.id === "unknown-live-project"),
).toBe(false);
}); });
it("omits a project without an approved demo URL", () => { it("omits a project without an approved demo URL", () => {
const result = selectNotebookProjects( const projectsWithoutDemoUrl = projectsFixture.map((project) =>
projectsFixture.map((project) =>
project.id === "nutriscan" ? { ...project, demoUrl: null } : project, project.id === "nutriscan" ? { ...project, demoUrl: null } : project,
), ) as unknown as PortfolioProject[];
); const result = selectNotebookProjects(projectsWithoutDemoUrl);
expect(result.some((project) => project.id === "nutriscan")).toBe(false); expect(result.some((project) => project.id === "nutriscan")).toBe(false);
}); });
+7 -2
View File
@@ -16,14 +16,19 @@ const NOTEBOOK_ORDER = [
"as-servicos", "as-servicos",
] as const; ] as const;
const SCREENSHOT_BY_PROJECT_ID: Record<(typeof NOTEBOOK_ORDER)[number], string> = { const SCREENSHOT_BY_PROJECT_ID: Record<
(typeof NOTEBOOK_ORDER)[number],
string
> = {
nutriscan: "/assets/prints/print-nutriscan.png", nutriscan: "/assets/prints/print-nutriscan.png",
"sc-plus": "/assets/prints/print-scplus.png", "sc-plus": "/assets/prints/print-scplus.png",
"sev-modaintima": "/assets/prints/print-sev.png", "sev-modaintima": "/assets/prints/print-sev.png",
"as-servicos": "/assets/prints/print-asservicos.png", "as-servicos": "/assets/prints/print-asservicos.png",
}; };
function isApprovedDemoUrl(value: PortfolioProject["demoUrl"]): value is string { function isApprovedDemoUrl(
value: PortfolioProject["demoUrl"],
): value is string {
return typeof value === "string" && /^https?:\/\//.test(value); return typeof value === "string" && /^https?:\/\//.test(value);
} }
+59 -13
View File
@@ -52,8 +52,19 @@ function getPhase(progress: number): NotebookPhase {
return "close"; return "close";
} }
function getActiveTabIndex(progress: number, tabCount: number, phase: NotebookPhase): number { function getActiveTabIndex(
if (tabCount < 1 || phase === "approach" || phase === "open" || phase === "boot" || phase === "cursor-in" || phase === "close") { progress: number,
tabCount: number,
phase: NotebookPhase,
): number {
if (
tabCount < 1 ||
phase === "approach" ||
phase === "open" ||
phase === "boot" ||
phase === "cursor-in" ||
phase === "close"
) {
return -1; return -1;
} }
@@ -61,11 +72,16 @@ function getActiveTabIndex(progress: number, tabCount: number, phase: NotebookPh
return tabCount - 1; return tabCount - 1;
} }
const browseProgress = (progress - BROWSE_START) / (BROWSE_END - BROWSE_START); const browseProgress =
(progress - BROWSE_START) / (BROWSE_END - BROWSE_START);
return clamp(Math.floor(browseProgress * tabCount), 0, tabCount - 1); return clamp(Math.floor(browseProgress * tabCount), 0, tabCount - 1);
} }
function getCursorPoint(progress: number, phase: NotebookPhase, activeTabIndex: number): { x: number; y: number } { function getCursorPoint(
progress: number,
phase: NotebookPhase,
activeTabIndex: number,
): { x: number; y: number } {
if (phase === "cursor-in") { if (phase === "cursor-in") {
const cursorProgress = (progress - 0.34) / 0.1; const cursorProgress = (progress - 0.34) / 0.1;
return { return {
@@ -82,8 +98,18 @@ function getCursorPoint(progress: number, phase: NotebookPhase, activeTabIndex:
return point; return point;
} }
function isCursorClicking(progress: number, phase: NotebookPhase, tabCount: number): boolean { function isCursorClicking(
if (tabCount < 1 || phase === "approach" || phase === "open" || phase === "boot" || phase === "close") { progress: number,
phase: NotebookPhase,
tabCount: number,
): boolean {
if (
tabCount < 1 ||
phase === "approach" ||
phase === "open" ||
phase === "boot" ||
phase === "close"
) {
return false; return false;
} }
@@ -95,16 +121,24 @@ function isCursorClicking(progress: number, phase: NotebookPhase, tabCount: numb
return false; return false;
} }
const browseProgress = (progress - BROWSE_START) / (BROWSE_END - BROWSE_START); const browseProgress =
(progress - BROWSE_START) / (BROWSE_END - BROWSE_START);
const slotProgress = (browseProgress * tabCount) % 1; const slotProgress = (browseProgress * tabCount) % 1;
return slotProgress < 0.12; return slotProgress < 0.12;
} }
export function getNotebookFrame(progress: number, tabCount: number): NotebookFrame { export function getNotebookFrame(
progress: number,
tabCount: number,
): NotebookFrame {
const normalizedProgress = normalizeProgress(progress); const normalizedProgress = normalizeProgress(progress);
const normalizedTabCount = Math.max(0, Math.floor(tabCount)); const normalizedTabCount = Math.max(0, Math.floor(tabCount));
const phase = getPhase(normalizedProgress); const phase = getPhase(normalizedProgress);
const activeTabIndex = getActiveTabIndex(normalizedProgress, normalizedTabCount, phase); const activeTabIndex = getActiveTabIndex(
normalizedProgress,
normalizedTabCount,
phase,
);
const isApproach = normalizedProgress < 0.1; const isApproach = normalizedProgress < 0.1;
const isOpening = phase === "open"; const isOpening = phase === "open";
const isBooting = phase === "boot"; const isBooting = phase === "boot";
@@ -112,7 +146,11 @@ export function getNotebookFrame(progress: number, tabCount: number): NotebookFr
const lidAngle = isOpening const lidAngle = isOpening
? interpolate(0, OPEN_ANGLE, (normalizedProgress - 0.1) / 0.15) ? interpolate(0, OPEN_ANGLE, (normalizedProgress - 0.1) / 0.15)
: isClosing : isClosing
? interpolate(OPEN_ANGLE, 0, (normalizedProgress - HOLD_END) / (1 - HOLD_END)) ? interpolate(
OPEN_ANGLE,
0,
(normalizedProgress - HOLD_END) / (1 - HOLD_END),
)
: normalizedProgress >= 0.25 : normalizedProgress >= 0.25
? OPEN_ANGLE ? OPEN_ANGLE
: 0; : 0;
@@ -128,8 +166,14 @@ export function getNotebookFrame(progress: number, tabCount: number): NotebookFr
: isClosing : isClosing
? interpolate(1, 0.92, (normalizedProgress - HOLD_END) / (1 - HOLD_END)) ? interpolate(1, 0.92, (normalizedProgress - HOLD_END) / (1 - HOLD_END))
: 1; : 1;
const cursorVisible = (phase === "cursor-in" || phase === "browse" || phase === "hold") && normalizedTabCount > 0; const cursorVisible =
const { x: cursorX, y: cursorY } = getCursorPoint(normalizedProgress, phase, activeTabIndex); (phase === "cursor-in" || phase === "browse" || phase === "hold") &&
normalizedTabCount > 0;
const { x: cursorX, y: cursorY } = getCursorPoint(
normalizedProgress,
phase,
activeTabIndex,
);
return { return {
phase, phase,
@@ -140,6 +184,8 @@ export function getNotebookFrame(progress: number, tabCount: number): NotebookFr
cursorVisible, cursorVisible,
cursorX, cursorX,
cursorY, cursorY,
cursorClicking: cursorVisible && isCursorClicking(normalizedProgress, phase, normalizedTabCount), cursorClicking:
cursorVisible &&
isCursorClicking(normalizedProgress, phase, normalizedTabCount),
}; };
} }