docs: adicionar plano do notebook story

This commit is contained in:
Evandro Fonseca Junior committed 2026-08-31 00:14:45 -03:00
1 parent b4a2b81d55
commit d768dfa791
1 file changed
+18 -13
@@ -42,7 +42,7 @@
- Test: `app/components/story/NotebookStory.test.tsx` — modo reduzido, elegibilidade 3D e integração de tabs.
- Modify: `app/components/cinematic/CinematicHome.tsx` — usar `NotebookStory` e remover `PrintReel`/`ScrollStory`.
- Modify: `app/globals.css` — somente tokens/classes novas do notebook, sem tocar no `globals.css` raiz do usuário.
- Delete after migration: `app/components/story/PrintReel.tsx`, `app/components/story/ScrollStory.tsx`, `app/components/story/StoryStage3D.tsx`, `app/components/story/phoneScreenTexture.ts` e testes órfãos.
- Delete after migration: `app/components/story/PrintReel.tsx`, `app/components/story/ScrollStory.tsx`, `app/components/story/StoryStage3D.tsx`, `app/components/story/phoneScreenTexture.ts`, seus testes órfãos e `public/models/phone-quaternius.glb`.
---
@@ -69,7 +69,7 @@ Invoke-WebRequest `
- [ ] **Step 2: Inspect the GLB node and material names before code**
Read the GLB header and JSON chunk with a one-off Node script. Record the actual node names, parent relationships, bounding boxes and material names in the implementation notes. The script must fail if there is no separate lid-like node.
Read the GLB header and JSON chunk with a one-off Node script. Record the actual validated mapping (`Laptop_Base`, `Laptop_Lid`), parent relationships, bounding boxes and material names in `NotebookStage3D.tsx` and `CREDITS.md`. The script must fail if there is no separate lid-like node.
```js
const fs = require('node:fs');
@@ -172,7 +172,7 @@ const projectsFixture = [
},
{
id: "sc-plus",
title: "SCPLUS",
title: "SC Plus Aquicultura",
subtitle: "Landing Page Institucional",
demoUrl: "https://scplus.evandro.dev.br",
technologies: ["Next.js", "React"],
@@ -344,14 +344,14 @@ export default function NotebookBrowser(props: NotebookBrowserProps): JSX.Elemen
- [ ] **Step 1: Write failing semantic and interaction tests**
Test observable behavior, not implementation details:
Test observable behavior, not implementation details. Import `userEvent` in the test file before using `userEvent.setup()`:
```tsx
it('renders the active project as an accessible tabpanel', () => {
render(<NotebookBrowser {...props} activeIndex={1} />);
expect(screen.getByRole('tablist', { name: /projetos em produção/i })).toBeInTheDocument();
expect(screen.getAllByRole('tab')[1]).toHaveAttribute('aria-selected', 'true');
expect(screen.getByRole('tabpanel')).toHaveAccessibleName(/SCPLUS/i);
expect(screen.getByRole('tabpanel')).toHaveAccessibleName(/SC Plus Aquicultura/i);
});
it('moves between tabs with arrow keys and Home/End', async () => {
@@ -510,21 +510,26 @@ export default function NotebookStage3D(props: NotebookStage3DProps): JSX.Elemen
- [ ] **Step 1: Write failing stage boundary tests**
Mock only the R3F/Drei renderer boundary; assert the observable fallback contract. The test mock must expose the hook used by production:
Mock only the R3F/Drei renderer boundary; assert the observable fallback contract. Keep the `useGLTF` mock persistent for the failure case because React error boundaries may retry rendering:
```tsx
jest.mock("@react-three/drei", () => ({
...jest.requireActual("@react-three/drei"),
Html: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
useGLTF: jest.fn(() => ({ scene: new THREE.Group() })),
}));
const mockedUseGLTF = jest.mocked(useGLTF);
function mockUseGLTFFailure() {
mockedUseGLTF.mockImplementationOnce(() => {
beforeEach(() => {
mockedUseGLTF.mockImplementation(() => ({ scene: new THREE.Group() }));
});
it("returns usable content when GLB loading fails", () => {
mockedUseGLTF.mockImplementation(() => {
throw new Error("GLB failed");
});
}
// render and assert the DOM fallback, not the Canvas internals
});
```
- [ ] **Step 2: Run stage tests and verify the expected red failure**
@@ -536,10 +541,8 @@ pnpm exec jest app/components/story/NotebookStage3D.test.tsx --runInBand
- [ ] **Step 3: Implement the GLB rig**
Implementation requirements:
- `useGLTF('/models/laptop-kenney.glb')` only; no remote URL.
- Clone the scene and apply materials from the notebook color map.
- Resolve the validated base/lid/screen/keyboard nodes from Task 1.
- Clone the scene, resolve the validated `Laptop_Base` and `Laptop_Lid` nodes, and apply materials from the notebook color map.
- Attach the browser surface to a calibrated screen anchor on the lid using Drei `Html transform`.
- Apply `getNotebookFrame(progress.current, projects.length)` to lid angle, screen opacity, device opacity and cursor props.
- Keep base stable; only lid articulates.
@@ -584,6 +587,8 @@ export type NotebookStoryProps = {
projects: PortfolioProject[];
};
export function canUseNotebook3D(view: Pick<Window, "innerWidth" | "matchMedia">): boolean;
export function getNotebookSeekProgress(index: number, tabCount: number): number;
export default function NotebookStory(props: NotebookStoryProps): JSX.Element;
```