diff --git a/v2-klasB/Les02-OpenCode/Les02-Docenttekst.md b/v2-klasB/Les02-OpenCode/Les02-Docenttekst.md new file mode 100644 index 0000000..6e1e2e4 --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Docenttekst.md @@ -0,0 +1,536 @@ +# Les 2 — OpenCode Pro: Rules, Worktrees & Scroll Animations +## Docenttekst + Docenthandleiding (Klas B — 2 uur, online) + +**Les:** 2 van 18 +**Duur:** 120 minuten online +**Vorige les:** Les 1 — Introductie AI Developer leerlijn + +--- + +## Leerdoelen +- Begrijpen waarom we OpenCode kiezen i.p.v. Cursor (multi-provider, open source, alternatieven) +- OpenCode Desktop kunnen draaien met Plan/Build/@explore/init +- `AGENTS.md` regelbestand schrijven (officieel format) +- `opencode.json` config met permissions +- `opencode-worktree` plugin via `ocx` installeren +- Parallel agents via Sessions sidebar +- Live: Next.js 16 + GSAP + Lenis scroll-site opzetten + +--- + +## Lesflow + +**Geen heen-en-weer switchen.** De les is opgedeeld in: +- **Theorie 1** — uninterrupted uitleg (slides 4-6) +- **Live Demo 1** — alle features tonen achter elkaar (slide 7) +- **Theorie 2** — uninterrupted uitleg (slides 8-12) +- **Live Demo 2** — setup + bouw SmoothScroll (slide 13) +- Pauze +- Lesopdracht + +--- + +# DEEL 1 — Docenthandleiding (volg dit live tijdens de les) + +## VÓÓR DE LES — Setup (30 min) + +### 1. OpenCode Desktop + +- Download van https://opencode.ai/download +- Installeer +- Log in met OpenAI key (uit Brightspace) + +### 2. `ocx` + worktree plugin + +```bash +curl -fsSL https://kdco.dev/ocx/install.sh | sh +ocx add kdco/worktree --from https://registry.kdco.dev +``` + +Sluit en open OpenCode opnieuw zodat plugin geladen wordt. + +### 3. Demo-repo aanmaken + +```bash +cd ~ +npx create-next-app@latest scroll-demo \ + --typescript --tailwind --app --eslint --no-src-dir --turbopack +cd scroll-demo +npm install gsap @gsap/react lenis +git init +git add . +git commit -m "init: next 16 + gsap + lenis" +``` + +### 4. Test je setup + +- Open OpenCode Desktop → File → Open Folder → `~/scroll-demo` +- Type in chat: `Wat is de stack?` → moet Next.js 16 + Tailwind herkennen +- Type `/init` en abort (Esc) — alleen testen dat 't werkt +- Maak test-worktree om plugin te valideren: + ``` + Maak een testworktree feature-test + ``` +- Klik **+ New Session** linksboven, open folder van die worktree +- Sluit Session, verwijder worktree: + ```bash + git worktree remove ~/.local/share/opencode/worktree/scroll-demo/feature-test + ``` + +### 5. Browser tabs open + +- https://opencode.ai/download +- https://opencode.ai/docs/rules/ +- https://gsap.com/resources/React/ +- https://github.com/kdcokenny/opencode-worktree + +### 6. Backup plan + +Als plugin live faalt: +```bash +git worktree add ../scroll-demo-hero -b feature-hero +``` +Dan handmatig nieuwe Session → Open Folder. + +--- + +## TIJDENS DE LES + +### Blok 1 — Welkom + Terugblik (10 min) + +**Slides 1, 2, 3.** + +- Start meeting +- Slide 2: vraag in chat: "Wie heeft tot nu toe alleen met chat (ChatGPT/Claude) gewerkt?" +- Slide 3: planning langslopen. **Benadruk** dat we eerst theorie geven, dan demo, dan weer theorie, dan demo. Geen heen-en-weer. + +**Geen demo nodig in dit blok.** + +--- + +### Blok 2 — THEORIE 1: Waarom OpenCode + Kern features (20 min) + +**Slides 4, 5, 6. Achter elkaar, zonder switchen.** + +#### Slide 4 — Waarom OpenCode (en niet Cursor) — 6 min + +**Vertel:** +"Iedereen kent Cursor. Sommigen gebruiken het al. Goede tool. Waarom beginnen we dan met OpenCode? + +Drie redenen. + +**Eén — alternatieven kennen is professioneel.** Wie alleen Cursor kent, zit vast aan één tool. Een goede dev kent meerdere coding-assistenten en kiest per project. + +**Twee — provider-keuze.** Cursor heeft eigen routing — jij hebt niet vol overzicht welk model wanneer wordt gebruikt. OpenCode is provider-agnostisch: OpenAI vandaag, Anthropic morgen, lokaal met Ollama als je dat wilt. Jij beslist. + +**Drie — open source = geen lock-in.** Voor je eindopdracht en je carrière is dit relevant. Tools veranderen, abonnementen veranderen. Open source blijft beschikbaar. + +Belangrijk: dit is **geen Cursor-bashing**. Cursor is uitstekend. We komen er later in de leerlijn op terug. Vandaag de open variant — dan kun je daarna zelf kiezen." + +Loop tabel langs. + +#### Slide 5 — Wat is OpenCode — 7 min + +**Vertel:** +"OpenCode is een AI coding-IDE. 60.000+ GitHub stars. Gemaakt door SST. Werkt met elke provider — OpenAI, Anthropic, Google, Groq, of lokaal met Ollama. + +Twee smaken: Desktop en TUI. Wij gebruiken Desktop. Waarom? +- Visuele diff-viewer (klik accept/reject) +- File tree links, Sessions sidebar +- Multiple sessies naast elkaar +- Sneller leren als je begint + +De TUI bestaat ook. Goed voor SSH-servers, scripts, CI/CD. Ik laat 'm 1 minuut zien in de demo — daarna 100% Desktop." + +Loop tabel langs. + +#### Slide 6 — Plan/Build/@explore + /init — 7 min + +**Vertel:** +"Vier dingen die je echt moet kennen. + +**Modes** — twee modes, Tab om te wisselen: +- **plan** is read-only. Denkt mee, leest files, wijzigt niks. Goed voor verkenning en brainstorm. +- **build** is default. Schrijft code, voert commands uit. + +**Sub-agents** — drie ingebouwde: +- `@explore` is read-only verkenning. Eigen context, spaart tokens. +- `@general` is brede taak. +- `@scout` is gerichte zoek. + +**`/init` commando** — eerste actie in elk nieuw project. Scant je repo, stelt vragen, schrijft je `AGENTS.md`. + +**Best practice:** plan eerst, lees mee, geef feedback, dan Tab → build. Dit voorkomt rommel. + +Dat was de theorie. Nu zien jullie het live." + +--- + +### Blok 3 — LIVE DEMO 1: Desktop Tour (10 min) + +**Slide 7.** + +**Setup vooraf:** OpenCode Desktop open op `~/scroll-demo`. Systeem-terminal open naast OpenCode voor de TUI-demo. + +#### Stap 1 — TUI in 1 minuut (1 min) + +- Switch naar systeem-terminal +- Type: `opencode` +- Toon kort de TUI met chat onderaan +- Type: `wat is de stack van dit project?` → krijg antwoord +- Druk **Tab** → switch naar plan mode → tab terug +- **Ctrl+D** om te sluiten + +**Zeg:** "Dat was de TUI. Werkt prima. Vandaag verder met Desktop." + +#### Stap 2 — Desktop UI tour (2 min) + +- Switch naar Desktop +- Wijs aan: + - **File tree** links (klik op `app/page.tsx` om te tonen) + - **Chat panel** onderaan + - **Diff viewer** in midden + - **Sessions sidebar** (toon dat 't er is, leeg op één session na) + - **Model selector** + **mode indicator** bovenin + +**Zeg:** "Alles op één scherm. File tree, chat, diff. Veel makkelijker dan switchen tussen terminals." + +#### Stap 3 — Plan mode demo (2 min) + +- Onder in chat, check dat mode-badge op `build` staat +- Druk **Tab** → wisselt naar `plan` +- Type in chat: `analyseer dit project en stel verbeteringen voor` +- Wacht op antwoord +- **Zeg:** "Zie? Concrete voorstellen. Geen file wijzigingen. Veilig om los te laten." + +#### Stap 4 — Build mode demo (2 min) + +- Druk **Tab** → terug naar `build` +- Type: `voeg een comment bovenaan app/page.tsx toe met de tekst "scroll demo"` +- Toon diff in viewer — klik **Accept** +- Open `app/page.tsx` in file tree — comment staat er + +**Zeg:** "Plan is denken, build is doen." + +#### Stap 5 — @explore demo (1.5 min) + +- Type in chat: `@explore wat is de folder structuur van dit project?` +- Toon dat het in eigen context werkt +- **Zeg:** "Read-only sub-agent. Spaart tokens, kan niks kapot maken." + +#### Stap 6 — /init demo (1.5 min) + +- Type: `/init` +- Beantwoord de vragen die OpenCode stelt (Next.js 16, GSAP, scroll-animaties) +- Wacht tot AGENTS.md gegenereerd is +- Open `AGENTS.md` in file tree — toon wat 't gegenereerd heeft +- **Zeg:** "Dit is een goede basis. Volgende blok: hoe we 'm aanvullen met onze regels." + +--- + +### Blok 4 — THEORIE 2: AGENTS.md + opencode.json + plugin + stack (15 min) + +**Slides 8, 9, 10, 11, 12. Achter elkaar, zonder switchen.** + +#### Slide 8 — AGENTS.md — 3 min + +**Vertel:** +"Belangrijkste concept van vandaag. `AGENTS.md` is **dé** manier om OpenCode jouw regels te geven. Officieel format, automatisch in elke context. + +Twee locaties: +- `./AGENTS.md` in je repo — versioned, deelt met team +- `~/.config/opencode/AGENTS.md` — globaal, jouw persoonlijke voorkeuren + +Externe regels mag je referencen met `@rules/styling.md` — agent laadt 'm lazy. + +Tip: schrijf je AGENTS.md zoals je 'm aan een junior dev zou geven. Concreet, controleerbaar." + +#### Slide 9 — opencode.json — 3 min + +**Vertel:** +"Naast regels heb je config. Welk model, welke permissies, welke plugins. + +`$schema` geeft autocomplete in je editor. +`model` is `provider/model-id`. + +Belangrijkste is **permissions**. Drie levels: `allow`, `ask`, `deny`. Glob patterns voor bash én files. + +Voor student-project: `bash.*` op `ask` is veilig — je reviewed elke shell command. Productie: alleen specifieke folders editable." + +#### Slide 10 — Worktree plugin — 3 min + +**Vertel:** +"Plugin van **kdcokenny** — community lid, ook achter `ocx` extension manager. Twee tools voor de agent: +- `worktree_create` — branch + worktree +- `worktree_delete` — commit + cleanup + +Installatie via `ocx`. Restart na install. + +Verschil TUI vs Desktop: +- TUI: plugin spawnt automatisch nieuw terminal venster +- Desktop: plugin maakt de worktree, jij opent 'm in nieuwe Sessions tab + +Klein verschil, zelfde resultaat: parallel agents." + +#### Slide 11 — Demo stack — 3 min + +**Vertel:** +"De stack waarmee we vandaag bouwen — en waarom dit dé combinatie is voor scroll storytelling. + +**Next.js 16** — uit oktober 2025. Belangrijkste verandering t.o.v. 15: `cookies()`, `headers()`, `params`, `searchParams` zijn allemaal async. Altijd `await`. Turbopack is default — snellere builds. + +**GSAP 3.15** — sinds januari 2025 100% gratis, incl. ScrollTrigger en SplitText. Hét animatie-pakket op pro-niveau. Apple product pages, Stripe homepage, OpenAI marketing — allemaal GSAP. Award-winning sites op Awwwards en FWA: vrijwel altijd GSAP. + +**Lenis 1.3** — door **Darkroom Engineering** (voorheen Studio Freight, een award-winning studio uit Brooklyn die zelf onder andere voor Studio Freight, Activision en Riot werkt). Beste smooth scroll lib. Integreert naadloos met GSAP ScrollTrigger. + +**`@gsap/react`** — geeft je de officiele `useGSAP` hook. Drop-in voor useEffect met auto cleanup. + +**Waarom niet Framer Motion?** +Framer Motion is uitstekend — voor app UI. Modals, page transitions, micro-interacties. Maar voor scroll storytelling (zoals jij gaat bouwen) wil je timing-precisie en GPU-optimalisaties die GSAP biedt en Framer Motion niet matcht. Pro-studios zoals **Active Theory**, **Locomotive**, **Resn**, **Darkroom Engineering** — allemaal GSAP-based. + +Daarom staat in onze AGENTS.md: 'Geen Framer Motion'. Het is geen anti-Framer, het is pro-GSAP voor dit type werk." + +#### Slide 12 — Onze AGENTS.md — 3 min + +**Vertel:** +"Hier zie je onze concrete regels voor dit project. Let op de structuur: +- Stack — versies expliciet +- Hard rules — niet onderhandelbaar +- Patterns — hoe we organiseren +- Done — wat klaar betekent + +Regels die opvallen: +- 'Geen Framer Motion' — voorkomt dat AI default naar 't bekendere +- 'useGSAP, nooit useEffect' — werkt met Strict Mode +- 'Lenis sync via gsap.ticker.add' — voorkomt jank +- 'await params in Next 16' — anders runtime error + +Deze regels heb ik uit ervaring. In de volgende demo zet ik dit in, en zie je dat de AI ze volgt." + +--- + +### Blok 5 — LIVE DEMO 2: Setup + Worktree + SmoothScroll (15 min) + +**Slide 13.** + +**Setup vooraf:** OpenCode Desktop nog open op `~/scroll-demo`. Plugin al geïnstalleerd. + +#### Stap 1 — AGENTS.md vervangen (3 min) + +- Open `AGENTS.md` in Desktop file tree +- Selecteer alles → vervang met content uit slide 12: + +```markdown +# Project Rules + +## Why this stack +High-end scroll storytelling site (Awwwards/FWA niveau). +GSAP + Lenis = pro standaard — Apple, Stripe, OpenAI, Active Theory, +Locomotive, Darkroom Engineering. Framer Motion is voor app UI, niet voor +scroll storytelling. Kies GSAP voor timing-precisie + GPU-perf. + +## Stack +- Next.js 16 (App Router, TypeScript, Turbopack) +- TailwindCSS · GSAP 3.15+ (incl. ScrollTrigger, SplitText) +- Lenis 1.3+ (`lenis/react`) +- @gsap/react voor de useGSAP hook + +## Hard rules +- Geen Framer Motion, react-spring, AOS +- Animaties altijd in Client Components (`"use client"`) +- Animatie-code in useGSAP(() => {}, { scope: ref }) +- Nooit useEffect voor GSAP code +- Lenis sync: gsap.ticker.add met autoRaf: false +- Next 16: await params, await cookies(), await headers() + +## Patterns +- Eén wrapper in app/layout.tsx +- Tailwind voor layout; GSAP voor transform/opacity + +## Done = +- Geen hydration warnings +- ScrollTrigger.refresh() na dynamische content +``` + +- Cmd+S +- Type in chat: `Lees AGENTS.md. Welke regels ga jij volgen, en waarom kiezen we deze stack?` +- Toon dat het ook de **why** uitlegt — niet alleen de regels napapegaait + +#### Stap 2 — opencode.json maken (3 min) + +- Right-click in file tree → New File → `opencode.json` +- Plak: + +```json +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/gpt-4o-mini", + "permission": { + "bash": { + "*": "ask", + "git *": "allow", + "npm *": "allow", + "rm *": "deny" + } + } +} +``` + +- Cmd+S +- Restart OpenCode (File → Reload Window) +- Type: `rm -rf node_modules` → toon dat 't blokkeert +- Type: `git status` → toon dat 't direct werkt +- **Zeg:** "Geen surprises meer." + +#### Stap 3 — Worktree plugin in actie (3 min) + +- Type: `Welke tools heb je beschikbaar?` +- Wijs aan: `worktree_create`, `worktree_delete` in lijst +- Type: `Maak een worktree feature-hero voor een hero-sectie.` +- Wacht tot plugin klaar is +- Klik **+ New Session** linksboven +- **Open Folder** → navigeer naar `~/.local/share/opencode/worktree/scroll-demo/feature-hero/` +- Nieuwe Session opent — toon dat 't naast main staat in sidebar + +#### Stap 4 — SmoothScroll laten bouwen (5 min) + +- In de **feature-hero** Session, type: + ``` + Bouw een SmoothScroll wrapper component volgens AGENTS.md. + Plaats in components/SmoothScroll.tsx en wrap children in app/layout.tsx. + ``` +- Wacht op diff +- **Wijs aan wat de regels volgen:** + - `"use client"` bovenaan + - `useGSAP` import (niet useEffect) + - `gsap.ticker.add` met `autoRaf: false` + - Geen Framer Motion of andere libs +- Klik **Accept** + +#### Stap 5 — Bewijs van regels (1 min) + +**Zeg:** "Zie je? Met goede AGENTS.md krijg je consistente output. Zonder zou de AI waarschijnlijk Framer Motion pakken of useEffect gebruiken. Schrijf je regels op, krijg je betrouwbare resultaten." + +--- + +### Blok 6 — Pauze (15 min) + +**Slide 14.** + +Stuur in chat: "Terug over 15 minuten." Camera mag uit. + +--- + +### Blok 7 — Lesopdracht (35 min) + +**Slide 15.** + +#### Briefing (3 min): + +"Eigen worktree maken, sectie kiezen, bouwen. Plan eerst, dan Build." + +Loop checklist op slide 15 langs. + +#### Tijdens 30 min werken: + +- Tim blijft in main meeting-room +- Breakout-rooms op verzoek +- Check in chat: "Wie heeft worktree? Type W." → "Wie ziet animatie? Type A." + +#### Veelvoorkomende problemen: + +| Probleem | Oplossing | +|----------|-----------| +| Plugin niet beschikbaar | `ocx list` om te checken | +| Worktree-tool onbekend | Desktop herstarten na ocx add | +| Sessions sidebar leeg | Klik `+ New Session` | +| `useGSAP is not a function` | `npm install @gsap/react` | +| Hydration mismatch | `"use client"` mist of `useEffect` ipv `useGSAP` | +| Lenis import error | Niet `@studio-freight/lenis`, maar `lenis` | +| ScrollTrigger niets | `gsap.registerPlugin(ScrollTrigger)` mist | +| Async params error Next 16 | `await params` toevoegen | + +#### Check-in (~25 min in): + +"Wie heeft werkende animatie? Type A in chat." Bij <50%: 5 min klassikaal debug. + +--- + +### Blok 8 — Huiswerk + Afsluiting + +**Slides 16, 17.** + +- Loop huiswerk eisen langs +- "Volgende keer: **Introductie Cursor** — de commerciele tegenhanger. Cursor rules vs AGENTS.md, Composer, wanneer welke tool." +- Vragen + feedback + +--- + +# DEEL 2 — Docenttekst (referentie per slide) + +## Slide 4 — Waarom OpenCode + +Vertel waarom **niet** Cursor: +- Cursor is goed maar commercieel ($20/mnd), eigen routing +- OpenCode is gratis (alleen API-kosten), open source, jij kiest model +- Voor je carriere: meerdere tools kennen > vasthangen aan één +- We komen later in leerlijn terug op Cursor — vandaag de open variant + +## Slide 5 — Wat is OpenCode + +Provider-agnostisch, scriptable, multi-instance. Twee smaken: Desktop (vandaag), TUI (kort tonen). + +## Slide 6 — Plan/Build/@explore + /init + +Tab voor mode switch. `@explore` voor verkenning. `/init` voor eerste AGENTS.md. + +## Slide 8 — AGENTS.md + +Officieel format. Automatisch in context. Concrete regels werken, vage regels niet. + +## Slide 9 — opencode.json + +Schema voor autocomplete. Model. Permissions (allow/ask/deny). Plugins. + +## Slide 10 — Worktree plugin + +Via ocx installeren. Twee tools voor agent. Desktop: nieuwe Session tab; TUI: auto terminal. + +## Slide 11 — Stack + +Next.js 16 (async params, Turbopack). GSAP gratis. Lenis door darkroomengineering. useGSAP hook. + +## Slide 12 — Onze AGENTS.md + +Concrete regels die voorkomen dat AI default naar bekende-maar-verkeerde keuzes (Framer Motion, useEffect). + +--- + +## Bronnen voor studenten + +**OpenCode:** +- Docs: https://opencode.ai/docs/ +- Download: https://opencode.ai/download +- Rules: https://opencode.ai/docs/rules/ +- Config: https://opencode.ai/docs/config/ +- GitHub: https://github.com/sst/opencode + +**Plugin:** +- opencode-worktree: https://github.com/kdcokenny/opencode-worktree +- ocx: https://github.com/kdcokenny/ocx + +**Stack:** +- Next.js 16: https://nextjs.org/blog/next-16 +- GSAP: https://gsap.com/docs/v3/ +- GSAP React: https://gsap.com/resources/React/ +- Lenis: https://github.com/darkroomengineering/lenis + +--- + +## Backup-onderwerpen (als tijd over) + +1. **Custom sub-agents** in `~/.config/opencode/` +2. **MCP servers** — context7 voor up-to-date library docs +3. **opencode-workspace plugin** (uitgebreidere variant) +4. **Cursor live laten zien** — vergelijken +5. **Ollama setup** voor lokale models (privacy) diff --git a/v2-klasB/Les02-OpenCode/Les02-Huiswerk.md b/v2-klasB/Les02-OpenCode/Les02-Huiswerk.md new file mode 100644 index 0000000..1c7f351 --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Huiswerk.md @@ -0,0 +1,153 @@ +# Les 2 — Huiswerk +## Bouw je eigen scroll-animatie landing page + +**Vak:** AI-Assisted Development +**Opleiding:** NOVI Hogeschool Utrecht +**Deadline:** Voor de volgende les (Les 3) +**Inleveren:** GitHub repo URL + Vercel deploy URL + `WORKFLOW.md` + +--- + +## Doel + +Bouw een **kleine scroll-animatie landing page** met OpenCode, gebruikmakend van AGENTS.md, opencode.json en worktrees. Deploy hem op Vercel. + +Dit is geen design-opdracht — focus is op de **workflow** en de **technologie**. Een simpele maar werkende site is genoeg. + +--- + +## Voorbeeld-thema (kies zelf!) + +Suggesties: +- **Mini portfolio** (over jou — projects, contact) +- **Restaurant landing** (menu, story, reserveer) +- **Product launch** (features, prijs, signup) +- **Conferentie / event** (speakers, schedule, register) +- **Fitness app marketing** (hero, features, testimonials) + +Eigen idee mag ook — even checken met Tim als 't iets totaal anders is. + +--- + +## Eisen (verplicht) + +### Code +- [ ] Next.js 16 (App Router, TypeScript, Turbopack) +- [ ] Eigen **`AGENTS.md`** (mag onze als basis) +- [ ] Eigen **`opencode.json`** met permissions + (optioneel) plugin config +- [ ] **Minimaal 3 secties** met scroll-animaties +- [ ] **Minimaal 3 verschillende animatie-technieken** (bv. fade-in, parallax, SplitText, horizontaal, stagger) +- [ ] **Lenis** smooth scroll werkt (geen "harde" scroll) +- [ ] TailwindCSS voor styling +- [ ] Werkt zonder console-errors of hydration warnings + +### Workflow +- [ ] **Minstens 2 worktrees** gebruikt (mag via plugin of handmatig) +- [ ] Branches gemerged naar `main` +- [ ] Worktrees opgeruimd +- [ ] Code op GitHub gepushed + +### Deployment +- [ ] **Vercel deploy** werkend, URL in README +- [ ] Geen broken pages / 500's + +### Documentatie +- [ ] **`WORKFLOW.md`** in repo-root met: + - Welke prompts werkten goed? + - Welke regels heb je later toegevoegd aan AGENTS.md (en waarom)? + - Hoe heb je worktrees georganiseerd? + - Wat zou je anders doen volgende keer? + - Screenshot van `git worktree list` tijdens het werk + - Max 400 woorden — concreet, geen fluff + +--- + +## Bonus (optioneel, geen extra punten — wel meer leren) + +- **Eigen sub-agent** definieren in `~/.config/opencode/` (bv. een "animation-reviewer") +- **MCP server** toevoegen (bv. context7 voor up-to-date docs in agent context) +- **GSAP advanced**: horizontale scroll met pin + scrub, of SplitText met stagger +- **Custom Lenis options** experimenteren (`lerp`, `duration`, etc.) +- **Lighthouse score** > 90 op alle 4 metrics +- **Reduced motion respecteren** (`prefers-reduced-motion` media query) +- **Eigen `ocx` plugin** schrijven (zie kdcokenny/ocx docs) + +--- + +## Tijd-indicatie + +- Setup (Next.js + deps + AGENTS.md + opencode.json): 30 min +- 3 scroll-secties bouwen via opencode (3× ~30 min): ~90 min +- Polish + responsiveness: 30 min +- Deploy op Vercel: 15 min +- `WORKFLOW.md`: 15 min + +**Totaal: ~3 uur.** Loop je vast? Vraag in Brightspace of plan een korte 1-op-1 met Tim. + +--- + +## Tips + +### Workflow tips + +- **Begin met een werkende `SmoothScroll` wrapper** — zonder dat doet niks goed. +- **Eén sectie per worktree** — niet alles in main mengen. +- **Plan-mode eerst** voor elke nieuwe sectie. Krijg een plan, geef feedback, dan pas Build. +- **AGENTS.md update tijdens werk** — loop je tegen iets aan dat de agent steeds verkeerd doet? Voeg een regel toe. + +### Technische tips + +- **`useGSAP` heeft een `scope` prop** — gebruik 'm met `useRef` zodat je `gsap.from(".class")` selectors scoped zijn aan je component. +- **`ScrollTrigger.refresh()`** als je dynamisch content laadt — anders triggert het niet. +- **`prefers-reduced-motion`** kun je checken met een media query in CSS én in JS (`window.matchMedia('(prefers-reduced-motion: reduce)')`). +- **Lenis `lerp`** is "hoe smooth" — lager = soepeler maar trager. Default `0.1` is goed. +- **Pin-secties** mogen niet inside flex/grid die hun height anders berekenen — pas op met layout. + +### Deploy tips + +- Vercel pakt je Next.js automatisch op +- `npm run build` lokaal eerst om te checken of er geen TS errors zijn +- Environment vars (als je ze hebt) in Vercel dashboard zetten + +--- + +## Inleveren + +1. **GitHub repo** — push alle code naar `main` +2. **Vercel deploy URL** in README van je repo +3. **`WORKFLOW.md`** in root +4. **Brightspace inlevering**: + - GitHub URL + - Vercel URL + - Korte note (1-2 zinnen): wat is je trots-punt? + +--- + +## Beoordeling + +| Criterium | Punten | +|-----------|--------| +| 3+ scroll-animaties, verschillend qua techniek | 3 | +| `AGENTS.md` aanwezig + concreet | 1 | +| `opencode.json` met permissions/plugin | 1 | +| Minstens 2 worktrees gebruikt (zichtbaar in git log) | 1 | +| Vercel deploy werkt | 1 | +| `WORKFLOW.md` concreet + screenshot | 2 | +| Geen console errors / hydration warnings | 1 | +| **Totaal** | **10** | + +Voldoende = 6+. Bonus telt mee bij twijfelgevallen. + +--- + +## Veelvoorkomende valkuilen (uit eerdere groepen) + +- **AGENTS.md te vaag** — "gebruik clean code" werkt niet. Concrete regels werken. +- **Te veel willen** — beter 3 secties goed dan 7 half. Schaal omlaag bij twijfel. +- **Worktrees na merge laten staan** — opruimen, anders chaos. +- **`Lenis` van oude package importeren** — `@studio-freight/lenis` werkt niet meer. Gebruik `lenis`. +- **`useEffect` voor GSAP code** — gebruik `useGSAP` van `@gsap/react`. Anders Strict Mode bugs. +- **Next.js 16 async params vergeten** — `cookies()`, `headers()`, `params`, `searchParams` zijn nu async. Altijd `await`. +- **Geen Vercel deploy testen vóór deadline** — minstens 1 dag van tevoren deployen. + +Succes! diff --git a/v2-klasB/Les02-OpenCode/Les02-Huiswerk.pdf b/v2-klasB/Les02-OpenCode/Les02-Huiswerk.pdf new file mode 100644 index 0000000..10fe2cb --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Huiswerk.pdf @@ -0,0 +1,143 @@ +%PDF-1.4 +% ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 5 0 R /F4 6 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +6 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +7 0 obj +<< +/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +9 0 obj +<< +/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 12 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +10 0 obj +<< +/PageMode /UseNone /Pages 12 0 R /Type /Catalog +>> +endobj +11 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260518122502+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260518122502+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Les 2 Huiswerk) /Trapped /False +>> +endobj +12 0 obj +<< +/Count 4 /Kids [ 4 0 R 7 0 R 8 0 R 9 0 R ] /Type /Pages +>> +endobj +13 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1631 +>> +stream +GauHL939k-']/^ggg+VAW0[@EB@iG>3FtfU;n?Dt3_CT*>HTQql+`i@^Crp>9Gs"W+BeR/rU(UQf_D36B:K%9T>K=r$C&AK%b)dW9[D[BCAkFDo3(!t5TFZ'+Ti?s5&0Zpl(n[Q[$4dDVf=)j$p_DpoS_]MpA3n%')#;StA%!PCKUHrlW>e7&VPi`7i&+QD+i-T6^@DgfSf]j9;3lL_SC@2RY5&gWB6B=TMfUV5\ofKE9HCS*>Y^XV6d-O&#CtYaVJtMH+p<2f3$sH:3h+T6[bX(Y*6cH$WL8-X**-^NfU_E5PG8:aZgE%HHZ`lUC$gW!FpO:M2ms"+!boVQ:BC5uJ,9E\O%`DVh4;q\3F=Cg.9d[M95oIC.((;bFB$2TS0uI_`0F%YYdC+[H'Z"qMG%&%T!-O-kFbt'\_M0=He0ojGmo=K82ZeY;L^_G9:e>[EAMT7P'Tb`99P+[RUP3qL9+XF$`'p7'BmfD0Q$jrp:jJ`)m\%G`B2LZ%d4CEJGaumi!]HEN.94/W^o^3_R.6LG;s(Yd*2cAuZV20M1GBFXB+U-I.gB%WeI+o.pnurAS3Q%p9_sa^5%iVO/.OC5_m6!r3S2H-6f\7Ne0mHtL"kL^/-=\BGQ:XW$L5T;I*u%3&OXO:7jV(aPT1(&-BsbauK\Io-GGiKRVr,j"P>;&YL"BeH7W(h`!Qr#Q`7/_k:83u@>f?#)P)''=H6FNRI:`e9oN@Qm-&sYsmAtkNJlqlfM,3gP^\VdH9*'p-WDQV6H&-k])H(s`\rD(?5e9L_JCFhT99%61bbo6eG9n/=?/eYO2'GY":TLnb3I&5>e;)_E_Z%m<=mesCM1r\XU&BX98UQ;-Njb)RiL<+Tn'#/WXjO!SHI5TkfVlj_F4K'N#-I"g)agr#871S&f3WI/o6EUYY=,BoNr-s-fYXH?fWgYoi$[!\'>tuMCR>taiUFu.8dscTASZF40^D*N)WZoT-XZ.i\eB1t]YQ7"=K6`rmpRJdH/>*n-h@80ZFO]HVB`_W*!+c=E1MBE$g[nJ@3%#?X@e.crPFMV]04L>7ftl3s,#6n-qfBe*"+USB"^@mQ_I:i(kP"_WlS.N1.o\@(3je9@=G^^:Z"?'*S5f0aI+a,A-#<1rt@)("*@4qXefps;jd\:#-Y6E$=/6a3f4QcS44l8\(PRR@/UJlcQC[~>endstream +endobj +14 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1724 +>> +stream +Gb"/'>BA7S&:XAWfXFeHe6);?]UUEI-prPQ)sBq$<*QW`.$B^XQLa@fg5b!r\M.I;\9*HPPGGLkg2AX:G"'3_(MQ's3P`fm]Qceo:`)uZGd[k;;`Gh&h^k6$2"Z1"h8jSG$?kdeJ"SSjJfiW29Oe-d'A?=IJJSn[MW+miXp[T0K'"b8D@s+j^^24rm"kF&'WeE\m=8*ig9^`#L``d,l5/"9`HJK8%b5CQ-k'aM-)b.Mle)d<6Qa6]qmgbDYWi->%iAJC46BuUEbqg2D_>&;hf8=t%N=ft@hNr`7/"gje.&n5hB#LVN.2oGQU$2L<#1pLh"ni:_sD[FX'+c>Xhcl@[#Be&&23,#EAZ6VX*e9CAT9r\@j9i\>ZoR,N4n[q3EGm_po:14po#,:@l!M^4t!N:]&B&ejh\:[;0s(cAJo@j]]>%;_,4HpO`bY_g=GIC5H9h$2SI\1MGZMUm(](5P"9_^8bp(:(E<,'$f"DE'8gCD9:*;?'GPC0D)(j=YcI/gStS>--h$c]?SnQ[W:etY#qKCIYQ-djPT?PE:il!j7qE$Voo>$dK;>KS7SAS%h;94E643%g=Rib,4&'fgF]qVDo(KoV_Vqh2/s@,9YkqA&IgXc't_0Y(F,rCb6/Ic_b%s-jE#43jEb,TTds:50E<.rK,L?:CV'W2-3JWDA2.^j+TJhjXZ#1@'!6gHAG,#4dUpjT0HB:oY?qN_br)*EKb.LdQl&JDLB`fW=n!;9GQ6`3b#3[ja:XY11EkEF&Y,2^0OnXd&`cP#5EmuQIoPT+Q>o']Nk[AMg]7_igM9T[ST7l7#=gXQb2gb@J@[Kj4(-J;H1c'Y]oJ40/&Y.*doY_qE35?o(Z($9B"LkK#jRa1^#_e*$$):Dr/b]rF9QCdq/*,dg6I=1VBo\V:[?=;'^7,?T4/sM(Kc.oaoMlB3a=srU%E.NeBQGtm1VKp0M"Za>N.h7.KY/)M+]Z-!H(mEsYL;IFh)%c+Vj?1%'YgHPI,3];5$$]dBd"M<^PO[I,Fn9^c0s7:k&[c4j>oh/)6/1o^G<.uY#*4*D,E,$$bl:6\/gg.#\-ToEP\Z'\:q<#fendstream +endobj +15 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1334 +>> +stream +GauHK9j?@A&AJ$CbgB#B79Y,AEbHOcJ>@==J4_H.#\blo.r1DUaj;+(m@BMQ@r,Z,Zk"!-fJE!?]^ohV/:?*pmplWA'#Dt`%5roR%(8f6Lk]jeomZN=okE?15TFAr+U&K>jpP/>/&:b5+4AL?Ad*hk0"B+PN.hZgl32_ZJDr83hm7k:^(i=<#dZ[dq&B/?nL6oU0(FhAHBA&c3DpmlWmB)@Qq-t.2Doh/EaJYT(!VHcj//NB4S+7:I/F\YTUGHG_iIs0]l^u7mehHC4Z4aJ$4XH&@PsPFLA@qnJh[SR]Ok$A>qN19Q.)a3s*//F-JB&mtaC6^+,?aR]OXWM@d5qOLm@-tWY]V&j^ONfrc./>bNSl>/kE6d97fBqZ!=ZX#%oiUN.2C@oTTXpaXuN44d5u*"6#^61rYHUk[LVBWr9!b]icH4X#uQ]+6%)E)YnnMNFXr?!YEPee,Nb(sd[n@MZB^,j&%-.li5*08,('FQa0[pdLgVI";Gaq'Tb.Q.VqMo/tAa2,XDJK,(e2Q8%/egC2jnW!a'O]'"oN'qM)nM^2>i8oW'd%Ln#HCcBZq[WCX88q#4:]>40/3>o5_Kj^oI)`PoB"*X`jeX=Z1Q1j>Glld),&kjJZbkUUUX[MP:uNi>$Rkk\/_OpN4;A^*qnS5`rM.P>&;"J#P=HSU1mt'o6Ci:,3fZ8HT]`.;7Gkp0+A)![/"s7_Q2nf,>r_.[WArJe'ZkP_Z3R)1aL2@@Nk<7h@rN[qd:N^RSc/IgHOlV0ZEEqBW@puu7^m5$,&(*pKEL_N37gJ<_Oc:5>PfWff_`ZN#06!uO3&>dPM)$S_QZ9mag15im59UqYmJ~>endstream +endobj +16 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1876 +>> +stream +Gatm;=``=U&:W67fU%Pf___DNSa!M)i](YCUQ,u%p][C"$8Mo9+D'+!oj**D7+#K*]!8>h_o9+Z[n?cT^52_]$\6\\*5c:BNi@.i#:O+R'[\^F5GTEKnV2nP+nJik6-G`3NY0F(o2)5;qm>u<#PB7?3hsIBshN.%#:I1lG?K>Xp>C1mqSUPJ8qE(8%K1gE7\]2%:J/NQJUXH"O3b8m)F^845YI1==(<^0[MVHXint28Sij'fq>00eoJRhk1M'@l:]nA/k.aO0C'EtN?042O9f@Y-gcafR_pEbG!)c$fi]>q-5k\`E1)L,<@jS\B)pkr'EJMF^p5=>]/+]U;JS7@0n$CU#d>g;\t8tMbOf?a2*DLfj%Asug&[GWf?;sqk&-)!S@$a'kg<2*oK]Z?Gls8-mq:rBm"RUA;bM>SV.bH-rB7J]?janS7W`NA\$LHmni0W%-,a@$3Bb4^29J.HCUM7rqKl]gQdjbmC"K4285Ebpl-K7gs*S_U8AQ[c`_piL)Q[n`?n,*'C_e35`!$lr7+eX8Ni1kT/8S-.AHkn9"bG!FMZUmS_.dh3P)gnW-K;\sR+a/WT"hZMD>haF%,H%g+O@3Xi1Xi1-lk8D$EL'sNfn[,PL,`_1[8`/URKVuXii;;`kKO_b_`?ps_SF.,f\QG4^"&BZ_58u'g"6E%_7g*-,mafhGF_1XYU(^kW6JfEJ0IQi(L7"1,!csL`H1RRcYi`F,53H+b'Yaa7E#Y5Ae$\nH0S?DB+@j`dD>&CpP"6c8p^&QPVG];SYNs#e\Z.#JT?MWqA.r0V.&i6ieY:\!u-Cbt"97*io8#)tO+!W,d%kB`OC?PtqdbmDS7Z`$5mYb"C'#BOg2gpL_2,3*D=E'gNa;Il8PfO@EUfsFn(nOgE0LWuSl&;UIO*&47.+(NkC5ILHYk45)ROAqDUIhS9Xlfr"O[&')/5(G8F(8t+A-L8nS:%d(ffk?mp5[PoHWIGkZ0!U>qHtE@h\D/7gtdLGpG,141b20Sm5&(V(9tLbF.CE(-6=:3:>R$C'fR?Im]O7M1N/73fG&P_`^Za@R,NQDg0$s#r0]FMice#`@[05GoE[X&KS_WYLbt"?!rmHrIkPkjY0^A\f"b0YihW@Cd>])4p`D*\f9UTJS.$(;mkt\=Xepkm*I_u)9mh1&';>J9:b3kpSa3'bpf(Ikr.Gjl]nEt#9SSF;3WY.endstream +endobj +xref +0 17 +0000000000 65535 f +0000000061 00000 n +0000000122 00000 n +0000000229 00000 n +0000000341 00000 n +0000000546 00000 n +0000000661 00000 n +0000000766 00000 n +0000000971 00000 n +0000001176 00000 n +0000001381 00000 n +0000001451 00000 n +0000001743 00000 n +0000001821 00000 n +0000003544 00000 n +0000005360 00000 n +0000006786 00000 n +trailer +<< +/ID +[<51c9ef4a08589d21f13f527085c33787><51c9ef4a08589d21f13f527085c33787>] +% ReportLab generated PDF document -- digest (opensource) + +/Info 11 0 R +/Root 10 0 R +/Size 17 +>> +startxref +8754 +%%EOF diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.md b/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.md new file mode 100644 index 0000000..fd106ef --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.md @@ -0,0 +1,188 @@ +# Les 2 — Lesopdracht +## Bouw een scroll-animatie sectie in een worktree + +**Vak:** AI-Assisted Development +**Opleiding:** NOVI Hogeschool Utrecht +**Duur:** 35 minuten (in de les) +**Inleveren:** niet — je code uit deze opdracht is je basis voor het huiswerk + +--- + +## Doel + +Bouw één scroll-animatie sectie in een Next.js 16 project, met: +- OpenCode **Desktop** (met onze AGENTS.md geladen) +- Een eigen worktree (via plugin of handmatig) +- Een nieuwe Sessions tab voor die worktree +- GSAP + Lenis +- TailwindCSS voor styling + +Het hoeft niet groot — een werkende sectie van 1-2 schermen is genoeg. + +--- + +## Vereisten (al opgezet vóór de les) + +In je `scroll-demo` repo staat al: +- Next.js 15 starter (App Router, TypeScript, Tailwind) +- `AGENTS.md` met onze regels +- `opencode.json` met permissions +- `npm install`-ed: `gsap`, `@gsap/react`, `lenis` +- `components/SmoothScroll.tsx` wrapper (klassikaal gebouwd) + +Geen scroll-demo repo? Vraag in chat, of clone: `git clone ` + +--- + +## Wat moet er staan? + +- [ ] Eigen **worktree** + branch voor jouw sectie +- [ ] OpenCode draait in die worktree +- [ ] Een **werkende scroll-animatie sectie** (kies één type — zie suggesties) +- [ ] Volgt de regels uit `AGENTS.md` (geen Framer Motion, `useGSAP`, scope, etc.) +- [ ] **Commit + push** naar je branch + +--- + +## Stappenplan + +### Stap 1 — Worktree aanmaken (3 min) + +**Met opencode-worktree plugin** (in main Session): + +``` +Maak een worktree voor feature-mijn-sectie (kies een naam, bv. feature-hero). +``` + +De agent roept `worktree_create` aan. De worktree-folder wordt gemaakt op `~/.local/share/opencode/worktree/scroll-demo/feature-hero/`. + +**Daarna in Desktop:** +1. Klik **+ New Session** linksboven in de Sessions sidebar +2. Kies **Open Folder** → navigeer naar de worktree-folder +3. Nieuwe Session opent met eigen chat-context — main blijft beschikbaar in sidebar + +**Of handmatig** (als plugin niet werkt): + +```bash +git worktree add ../scroll-demo-hero -b feature-hero +``` +Dan in Desktop: **+ New Session → Open Folder → ../scroll-demo-hero**. + +### Stap 2 — Sectie kiezen (2 min) + +Kies één van deze: + +| Type | Wat | Moeilijkheid | +|------|-----|--------------| +| **Hero met SplitText reveal** | Tekst die letter-voor-letter verschijnt op scroll | ⭐⭐ | +| **Features grid stagger** | 3-koloms grid die in-fade'd, staggered | ⭐ | +| **Testimonials horizontaal** | Horizontale scroll-sectie met cards (pin) | ⭐⭐⭐ | +| **Gallery met parallax** | Afbeeldingen die parallax-en bij scroll | ⭐⭐ | +| **Stats counter** | Cijfers die optellen wanneer in beeld | ⭐⭐ | + +### Stap 3 — Plan-mode (5 min) + +In opencode (start in plan-mode of `Tab` om te wisselen): + +> Plan een [type sectie] component die voldoet aan de regels in AGENTS.md. Toon me het plan: welke files maak je, welke GSAP API gebruik je, hoe sync je met Lenis? + +Lees het plan. Stel vragen / geef feedback. Niet meteen op Tab naar build. + +### Stap 4 — Build-mode (15 min) + +`Tab` → build. Geef commando: + +> Bouw de geplande sectie. Plaats hem in components/ en importeer in app/page.tsx zodat ik 'm direct kan testen. + +Laat het bouwen. Lees mee. Stel correcties als iets ingaat tegen AGENTS.md. + +### Stap 5 — Testen (5 min) + +In de worktree: + +```bash +npm run dev +``` + +Open `http://localhost:3000`. Scroll. Check: +- Smooth scroll werkt (Lenis) +- Jouw animatie triggered op het juiste moment +- Geen console errors +- Geen hydration mismatch + +Werkt het niet? Vraag opencode om te debuggen. + +### Stap 6 — Commit + push (3 min) + +```bash +git add . +git commit -m "feat: [jouw sectie]" +git push origin [jouw branch] +``` + +### Stap 7 — (Optioneel) Cleanup + +Klaar met deze worktree? In de main Session: + +> Sluit de feature-X worktree en merge naar main. + +Of handmatig: + +```bash +git worktree remove ~/.local/share/opencode/worktree/scroll-demo/feature-hero +cd ~/scroll-demo +git merge feature-hero +``` + +Sluit ook de feature-Session in Desktop (right-click → Close). + +--- + +## Veelvoorkomende problemen + +| Probleem | Oplossing | +|----------|-----------| +| `useGSAP is not a function` | `npm install @gsap/react` | +| Hydration mismatch warning | `"use client"` mist of `useEffect` voor GSAP code | +| Lenis import error | Niet `@studio-freight/lenis` — gebruik `lenis` package | +| ScrollTrigger niks doet | `gsap.registerPlugin(ScrollTrigger)` boven in file | +| Geen smooth scroll | `SmoothScroll` wrapper mist in `app/layout.tsx` | +| Plugin niet beschikbaar | `ocx list` om te checken, evt. `ocx add` opnieuw | +| Worktree-tool niet bekend | Plugin niet correct geïnstalleerd of opencode niet herstart | +| `gsap.context is not a function` | Oude versie — `npm i gsap@latest @gsap/react@latest` | +| `params is not iterable` / `Cannot read params` | Next.js 16: gebruik `const { id } = await params` (async!) | + +--- + +## Klaar zijn = je hebt: + +1. Een werkende **scroll-animatie sectie** in je app +2. Gebruik gemaakt van **OpenCode + worktrees** +3. De **regels uit AGENTS.md** gevolgd +4. **Smooth scroll** werkend via Lenis + +Deze code is je basis voor het huiswerk — daar bouw je 'm uit naar een complete landing page. + +--- + +## Voorbeelden van prompts die werken + +**Goed:** +- "Plan een hero sectie met SplitText reveal. 1 lange koptekst, 1 paragraaf. Lees AGENTS.md eerst." +- "Bouw een features grid van 3 cards die fade-in'en met stagger 0.15 wanneer de sectie 80% in beeld is." +- "Voeg een horizontale scroll sectie toe met 5 cards. Gebruik pin: true en scrub: 1." + +**Te vaag (krijg je rommel):** +- "Maak iets met scroll." +- "Voeg animaties toe." +- "Maak het mooi." + +**Tip:** wees specifiek over wat én volg de patterns uit AGENTS.md. Hoe specifieker, hoe beter. + +--- + +## Reflectievragen (1 min, niet inleveren) + +- Werkt OpenCode anders met goede AGENTS.md dan zonder? +- Voelde plan-mode → build flow natuurlijker? +- Waar zou je worktrees morgen gebruiken op je eigen project? diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.pdf b/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.pdf new file mode 100644 index 0000000..1317176 --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Lesopdracht.pdf @@ -0,0 +1,149 @@ +%PDF-1.4 +% ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 6 0 R /F5 7 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +6 0 obj +<< +/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +7 0 obj +<< +/BaseFont /ZapfDingbats /Name /F5 /Subtype /Type1 /Type /Font +>> +endobj +8 0 obj +<< +/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +9 0 obj +<< +/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +10 0 obj +<< +/Contents 17 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 13 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +11 0 obj +<< +/PageMode /UseNone /Pages 13 0 R /Type /Catalog +>> +endobj +12 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260518122502+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260518122502+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Les 2 Lesopdracht) /Trapped /False +>> +endobj +13 0 obj +<< +/Count 4 /Kids [ 5 0 R 8 0 R 9 0 R 10 0 R ] /Type /Pages +>> +endobj +14 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1679 +>> +stream +Gb!;c>BcPr&:WeDbbNq&$5,;FQeaKb2Wo2:[.^@MISm>,M#gJhFgo,77t:V%[c;VemV2%GK90^-S'*_m"VX,-\crJVX"g,N;4n?;T7P_7L@JKX*#i5n_Hc6V%M4PfQHt*VQMuM!1fA9tiqgls^eKl/cN]k;_R\!DiAo5Z*omqt5;su%cS[k'"6%aL\bkI9"kmGPR='H;'4/e.p4K?Ciq=%4Q!Ct*.**O\9KV\CsT($D*j1?5%1=hTC`3CEf5)7/d+Xj6`ELJa.`?G\)n=HUHZ'5..un3Xg3*r0RAdiUJ]UFR%6(?;\g2sRM"Sfg#)_k*qYs8ic9hfn;B8a)Y<_RJt^TE-d4!>[9"M=KEs=?4pb1;pSngG\EgcH@@N(X2OC:np20Q6pW>tYkC]_M9i-/!(%9eVh=/B;X2c`?TOh_H2/CHpDL6"\^<5Sp)*Se73$8[`pEj8a-O/"[6p]_),bTods,pqthJ=/Ka&BePRC`eV2:gBnQ`]KOO!Dnf77jOStF>"e6#UCnX:11BD=/Br)iT&T8.[Di^5;;'6L+-t^3nOr\7k\^YB=@mZCpi6\7.B_(/hSZOZQTA(oc''H(e36`g>$TMmN#Pqg4i5BZ7rbo>DUerDn@U?'N=Da@$f]1B<$HPIs9-^uL8f1-1'9s>0MRt%E*hD-&$*nps'dt8[Z&%CNPYTOp,BA;V/E[R/Ceqf)3k^dU>\__[#]li2FKCo3-Cmi.XZO7S'^,pZhO;P+),TKGJlOQg^eW)QE>`eT-)p-2QbS>BAYK$^k$toY;WO[upJ=.7PFAjkd(p`[_lB[VMIATS9W7.A1_M0iHGJ`d4cNLRN,:\/SiY%Mpo'93o-'(malFkkUT>HDq6n=X][o2o53QM2o]s\Eit4k[6]>SD7P]CSiD,Q%7q#j)2$Od?Up!r;F`rH"E#&R\e^RLo5Pqh=NS<=e3A;GOEs7VWGu$j)]).`F]Y3#s;9XKtdSAOosOX&O91(r.mC=]SPe4;Jdf\NjI/jW1e/D03b\hLodCGd-n]e.:dMguT^hB([9jY6U*!O8duegJ0p,7Ok.4ktZ4V=LeGg\3Jj:m4&&?q*$OqT2m2`B%XRgN5%:lhl8XeS"9ooN,@0D-\G5kKNG]TO:D1e,mJT"MaH^nFbg\SG,`0[rq1++7.B@h8J(G!A;an$W=ZUh-*'[`#QKDG0=[&endstream +endobj +15 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1751 +>> +stream +Gb"/'94Q^7%)2%/i95=t6W0.:mFJC4gTB^Mn^^>cN'uEXp$a4QC&@3$l9WO8&uAmC;rbfZ-j';HHj%5=T_Ms_GFSd5]S)JI#)pH?"\]I.bLc7LdeVW@4Q)q&JU!?(&I/qojp.oo;h,sO+QSsaX.LiGV%Kd=J1mA&tA:>YR,C,=QSE&%Lpd#H3b4k\,JJXHA7u&e1?W;b9=DsGSl3TeF+8W4c@I`LI?a&C!\^ko9.E`/!CapHD)S"]fKP.LDB6BcrNDAEB^d_(%<8[81!dg)IbZIoXX"UDkn`^&O>c'T6.Pc/9?&,-l$bXL3UVb^J6bT)`AK*KbM$ma_Rh)\9rB[JK,5bQXi5B7?;Kkq:LU,VXd537OjD(`RNYNVrBe`,7+JP/7Ag9Y:]2*VBQW^9C]sQP-#2A-22c/pa&XS>tD[-ugll,*o(CU/`'n2]hUjZBN4S`\%c/!V45]@6X'P.u<`GKIq*E?=$@NHZ)F%5SR0^M\h*QoEGW=^,*Cu2rB":_7REgRM%G0K!KP2Q_t0MGc)lEBH5?Z.&oknnHSt^\;ec[YBjjAN;jJ0"Ztc`omH3Y5iRU&-;GCcfJid]QcPP'l>t9hniA;=.].1-TWDH5-DKl!T*%p@6=-8#O)D&BsFcnb8UjH3\f%"`#@ntd?glE(G(m#DrM<,#_2?IoLFe8n'E.p1&&ItbF^"toE/PEikNO0_>D7I1tdhO)U]bQ[u=u=>(&!jU8TP$u3'7b"&YYdiPX!V+^h,%6;'TjS.R(!t9nYk^r,#$*@OPd+Z\#US9FA18YOou%NWT1#&1,mU+l#(TCJpV4TM0Y[C,JqKeNeZnI,Po/5UAR5n7*jU7!k,*-4>`c0JL5J$6Q%I=*G/'!e/[AcXlj_5:Og'%g,Y\[22=T^=GXC,>^NFuQrP-sYL"f\XBtn19g_>B)k'>\O?^C9_=H&MhgM;2!@&3LI67+S]'&SI"oY-&R!D`t1eY$\)kA;hOi)o(H2o#R:9ZBG[P5,PI$^\S&k`_gHD=IuLUiaYl>>?BJh5)ee*+q3XfU:5_i:hgi/R-jXBhC*P>csgGu9MpeR\GG9`U?a?r"=u=2VUA';ukX?LROnG8\heHLA5cb.JDd&KC/(1K*doJXPIuC_^FQB-;K<;S1_6GPgAeiXbhMX-$]"LrCX[5d-8tI__0Op:K(BLp>aIM~>endstream +endobj +16 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1729 +>> +stream +Gb"/'968iG&AIa;m,V%lD'6IsK"c>s1i_Qm6-m\1:rUjHX,"O/+ChG*;Toh/U\H)=(I/%kN"r+hRqbKk8B60HRAAC1Kc[eY3@K%0Ob_(rWiJL,\"P1a4uD+>kBI`u6ptM]rp&D;b=`^jnh17Tol@/6#f$0jYF13LZ6Z<%?`G^\1GbpL9lJhP$G;&!^Q6[LUGq'p;B/7NouKfR*5`mj!'g(T'cjee[aV>FT`f_C=X[*#q+)BUZSJ9a6*7CK&kcBr^k]bV*B'`7^UCQX#_Fii!=bY>#*=nDCCS3q8Z"P=8e(+==,FiNs*+'OU_U;,)_:&MD+*k<';82d/`u'pJ_08Up&;7DL-7WRMS!qV0mu_BQssP&TWK#%!$C9Jbg1Gu9P!iXib?_>6@PJO,BlK>W*L0Pk<1,P?qm[upr;oV0,+n)Sf!%q?5t)D$LNdC3R3gEd-Y#jmono'hGFH"AZE*n\_"2[h*PGab55Vn@@j!?/YP&k.;p,'R-O>6re=K5qo/G;8,WMpgrMEGf*67%Vg&j>kY""t#o0T8lcW>]<9:lZV7P_\e?PP;jc:t>@!:`=%2Br7X\3neU>eRK#0oQ<cjDT1g63/pMR)+V%A&dciR(kFCV]Pg$<$oB;JF7TE1>HVdr6j6_2i7\s72(^*+(DjY@$WAl@;qPK2j5b>UlAQiZ%LujYR@fQr,;?U8K$qFI2*1Br_^AAf+rf\K10p:nQZ-A^l\/\)10gB0_io^_3&j*RKfQ3;nm9Ab@S!,l'Hb\2M^2Kfc0ojo'.Q-b+1tUmL01N=gmW-3X:2\p$d&niKeEZ`Ca[-JZl67Ga_o#:qtSP`D(lrNhi2ZfRF,C>H0Q#0ra.":cGaOUoU1b3V:4>\ZRAR&V6(k#.;.L*<%L\Q"5:'erMH10Sl?4W#H)bl*/AF"a5^1-4.C0putLlna"]aV"0^3:b/q3[]G/T/1cOl(rrM5\U^2+?iucSe@\WqAdhl_6XT*qla[2qd-A39D!q3*16_1A#F2E%F\:;m[N5mta%GC>RP.<$0nu\.uU?GW*EP$u72tt8AK/3*kBl1J8VOJ4"Ja5rkmdG:@lfmG6XWm!$mQqTEn2jA6l8pBgkBJDr>PJq6=3\%soAVrZSn"AT6Lc.ikPob0HPX+t8X9C&^tTee-LI/rBaKjG:"&IIq[:I,ldnRdmXKt1au[%7jF99]oQngJUe6sq2LknGJ#`<@$3~>endstream +endobj +17 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1412 +>> +stream +Gat=*gN)%,&:N/3lsqG!Ei+Qtn-2'&DX0:(A8F2*m2*H$'L^=Q!=#@$\6n+s8DS)RmaRCGP/s3*!Dha_YZR0$qRRK8C="JbF)0cKs0&=0OWMLgFC#VuZ1$f)oW@\=l>8ik`(f`9&]"\%K?VDW4Hu=mp^KW5[Gme-%;E4]&HJUJie&j-;.A*CkFX;0W!i#u;I;lp/n0>qE)PP[krcd#q"8k"Ke;?-SN@7CN+PMGD$e)](8m'CsAI7=8&E.dPch\E>!#"Jkln^!6VhY"q)r=/cSrBq8#(r:8W3)I9M:Xg7[V;gA(B7GSg=bG[oo>Z=o@7Hbf4n$spT5U,X+CmhI]oiIk\XiGr"%j>GnW0n)q>n;dG?R$E7scKI#[/]#/1=F?fPAFSH)C_L(A5#L<=7\IF9Yh/8"4uf8mGEMr%^j'pQ:DKi7E>BLI8\V8s1S(j]b]2uE0)h;l46.7A8kp8'8TQtFXa/AVD>$>6fSc=1[*)$]g_)sQ<)Kmo(Um9C;@G;-(`AU-#N8>&:q'dUO1KJP\td#3YaT"+s0\CNg&[$]>u^D\06Q*@Z$je2-ea2s'\_Or=:gj];eG`T9'.Me0N)98.815:RJ%)9DofBY?PShkW?l1RaRIA,Db][FkaosrkGh3"$/l:89!Id-nCO'EP$O>DTd9DN*eJ[IQ\;X>S*>?A>2?Dm;PbJ'^d8je>qa^+lTlh2r7OiW1:dUdT%]-'7Xn'o29ZAVYf&V"'J]XWc;j]#?">/?H_Q*1cn[5-=+Y7Y9^k7;O0*Ont=I5+U8mPZQD?4K(?K:S=!Rpc4e`iuI&K(ER,O`Gq~>endstream +endobj +xref +0 18 +0000000000 65535 f +0000000061 00000 n +0000000132 00000 n +0000000239 00000 n +0000000351 00000 n +0000000456 00000 n +0000000661 00000 n +0000000738 00000 n +0000000821 00000 n +0000001026 00000 n +0000001231 00000 n +0000001437 00000 n +0000001507 00000 n +0000001802 00000 n +0000001881 00000 n +0000003652 00000 n +0000005495 00000 n +0000007316 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (opensource) + +/Info 12 0 R +/Root 11 0 R +/Size 18 +>> +startxref +8820 +%%EOF diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesstof.md b/v2-klasB/Les02-OpenCode/Les02-Lesstof.md new file mode 100644 index 0000000..69bd53b --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Lesstof.md @@ -0,0 +1,619 @@ +# Les 2 — OpenCode Pro +## Lesstof — Rules, Worktrees & Scroll Animations + +**Vak:** AI-Assisted Development +**Opleiding:** NOVI Hogeschool Utrecht +**Onderwerp:** OpenCode Desktop professioneel inzetten met regels, config en parallel worktrees + +--- + +## Inhoudsopgave + +1. [OpenCode in 2026](#1-opencode-in-2026) +2. [TUI vs Desktop — welke kies je?](#2-tui-vs-desktop) +3. [Plan/Build/@explore + /init](#3-planbuildexplore--init) +4. [`AGENTS.md` — het officiële regelbestand](#4-agentsmd--het-officiele-regelbestand) +5. [`opencode.json` — config & permissies](#5-opencodejson--config--permissies) +6. [Plugins & `opencode-worktree`](#6-plugins--opencode-worktree) +7. [De scroll-animatie stack (Next.js 16)](#7-de-scroll-animatie-stack) +8. [Project setup van A tot Z](#8-project-setup-van-a-tot-z) +9. [Workflow patterns](#9-workflow-patterns) +10. [Bronnen](#10-bronnen) + +--- + +## 1. OpenCode in 2026 + +OpenCode is een open-source AI coding-IDE. 60.000+ GitHub stars. Gemaakt door SST. Provider-agnostisch — werkt met OpenAI, Anthropic, Google, Groq, lokale modellen via Ollama, en meer. + +Vergelijkbaar concept als Cursor of Claude Code, maar: +- **Open source** — geen vendor lock-in +- **Provider-keuze** — niet vast aan één AI-bedrijf +- **Script-vriendelijk** — perfect voor automation, CI/CD, plugins +- **Twee smaken:** Desktop (UI) en TUI (terminal) + +### Desktop App (vandaag onze keuze) + +Native app voor macOS, Windows, Linux. Download via https://opencode.ai/download. + +Layout (globaal): +- **File tree** links — bestanden + folders, right-click voor acties +- **Diff viewer** midden — visueel groen/rood met klik-accept/reject +- **Chat panel** onder — prompts naar de agent +- **Sessions sidebar** links — meerdere parallelle sessies (voor worktrees!) +- **Ingebouwde terminal** — onderaan, optioneel + +### TUI (terminal) + +```bash +npm i -g opencode-ai@latest +opencode +``` + +Zelfde engine, zelfde commando's, zelfde plugins. Goed voor SSH, scripts, CI/CD. + +--- + +## 2. TUI vs Desktop — Welke kies je? + +| Aspect | TUI | Desktop | +|--------|-----|---------| +| Leercurve | Steiler — alles via keyboard | Vlakker — visueel | +| Diff review | Plain text inline | Visuele diff-viewer | +| Files browsen | Via @-references | File tree zichtbaar | +| Sessies parallel | Tmux / aparte terminals | Sessions sidebar | +| SSH / headless servers | ✅ | ❌ (X-server nodig) | +| Worktree plugin auto-spawn | ✅ (spawnt terminal) | ❌ (handmatige tab) | +| Snel typen / muscle memory | ✅ | Ok, maar trager | +| Scripted workflows | ✅ | Beperkt | + +### Wanneer wel TUI? + +- **SSH-only servers** — geen X11 nodig +- **CI/CD pipelines** — scripted invocaties +- **Tmux fans** — past in je tmux setup +- **Worktree power-users** — auto-terminal-spawn van de plugin + +### Onze keuze voor deze les en eindopdracht: Desktop + +Sneller te leren, visueler, beter voor parallel-werken via Sessions sidebar. + +--- + +## 3. Plan/Build/@explore + /init + +### Plan vs Build mode + +OpenCode heeft twee modes — Tab om te wisselen. + +**Plan mode** is read-only. Tools: `read`, `grep`, `glob`, `webfetch`. Geen edits, geen shell commands. Goed voor brainstorm / verkenning. + +**Build mode** is default. Alle tools aan. Schrijft code, voert commands uit. + +Best practice: begin **altijd** in plan-mode. Laat het uitleggen wat het van plan is. Geef feedback. Pas dan Tab → build. + +### Sub-agents + +Drie ingebouwde sub-agents: + +| Naam | Wat | Wanneer | +|------|-----|---------| +| `@explore` | Read-only verkenning | "Waar staat X?" "Wat is dit?" | +| `@general` | Brede taak-agent | Default voor de meeste taken | +| `@scout` | Gerichte zoekopdracht | "Vind alle plaatsen waar..." | + +Manuele aanroep: `@explore vind alle authenticatie code`. Primaire agent kan ze ook autonoom inschakelen. + +### `/init` commando + +In de chat: `/init`. OpenCode scant je repo, stelt vragen, en genereert/update je `AGENTS.md`. + +Doe dit **als eerste actie** in elk nieuw project. Daarna kun je het bestand uitbreiden met jouw specifieke regels. + +### Belangrijke keybinds (Desktop én TUI) + +| Shortcut | Actie | +|----------|-------| +| `Tab` | Wissel plan ↔ build | +| `Enter` | Submit | +| `Esc` | Cancel | +| `Ctrl+X h` | Help (alle bindings) | +| `Ctrl+D` | Exit (TUI) | + +Leader-key voor commando-specifieke bindings: `Ctrl+X`. Customizen via `~/.config/opencode/tui.json` of in Desktop settings. + +--- + +## 4. `AGENTS.md` — Het officiële regelbestand + +`AGENTS.md` is markdown in je project-root die OpenCode automatisch in elke context laadt. Officieel format. Expliciet vergeleken in de docs met Cursor's `.cursorrules`. + +### Twee locaties + +| Pad | Wanneer | +|-----|---------| +| `./AGENTS.md` | Per project (in repo, geversioneerd) | +| `~/.config/opencode/AGENTS.md` | Globaal voor alle projecten | + +### Externe regels referencen + +```markdown +# Stack rules +@rules/styling.md +@rules/security.md +``` + +OpenCode laadt deze lazy via z'n Read-tool — pas wanneer relevant. + +### Voorbeeld voor scroll-animatie project + +```markdown +# Project Rules + +## Why this stack +High-end scroll storytelling site (Awwwards/FWA niveau). +GSAP + Lenis = pro standaard — Apple, Stripe, OpenAI, Active Theory, +Locomotive, Darkroom Engineering. Framer Motion is voor app UI, niet voor +scroll storytelling. Kies GSAP voor timing-precisie + GPU-perf. + +## Stack +- Next.js 16 (App Router, TypeScript, Turbopack) +- TailwindCSS voor styling +- GSAP 3.15+ (incl. ScrollTrigger, SplitText) met `@gsap/react` +- Lenis 1.3+ (`lenis/react`) voor smooth scroll + +## Hard rules +- Geen Framer Motion, react-spring, AOS +- Animaties altijd in Client Components (`"use client"`) +- ScrollTrigger registratie 1x per file +- Animatie-code altijd binnen `useGSAP(() => {}, { scope: ref })` +- Nooit `useEffect` voor GSAP code +- Lenis sync: `gsap.ticker.add` met `autoRaf: false` +- **Async params (Next 16):** altijd `await params` en `await searchParams` + +## Patterns +- Eén `` wrapper in `app/layout.tsx` +- Refs i.p.v. globale selectors; scope animaties +- Tailwind voor layout/typografie; GSAP voor transform/opacity +- Geen `transition-*` Tailwind classes op geanimeerde elementen + +## Done = +- Geen hydration warnings +- Werkt in React Strict Mode +- `ScrollTrigger.refresh()` na dynamische content +``` + +**De `## Why this stack` sectie is cruciaal.** Hiermee snapt de agent *waarom* +we deze keuzes maken — niet alleen *wat*. Resultaat: hij stelt op eigen +initiatief geen Framer Motion meer voor, want hij begrijpt dat we voor scroll +storytelling werken. + +### Tips voor goede AGENTS.md + +- **Concreet, niet vaag.** "Gebruik clean code" werkt niet. "useGSAP alleen, geen useEffect" wel. +- **Versie-pinning** voor libraries die snel veranderen. +- **Definitie van klaar** — checkpoints die de agent zelf kan toetsen. +- **Anti-patterns expliciet noemen** — "Geen Framer Motion" voorkomt veel discussie. + +--- + +## 5. `opencode.json` — Config & Permissies + +### Locaties + +| Pad | Scope | +|-----|-------| +| `./opencode.json` | Per project | +| `~/.config/opencode/opencode.json` | Globaal | + +### Basis voorbeeld + +```json +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/gpt-4o-mini" +} +``` + +Model formaat: `provider/model-id`. Voorbeelden: +- `openai/gpt-4o-mini` +- `openai/gpt-4o` +- `anthropic/claude-sonnet-4` +- `groq/llama-3.3-70b-versatile` + +### Permissies + +```json +{ + "$schema": "https://opencode.ai/config.json", + "permission": { + "bash": { + "*": "ask", + "git *": "allow", + "rm *": "deny" + }, + "edit": { + "*": "deny", + "app/**/*.tsx": "allow", + "components/**": "allow" + } + } +} +``` + +Drie levels: `allow`, `ask`, `deny`. Glob patterns voor bash commands en file paths. + +### Plugin syntax (npm) + +```json +{ + "plugin": [ + "@anthropic/some-plugin@1.0.0" + ] +} +``` + +OpenCode installeert plugins automatisch via Bun in `~/.cache/opencode/node_modules/` bij het opstarten. + +### MCP servers (optioneel) + +```json +{ + "mcp": { + "context7": { + "type": "local", + "command": "npx", + "args": ["-y", "@upstash/context7-mcp"] + } + } +} +``` + +Voor up-to-date library docs binnen je AI-context. Niet vereist voor deze les. + +--- + +## 6. Plugins & `opencode-worktree` + +### Wat is `opencode-worktree`? + +Plugin van **kdcokenny** (community maker, ook achter `ocx` en `opencode-workspace`). + +**Wat doet het?** +Geeft de agent twee nieuwe tools: +- `worktree_create` — maakt nieuwe branch + worktree-folder +- `worktree_delete` — commit changes + cleanup + +**Worktree-locatie:** +`~/.local/share/opencode/worktree///` + +### Installatie via `ocx` + +```bash +# Eenmalig: ocx installeren +curl -fsSL https://kdco.dev/ocx/install.sh | sh + +# Plugin toevoegen +ocx add kdco/worktree --from https://registry.kdco.dev +``` + +Restart OpenCode na install. + +### Verschil Desktop vs TUI + +| Actie | TUI | Desktop | +|-------|-----|---------| +| `worktree_create` aanroepen | ✅ | ✅ | +| `worktree_delete` aanroepen | ✅ | ✅ | +| **Auto nieuw terminal venster** | ✅ | ❌ | +| Nieuwe context openen | Auto | Handmatig (Sessions sidebar) | + +In Desktop maakt de plugin de worktree, daarna open je hem zelf in een nieuwe Sessions tab. + +### Workflow in Desktop + +1. In je hoofd-Session: `Maak een worktree voor feature-hero.` +2. Plugin maakt worktree-folder +3. Klik **+ New Session** in sidebar +4. **Open Folder** → navigate naar de worktree-folder +5. Nu heb je 2 sessies parallel in sidebar — main + feature-hero +6. Klik tussen sessies om te wisselen + +### Handmatig alternatief (zonder plugin) + +```bash +git worktree add ../mijn-repo-hero -b feature-hero +``` + +Dan handmatig nieuwe Session openen op die folder. + +### Best practices + +- Eén opencode-instance per worktree-directory (gouden regel) +- Niet wisselen tussen sessies van verschillende worktrees binnen één session-context (cwd-bug) +- Branch name = folder name (makkelijker zoeken in `git worktree list`) +- Cleanup na merge — niet stapelen + +--- + +## 7. De scroll-animatie stack + +### Next.js 16 (mei 2026) + +Uitgekomen oktober 2025. **Breaking changes ten opzichte van 15:** + +- **Async API's verplicht:** `cookies()`, `headers()`, `params`, `searchParams` zijn nu allemaal async. Altijd `await`: + ```tsx + // app/products/[id]/page.tsx + export default async function Page({ params }: { params: Promise<{ id: string }> }) { + const { id } = await params; // VERPLICHT in 16 + return
{id}
; + } + ``` +- **Turbopack is default** voor `dev` en `build`. Webpack alleen via opt-in. +- **`middleware.ts` → `proxy.ts`** met nieuwe Node.js runtime. +- **`revalidateTag` vereist 2e argument** (cacheLife profile). +- **Node.js 20.9+** vereist (18 is dropped). + +### Waarom GSAP + Lenis? + +Dit is **dé** stack voor scroll storytelling op pro-niveau. Niet "een" optie — de standaard. + +**Wie gebruikt dit?** +- **Apple** — product pages met scroll-revealed content +- **Stripe** — homepage en product pages +- **OpenAI** marketing pages +- **Awwwards / FWA winnaars** — vrijwel altijd GSAP +- Pro studios: **Active Theory**, **Locomotive**, **Resn**, **Darkroom Engineering** (makers van Lenis) + +**Waarom niet Framer Motion?** +- Framer Motion is uitstekend voor **app UI** — modals, page transitions, micro-interacties +- Maar voor **scroll storytelling** (jouw eindopdracht): GSAP heeft betere timing-precisie en GPU-optimalisaties +- Daarom: Framer Motion voor app-werk, GSAP voor scroll-werk + +**Andere voordelen:** +- **GSAP sinds januari 2025: 100% gratis** onder de "no charge" license — incl. ScrollTrigger, SplitText, MorphSVG (vroeger premium) +- **Lenis** integreert naadloos met ScrollTrigger via één RAF sync +- Beide zijn **populair op v0 en Cursor** — AI's kennen de patterns goed + +Schrijf dit op in je `AGENTS.md` (sectie `## Why this stack`). Dan kiest je AI agent dezelfde keuzes om dezelfde redenen. + +### Packages + +```bash +npm install gsap @gsap/react lenis +``` + +**Belangrijk:** +- Pakket heet **`lenis`** (niet meer `@studio-freight/lenis`) +- `@gsap/react` geeft je de officiele `useGSAP` hook + +### De `useGSAP` hook + +Drop-in vervanger voor `useEffect`/`useLayoutEffect` voor GSAP code. Doet automatisch cleanup via `gsap.context()`. Werkt veilig met React Strict Mode en SSR. + +```tsx +"use client"; +import { useRef } from "react"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import { useGSAP } from "@gsap/react"; + +gsap.registerPlugin(useGSAP, ScrollTrigger); + +export default function FadeIn() { + const ref = useRef(null); + + useGSAP(() => { + gsap.from(".fade-item", { + opacity: 0, + y: 60, + duration: 1, + stagger: 0.15, + scrollTrigger: { + trigger: ref.current, + start: "top 80%", + }, + }); + }, { scope: ref }); + + return ( +
+

Hello scroll

+

Ik fade in.

+
+ ); +} +``` + +### Lenis sync met ScrollTrigger + +Zonder sync lopen GSAP en Lenis hun eigen requestAnimationFrame — animaties haperen. Officieel patroon: `autoRaf: false` op Lenis, GSAP's ticker drijft Lenis aan. + +```tsx +// components/SmoothScroll.tsx +"use client"; +import { ReactLenis, type LenisRef } from "lenis/react"; +import { useEffect, useRef } from "react"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; + +gsap.registerPlugin(ScrollTrigger); + +export default function SmoothScroll({ children }: { children: React.ReactNode }) { + const lenisRef = useRef(null); + + useEffect(() => { + function update(time: number) { + lenisRef.current?.lenis?.raf(time * 1000); + } + gsap.ticker.add(update); + gsap.ticker.lagSmoothing(0); + return () => gsap.ticker.remove(update); + }, []); + + return ( + + {children} + + ); +} +``` + +In `app/layout.tsx`: + +```tsx +import SmoothScroll from "@/components/SmoothScroll"; +import "./globals.css"; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + + ); +} +``` + +--- + +## 8. Project setup van A tot Z + +### Stap 1 — Next.js 16 project aanmaken + +```bash +npx create-next-app@latest scroll-site \ + --typescript --tailwind --app --eslint --no-src-dir --turbopack +cd scroll-site +``` + +### Stap 2 — Animation dependencies + +```bash +npm install gsap @gsap/react lenis +``` + +### Stap 3 — Git init + first commit + +```bash +git init +git add . +git commit -m "init: next 16 + gsap + lenis" +``` + +### Stap 4 — `AGENTS.md` toevoegen + +Plak het voorbeeld uit sectie 4 in `AGENTS.md` in root. Pas aan op jouw project. + +### Stap 5 — `opencode.json` toevoegen + +```json +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/gpt-4o-mini", + "permission": { + "bash": { + "*": "ask", + "git *": "allow", + "npm *": "allow", + "rm *": "deny" + } + } +} +``` + +### Stap 6 — OpenCode Desktop starten + +Open OpenCode Desktop → File → Open Folder → `scroll-site`. + +In chat: `/init` om je AGENTS.md te valideren. + +### Stap 7 — SmoothScroll wrapper bouwen + +Vraag in chat: +> Bouw een SmoothScroll wrapper component volgens onze AGENTS.md. Plaats hem in `components/SmoothScroll.tsx` en wrap children in `app/layout.tsx`. + +Plan-mode → review → Tab → Build-mode. + +### Stap 8 — Eerste worktree + +> Maak een worktree voor feature-hero en bouw daar een hero-sectie met een SplitText-reveal op scroll. + +Plugin maakt worktree → open nieuwe Sessions tab op die folder → agent bouwt. + +--- + +## 9. Workflow patterns + +### Pattern 1 — Plan eerst, dan Build + +Begin altijd in **plan-mode**. Laat de agent uitleggen wat het van plan is. Geef feedback. Pas dan Tab → build. + +### Pattern 2 — Eén worktree per feature + +Werk niet alles in main. Een feature = een worktree = een branch. Makkelijk parallel + makkelijk reviewen via PR. + +### Pattern 3 — `AGENTS.md` evolueert + +Loop je tegen iets aan dat steeds verkeerd gaat? Voeg toe aan AGENTS.md. Het is een levend document. + +### Pattern 4 — `@explore` voor verkenning + +Nieuwe codebase of vergeten waar iets staat? `@explore` eerst, dan pas hoofd-agent inzetten. Spaart tokens en geeft jou betere context. + +### Pattern 5 — Permissions matchen je risico + +Beginnersproject: `bash.*` op `ask`. Productie-werk: alleen specifieke folders editable. Schrijf permissions naar het werk dat je doet. + +### Pattern 6 — Sessions sidebar = jouw parallel werk + +Eén Session = orchestrator (in main). Worktrees met aparte Sessions = workers. Orchestrator stuurt, workers bouwen. Klik tussen tabs. + +--- + +## 10. Bronnen + +### OpenCode officieel +- Hoofdsite: https://opencode.ai +- Download (Desktop): https://opencode.ai/download +- Docs: https://opencode.ai/docs/ +- Rules (`AGENTS.md`): https://opencode.ai/docs/rules/ +- Config: https://opencode.ai/docs/config/ +- Plugins: https://opencode.ai/docs/plugins/ +- Agents (sub-agents): https://opencode.ai/docs/agents/ +- Modes: https://opencode.ai/docs/modes/ +- Commands: https://opencode.ai/docs/commands/ +- Permissions: https://opencode.ai/docs/permissions/ +- GitHub: https://github.com/sst/opencode +- DeepWiki Desktop apps: https://deepwiki.com/sst/opencode/6.7-desktop-applications + +### Plugin ecosystem +- `opencode-worktree`: https://github.com/kdcokenny/opencode-worktree +- `ocx` extension manager: https://github.com/kdcokenny/ocx +- `opencode-workspace` (uitgebreidere variant): https://github.com/kdcokenny/opencode-workspace + +### Next.js 16 +- Blog post: https://nextjs.org/blog/next-16 +- Upgrade guide: https://nextjs.org/docs/app/guides/upgrading/version-16 +- create-next-app CLI: https://nextjs.org/docs/app/api-reference/cli/create-next-app + +### GSAP +- Docs v3: https://gsap.com/docs/v3/ +- React guide (`useGSAP`): https://gsap.com/resources/React/ +- License (sinds 2025 gratis): https://gsap.com/standard-license +- ScrollTrigger: https://gsap.com/docs/v3/Plugins/ScrollTrigger/ +- SplitText: https://gsap.com/docs/v3/Plugins/SplitText/ + +### Lenis +- GitHub: https://github.com/darkroomengineering/lenis +- React README: https://github.com/darkroomengineering/lenis/blob/main/packages/react/README.md +- Homepage: https://lenis.darkroom.engineering/ + +### Combinaties / patterns +- GSAP forum: Next 15/16 best practices: https://gsap.com/community/forums/topic/43831-what-are-the-best-practices-for-using-gsap-with-next-15-clientserver-components/ +- Tutorial: Next.js + Lenis + GSAP: https://devdreaming.com/blogs/nextjs-smooth-scrolling-with-lenis-gsap + +### Volgende les preview +Les 3: **Introductie Cursor**. De commerciele tegenhanger van OpenCode. Cursor rules vs AGENTS.md, Composer mode, Tab-completion, @-references. Wanneer kies je welke tool? diff --git a/v2-klasB/Les02-OpenCode/Les02-Lesstof.pdf b/v2-klasB/Les02-OpenCode/Les02-Lesstof.pdf new file mode 100644 index 0000000..5808844 --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Lesstof.pdf @@ -0,0 +1,277 @@ +%PDF-1.4 +% ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 5 0 R /F4 8 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 20 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +6 0 obj +<< +/Contents 21 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/Contents 22 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +8 0 obj +<< +/BaseFont /Symbol /Name /F4 /Subtype /Type1 /Type /Font +>> +endobj +9 0 obj +<< +/Contents 23 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +10 0 obj +<< +/Contents 24 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +11 0 obj +<< +/Contents 25 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +12 0 obj +<< +/Contents 26 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +13 0 obj +<< +/Contents 27 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +14 0 obj +<< +/Contents 28 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +15 0 obj +<< +/Contents 29 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +16 0 obj +<< +/Contents 30 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 19 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +17 0 obj +<< +/PageMode /UseNone /Pages 19 0 R /Type /Catalog +>> +endobj +18 0 obj +<< +/Author (NOVI Hogeschool Utrecht) /CreationDate (D:20260518122502+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260518122502+00'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (Les 2 Lesstof) /Trapped /False +>> +endobj +19 0 obj +<< +/Count 11 /Kids [ 4 0 R 6 0 R 7 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R + 16 0 R ] /Type /Pages +>> +endobj +20 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1817 +>> +stream +Gatm<>B?8n'Ro4HSm7,J,K\2XIUt+4EWF.Nrq\bZ$okC1f'>;J,GB2S`;.+l_LUX$f";:%9[u\N!*dehuZ5=OjYrK'l[0_TB81]A]G]AEkBVJ8gRT9&n7d)Ld[cD,gc.LKgFTtaCt?do/e5SCA:82:aES/juCZ7=^7gBMR:lPD&5j94\6V%(HUAPb4s\(eBOeB`2%Hu&95quoj5)OV.p5j&8JnhE$;h:HL1nF7GIHi:B(4;%PC'nm:bT8EDhH?BX=SMdOO,WDW(C"aLl.H)fK+.&WILD]gEYIYY1(V5#4$6K_9=)]2@BR#"p4B%Vpr=XhYcbY/KVBW[bRE(>Fm-Y4:_'Cpe!LhM(W=]uAERAK9lV##!cpY.I[5`&fE9P6V/m(3s?[b.[CGgL$=-4#pN@AdgJ.K$42B5N]uF0[AJU!u7&p>'6h1.+i.PqDs)<-Qi#B&sEDEiFPGtr2N<%C=?1,[e&@Q$t?pV6PB3D[$CI(BC1;AD]NWD-[jgF^"mF6$U2Bk[q:.=jZu`6+XlH$-q@XjiM#JC/Vd2m]8HUlU>1UO56sG-"J%r9h-Y\la5:ds3ILood&7BlM@/[D85-l1Za]^g16m/L!2V$qnUV^TsM2Y!i6'):lR8^U:)F2pY;@`kuJL)urk'L[_&'7g7o*kZ']P*Ca;N!O2:IQIgHSe[1:!47)QEQeZT7a&hnc5Z@h.mOKs$]lb^B6C3Tj4N&,-6>9*fB*YBQBM1/^e=P+Jrl$QGFp[aWcR=(IMd(sNj)b9grNMgR2TrP#ZbZGSrgWN;~>endstream +endobj +21 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1814 +>> +stream +Gb!;d>>s99'Roe[3;^(i9nXhMj?>.'D932bg*,k/m-l2=`JDT5C-,k5^OD71fJ-*nd+Mh387Gf3Nm@ImSXV)'peRC:X8r$:$A\(!:^5B/d#`o74bIrWn!cgLb`XX]&o^'+)3?[oIo,:!JLDjbb#/T%:C'`_#WX3$V]6Q!9+m[Y.:N50UHk!p:O!.udjXAKcMW42N8`2.M;gMnEW/ElmaOm$][75>Cd0ji,][H!Y^r1f#\bY6AfElHT9CDe'ASHMnb*%&Ka2$+`_^n2,40)/VEL4Vh[fNjN:G.k+e_KOL*KRi9]l"XGX=Ee:/jHBicAf:^$L9Xch%Qd!uWT@SSO;G?+R`6pO`%kS4Q/#($bnsQSH.Q@i.JM`fr8DumSVFa2i>ZqKG)Z9NEQS?U-o^&rDg%'Lp4^j/jr]Wdq/IWD-#?Y3a@J]tV!T2uk=]NLm7`*e^jHptN!=87pG?iL#OU\,\aeEFDqrl]h:3r2=KMWR%9U7a=*@SU''TjnSLKb`-R\?g`'3UbrrtZcqK-i`<@u@N^^"!JZ)D)CD$_!p;psoWn>Wd123[-(jMk6.64[f;IOe']:rjR1l:7$M*1?Z7ZqH[Di.JGJQ?XtJ[h3;c&6_DO)JY8sXd+EXX[OC&JR=7O+=<;q)MG9OGZ@dX2#?UE(+[>M!BC5"UO7El;LOph3#a:-2n(m*oI@(/U3?,g9'GWOfB;mr`U91;8X&4A-.2T6MS@ZF[4AHrZP>Pe[:L)[/8Ebg>'/+h+Eq'p9/As5[*9@rAaWQQ6bo>?ibkkG>25*]>(IXV$,8$1,3KsiB:LS=+OI'aEG2Z5MTn"A.qB^'VC$SrG?#A_%HBNVr[G0>[,DZ\7(GSpEBFQG`N%!Za%2Jl\T_5Y:rOXA\8uWZndQ>Fo*\G,m+>U")hRb2$Q_/dSf2Yof1DM>AW:b<\+!-n%XDFGG=5@,cARIB*XMbbo?iq,&:2\UstKmh/[S]?7HWlo_c#?!W-m:/nb?;;YS9Rl*Af>bga\!;8r#nboQ9PbGC=;kG$>qui$_j.N`Kg_S+J[I+ZTQ9Y/6,[0t>iXCEh2G*0rUrK%IK@Q1@WHa*=1>]]?-:o/-oC;+>%rXehGji%_k9M2`ZY6[1?21%oBtm0N(n$5!(3=iBAASiAgG[ZcB01,Lim=j>>'=U@-gQIs!Tq;``GkXnooj_Dq;Ep%P,H;\Mj_10Wcq\hkH"a6E2j-09P?nG>$0Qrqr`5E$?-BY>Hu"ALe[:n(>I-KKgS+q72B1WDld/Rn1D0OT~>endstream +endobj +22 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1690 +>> +stream +GauHK>BcPr&:WeDbfcTAp=+sF,UAt)HeE;ce'lL[:%oea(s?pZ+4isY+qOck4>)K&m%:*q=+jJF#LBntV$(3Vi56qJPDR!P).##AR"afIKd[a0kcoQuA9n=5K,=o$-+Q@aOSQTNr>_MjBLe(Kp]*8;[hZuKn[@5@.=/,oASmU[Pce%UBo3(1QUGe.^+JoS>Y\.7;6J@2p9m?A-.US6oX$)Ok+QiT=fj@`]>WK!EG[SiVbrUCD8+S_A8.3dppatgl8>$EY:GJEsP2-sLS`3a(KX7Z6S&A#roJ?H-PSf[8[gfr\.GX9hY\O&9q,cV.col%,r'0!FgF1U]0C\W:T\j6Ykd;jbSE!rr8[&IEZ0T@iMrUV0C\&t@C+e'j2]ZQ;UcCfDbcKU0@F5ac"2`Y`WDSg<$V?)::N,P_TS(q8ijds]jgo`HApK\kK?@Si03oL`h)]UGG\p+Ys5>_Fq(c*6P0r4RhreullR+iEIW>Ab"VaO:Csa>E\E_qS,hqE8^6T!-pt0@WJ"P>`Xe56sWRn1!)tCCn.bb0d1KGSJ=daS?4QQ$NFSM4#mAkDm@tU@iX3o^?b'M[T>^_B5PeduTdk,IHmSQ*LA,G)U<.dO-a24qFKFof?-0*:TL,_m7]15:A/<;9"jcO"l'FOT-dap_p2SB#s^m,K"DfDbE%dlZb_I=$"q&UQ[A(8Irg*5N&m8U#QMX_t^BA*^&,Ah\FE!)^R6g">UZK45VYl'969+g/1gieC#JsO~>endstream +endobj +23 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1726 +>> +stream +Gatm;bBDVu&Dcq.As6^`[:'%<5qneXDA"<[3I+b%]#Y)M+Ghuu$45C&^V0g>+\J&poX'oX'&[0[%WLSS"r';G?df)7C'#3T(bcG`3A<`%9]!Y08an;S>k1ub5XIoWKEFe4pBU@8-m=aA-9P_0BM;AV5U]*d8ec5L(WS(]g;*W#^js@d`_$5./cG0MT-[WZ6Js[7Q]#^2b:4jdYkbVTB`lB(3[mS%Mo>VR/S(j;'(^k_Bs1%t%VJHM#t#Wk;1*T&K+RcGKgH+Q7DL.r4q;XZ^gf^1:np%R3[m?s/GXN;l=JCOR)90+(iu^3WmQTZD+,JF"$^DN$\RK4WsijZ`(R?bCF/n_BBtFmKs9(o)a7`^=.42cPq1-Ph`fKs5`iGOHdTdd'[7J#m!r'!VRR];>NpY02a(FdC?+IPH>Tnd;r*1L+I%AreZ5.QT9CgC\C@Lh5PhNqU?YKSKU&%a"kNeD8G0+#4O'P%;$G?40Qg)0*2e\%*^RmEs@QVXRMZ[dkc5&e%)aJP/+I[-(%hI)gph?S#+5JS?FVhgfIJbdopB1-@m)EVK_D!hUHbH/@0Be,L/&jaBh6qq"c-9hV1M$o"3g%-kQ^&['$AHQJoifajI;3"$!%kI;%iDYG'm&)/`-%n,j0W#TrMsJ@-X^<&fsQVQmA*F_\7)6OU0B*I#(>^0LL*e%*_@B7Nc.N1tA`/-9at[M[lP&L05m"ieAG2fl0,8S"g?(e4^/'D-Y)Pb4G4+01ToSF"ik$C_g"U-%BWi\)D/Ep^Fg8\[8f0jl_ml'2U(=V14QOGiH\5^/)H2]MGG?KF$nLo=ESB%",LAL9:ONt.9r^C/[9IqI7I(j1lU'Tlk9kdilVhY4T:bIOJc/9p@RNtZ>3AC0G1'I"_5`BImTM@e_:Pf8dAHiM38s_N?oDt=d*"fGOqq>e>(cJ'm.Mo@:"[Wq7!rk-Cq#7J#XOZiQ+I$BjLRS=QY8MR`\e@l+g-4g9DV3.kiULTgN?X=+,5e`@O*X&EJYC2$;I*J9F_,PmSREKTa0jV5oF14?4i9"?mDXB/e&W(7]tAgiDK8+%4mDmLs&c9kHiNPendstream +endobj +24 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2485 +>> +stream +Gau`T>BAQ-&q8H9fGRJ_:*RLS#_JjbEi*a6>WQTYA*Gg'IXt']U.pb$8QenOm;MFp]ZQA5SE\6XQ6h,*G-T]up-o6#5Ks+].tF7)E;#"ej"Qr)'1BY&"'j,@^Q/Yeg_2:Oa/;\JJHV#/K46Q-_3ICm4CI)GO8,:HiA9KjJI`ab4HFbaBUD"+0d*'`ShGWhba#p?L>F):F50q1=3BGUI3YA&`!_B;UdHm&Iq49a=ECF$X!7m]Y33Z<"fEG,rUq;hAIDdub:HN#c5B[b]1C#,ma)f+3qC`RW-d$iIL`_)-5QmJEm7Q3o1@If5q)$'-R_8Nft'mVlYA=4%?U3,ok<=M?IiY+WKG/(k\$-UZKQ`b'[sIT=lu+Z.P>[DpU5C(o)c]N'"h:YN*7/[?qf`Ks4b*O08ZbdddnSs-CFX`+8tdG)-+:gWPYa.9T(s)O@sdCd*otpr7J6TcFm67$hFS(&O7>r1PkUR]b8%W)_:i`T#s#[a?"V/AuVpo#\#GOQ(S.`sr?+,6a/&!HGtF(CM2?RO,i.[tFl27cI8d>oOt?%e=H;>9dMd>g`5@7Hg"op4kV&0aDZa[gPF$1tB/S/>dT%],QC\l-3$JL'Ka)@!H52L>YZ?/NVkQ'X:U#4<)0>j)\);R1UjYH+'?31]eh]^Wm*OFri<^15'e+Yl8MhC'JU?lrL(g*jVjIPpDp`8MX.WCiX8,7ZP[1:iD_@f`0j438u!sUGqfq/h%CV%6D8@PZTU(,j8j!?Ze;u?Ze03B1Dd3rl9K,*D'FnT>4HjI]gH'YrM0sWGMhL\K<1/X#l'"'DT5q6FFb+X>Kp'A-7_3Tn*C%l+;2FSEMeE@"n5UZ)r+Wr"tXZ`FcGR79#klq0W3Wr1MU:jr$2'*toH1&P[E%RpK@9$Z'HR'^hdu/(cm_+kNT&b4A#^1%5lsN`Y86([oC7[_OBD%K-&.(L`#L_/-)JoJ(a7_JGL*WA2'*&f'`['jRWg6(ngYk]DKc'\6V$X.QpB60!f&s3p"CB*.V_ZW\popk[HR\LUB9J83AN\B`Zm1<2*'k'@r?1+F9IMq]dQek_PY*HUS#!`+].q+ScL-;L=;djZbLH=5"U&E^^*mq[8XPO[W'W"P0D--bh[\cSKhA0DOSR1&QT)a!0s,'IE5rc+=V3%F8H[Wgm%.GcqLLU<*$UV?+8/o5g;"Ust0&],i[3?iuc=7a=;:'Zuam<)5Mlm_UNea)8d7mP%Rur6k-5_cGQp"A[l4C"Wm2-07e\DpYLB+c>pkN(5E<%J2CYNG0aC,_EjolAq@4@1E`F^$GQ*`eSkrQP\Zhq4/NpL[i#R@>i)5?QEe?GZ[,%](@a!&mj-##?uOL?FaE!f<.LlZ8VT`1cgc4H<2q)P7G+&(I=Hkm@A^o%6m;I.LS,j7%riB7a%Ji@hqpUCNVeFN9Y`PMajXY+fIQQ!&MNF`J7Af*Ydf#$ZeD3EHQlTS7cT_jD]3fL=2Z5,;aLmgb6hHB(9dUDFh`')PC/6cA'2/Y.%72Rs-""2X.]'.d=N5QPT6*ssQ?TMVFM+jH*9&G+("XEea2``endstream +endobj +25 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1620 +>> +stream +Gau`T95iQS&AIa;%#goWK4q[]?2Bj0:"ge.h+#X<(Rh(03M\'eXFm6fTYtb:#OsP**o?I.u1SFMRr]Mh5?7o[VRK;HQ]E0F:K]QG5IMps2KPPJ$![9010aA=!pOI!!D*_m1@idG1e.?C@8BZgsYm$/8`\7Kp#$9+p#/k1iX9<"#dT/Tj(IW>)U*WZ?a8>e*Ib(J7'A[AXC:5bu6OMB^'i9T0PUWA8=hsBj?f1kh5EtK8:9VhrLHh4W97Z9D`4'GnVi2t)6Vn?p;t$%Iaa3Dh?I[(V6%m]p\5Xtk#rQeODCdW0]B=i7_QbGo$()4/lPae:9qa`0D9-OXK;SH^=JGc/dBQ>+e6b8a.aV5#I]90Lf.-l>,^rg_/.o3HkdM6r%/8F1X'>-[hT0l_IO[gro?(*3_hVRZ>^\4BYU9:8`FF^4I.n6Lo;)j+#of?7V7*uBY!E!Tuo0C0?]4/Rq;JmDC5AEf5BURbp@r+A=L'FI_Z-*@DsPF%V-P)j9^!rR_$2jdt'Ee0A8fW9dFgD490^V>V]JkpeGM]jd!Q,>\U7gJWe`I_3pXimNX"Bkt\Y>+MQ#rZe5/'I35bOX324:f<*7I?s-[cSjO+lbugK]Ii[uP/_U`pLeasYtgF$3!p_TKM$"!-aopEUOgQ!_mGRJW]uD5b`/6q/\#40;:$GU+OkAh]!&A^oJ7N'kTJ&+?p!"+#@0Fj_4'?K_$2I%g7X5tYER0ic6,Q_0Pu%3qf&_3Ud>V;3,`L3cG%-"SJB9"3LjA6c)s6;%%3`((2[(H)E<(m)OWCJDT;=i"[_8[fARn&HIpAP3A3;Fc.Qg,rmLmrT'G@aV0SDA[g-rAq45]I@D8GoS7u?\)J22q*T=$"MBlo]_96j_Db4To475#-XBKjQk*o@@cst<+]^KV[gR_3MIpqB"ZMRpHYN$,jf(IFiM/f>M>>4L=f`u-5;'g4U:^ks2Of[K?KAc)[pqnd-'kPc):M7urlc\ZU@1&o.B?D@_;5PVnk.3$dRrjeTae,VMNodpud&p;Z4eYk_ACS1?D\m\j'ZjSk0fS`FI[=%l/n0,"5j$HK.Pr)EjO[,JMo3a$6+NNGqWVnBrq?H8n-4P=ai4FGiB;lo8q[5e0NanpBFmqoJ.*6&5_]q5JC-[6'+[W'(%HqP5>4W'X14%hm,qK#97[cG*%#pXImV7pqZM;:>pf7;1`u,s;WlVB&8UGe,T52ZMNTiXP1+`2RqOn%mmNQ>[^R3a^/2&k[-:j#h/~>endstream +endobj +26 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 2009 +>> +stream +GauHK=c_;q&:W67(na.S,W./O'M0m6(296pS#2[u8RE5]'U_/9ZKOib2Z\g8p'u4lII;Zh]p;%qLNCd#'O:R_i\t$@r%`rA$OW-7E"%sE%`?ofI->rt#STmP+,Hh"047$Hci-3kE>%aE&HN7f-s%$-3H.ED;Lnn(qkU##;@VY6%L"&1.hr;`W%XCfg@#Wi(`pqY019c#IjL_miWV_Ld\3&i(RZEMXd!S1HCVLpQTbfod(=Poko=#uM9_ip0en(";]SSR\7A:MZh+1&/=U4]*l2c@X^e\BkTkWd_^cQfZ"J4SF"U!+\_Q9G>%bVNJ$[ph9]nVbWi.nN;R#SH?dhD1,5/e-ciq=Pq./`OE4&kr+LUk*3r,>:F$/BSRVGpm`rbC(ddGH%D7?m_8c1"LM.SQfDs7u;67ZG[G2D[h;0!GpW1FjC3.EB$M0ZLWtWs_`VapFm(7M(9uBp@l9lT@g'roK]cYG@1,W,o.bdnjVt.,b;0mNn_8/P4'tZi-[QZ?;E)D.4k=LtpEGX0ZodmHrjL>1p.DgL1G!bJ'9h"-i>g(JE3[$g!_TJ=ZE4D,Yl=eaXLF^toCmQ&R,)FEJfj.H4:#n-H19c;Ioa=eX*F:miq+rkK?:E@7d&\@jp;&XSWDr&ggG3j)ZYVj*XU]5!CULq-VS1g=%m:1*?b!`%@6al`)$0e0I-u9ekZ6TWF.Fl!GW%Q4b5JF6T2'O7)[=ht\5.c*bE`sHdMVi@m)*V+k4pbo)[pQ++X=ZA+O`WqP[m_f#I-(gT;)Q,DkdAg%A$X]f]@SMQN^dC[DXn;6rGcQfNAdc-+A,>BO]nksSX`?^M.g*_S#W\KLZi@+I':q&Bb#'c*;k]U!"_lU-9NH>&=.Eu^/m4BdOF2<=cDQ(S*+AilO8Lic]:"mO)O<5@%)$X&8b>=)/k7b.^CX_A@kB'@##(glGg6c`77`!N#s=E6/X(tkSR(Q$r_i,ZNr)tI48bb&77LO$`'?&KEE&=(&S-Q@Q8:M,ubJtV=2KN3`rtX[q;U!#m@5old`SdgOC#JsY%e8Mh:4bnRY4r^KL],R'.m2qreko,Wh#o]cf*(h"'Gftm@rmWP$F[iq:Q[nefFd$W.%Rs+6f:nS^RAX-.VJ\AW*<`Go)tr1N(P'fqUbDUP$S^Wp)Q[@o=.RSutuITBapf7gC`ZH4V=*'9(;0FK7T+2L15@br/2:`ok1e_38J1),]NTbL8b@-EBTt)83G^!p&bXdLq_jOIIF%NOV%=endstream +endobj +27 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1975 +>> +stream +Gb!;d>BA7Q'Roe[3%m@hd&<+TPBh^5"h$3`b*$XLqP)\YU;f-.VZD8F*r-R!Bj/Y5h4a)cQ>D!4IZYIDr3?):-3.t]E:f.kj"S.Njq%cp+5lP9qJ$W1ESkbefRhfX#85\O%uQd?K'O"%huNpGI3O%D*5RFBJH1_rI%U42.(F_D0d+2#IGk/3A`3uY+VRT^bQAp2&[c9@h,SkSqo@VC.(AA-3NYou[d=10gk'MJ8j*'=5<^2?M9JC(OqPaJ0$XDo#D:X*`1XJjLaC+`^>7BDpoh4!@d@NsDd5;^p%XKJV`P!)ja[9i]eLkE^NTH5X):tj9f-@$_Ne,.d-!W-A"%-$F!5HkN(NLI?F"+7;l%g_(3G;sSFJ8VuMJ#o:OM_'n/1=_5_1la((/fo$<:XIr=^-9=MlN>fp1^Ao:eM'$ah^WYF)X]7KM:00gpFZt7=Lah,DhA@<`;4roc&?q:mk,M*uZA$_6$C^\M*\4f*A`7S,m]DujY^orp%^ZV=8bkLY@<-P^XFseXjK[*,UQ`*),7(VlTiH@u\i_s*\WW()g-I>!R?WJ'.TqGrZ[)a=oM#\W&nHu.B."QuqBc9SP.K?tNk!e;FYX?0t]o?N;)FMZj*-e0f>(Nn.\J5b?#2a.mFNT\Z>R=CI(G[51+d[1,TV4faOHGn%*eg2($',Pr!"JR>EDkBg/^a0*:8D_h-C3r\IYp*j)-jCCdA6l$/1RAbO)`rDfK4k?l>.kFh##tOLQ@KJ'NVO1]4]BsuMkaRip].V&Jdm'X5Z+FO2.l'esb=H:-9@t`;UIeQr15>$u@@BB5%p>8GR@-*hIoMgW_D/VfO^Ut0S)TH+W<,6B_:gg""qQpZ?WRRhTTWB]_VI)(:DiEQCrUHp!fl_,4*V)e%WJcAg1E%og"YA0kX)d#i)VfYN-sFQ4)5BnfBDuLi`]ch1l0"fh(GB*.E%^lU[8<2Q.C>;0SbSLOBR.51tn(GU#CFf3WU?XDS,:M"g'fK\/XJ$3F)@DnpqQgGFIFFnpOQnIr?"J[g8t4^$i0k`spHe[!7nNlD)2Kp*!r46'uI>1W=jCX0PV2UQE(\b<1ROP9u`]/MX(;mE@ohd9qcl^`[b%?%\(aW5d-d9m5"AnCGu'fn8qS`lG]Opf=:Voam;Mr.CcT#Aa+`iYhC1TS*U4/J7/'DholE$p6+RZF0_ENueO4%?e2G>nNk(SL(gi)FMY:ha_(qoB@a,p!F3uUCo=9=_o+]3!TN3YXlOYBc!GC3Rn)fEObnJGQG+.W:ZVorTg0B[G,r(]qdlBX7=MeA#>NO6Il&CR33l=Rrf\KruEe1&'\(5eZ2.mkNCK/>qK)'\)`TnlHO0endstream +endobj +28 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1636 +>> +stream +Gb!#[gMYb*&:N/3%#dX&D.B"nO]7q+7@S1GU@"]3I9>Q!OVqA_DO$GmYirjj:m*7fbsi!NUh^p4392&#EXkTL"r+hR]-]PRDeiIG0OPmOkJ/@*Y^Q[-f>4A8E5f3nJI"V3`.:=nIA#F$"b_c3Ldqj^e\l,-dP,-(A:UGIekAGJiVZ9_J7qBZO['<_Pu0d@$_fW$W?hk9MQZddr/@Be&N-d[Rf*c5E!.*qj\^^WP_D/OFUjJi^AI4Rr*F8XPO-FIK0S,E-G($g3S3cup%!CWUePV`nA9X_=]sjpBRba/7p%uqF%u/#<$]mi+6(&?%<0*ue!B/[lMGgq(,82jQp5O_pGe$@4>eua#c]Tn2stl4E*nQc8h.ma*g6NMgTl?F6E)dT)`PY_8XNP+C'jWEP/,iAJJlHIPW)#j/dfF)$eCC/"SRQQ$1=FlJ71)M4/iIZI[27P,DusLT&ehrQSDWg''t3F;XW:0IWaYGdqMkq1T4dr0h)I.N!j$L.njWfLm^h>#j\tql@"8f3rFg'+7UX)+cB1*#PdcRntSo_BP`#u_<8=YkKoMHr&pq_AZ44uV\s6nT4I15Sa8bR&!&r^EJ<5>*oG\T/c"&/V,TUe"")W@EI3I2Z-t%p\4Sm,rn_Ftdj?,b_Y[W["0$F8#J;Vr,d%Ip8!?^aMGQn;e7/,]P/,g=NXU+dD!lp>B@9%.Xh>"eiqjPG^o"9fAR&6]12JfrWMD/QCtYVdL""N:e'oG&j'Hj?FI3'OAc&UH,ZpsElPac3I7]>+4sE0sD3LfMlRBtf;^]a_>.\40oiYG:ai2MGmI+qmA2C&BVtn\YYHrT'4RY0-_H++tfHNn(1U$LKW%OqHI'nk7S`?2o1H*B\W);J>Fj_%?'K&'QmmA,H"7(33K;-L3/XeTC1>"K%R:ks!6=FAAb\WD"ZeE4hj$"seU(,Lcj&OeWZh,#9:eFCpTb0Lr(K-?NL*<%d"7$Q=P\+5C;@eCL#()8lMJmaUGRGP.kW%/rX61U8D1;uBN8U3r]\*:97utGCOttAR[`i1=OH./R;th&Tg'dIc0>kc!%](C.W8M!(.#7*Ze]YGf%j^$t*#EmJ@>e>kESUi',MOMs%=_]tKoTP_SEkaP.qAA+5guYnIK/)M:`0]K;kY22Oi9!KGKf4>iYWHC[]Q"Ye]Pon:01"iS>Y*olAMF*'Hh?@On$LQ6=^SXi??^ZC6u52k*Q54TJ01dPUP#^Wj6c=ir;@kE==W2<"Y8@(d@_hpK?;GR&r%KDU+qU2sr=1ja_EbJGgirbUIbMG\eZH&".pRI5cG0&!TlK)2d'ea;W(V@PZB/K:=XR>#O=eVQET3p!%RHEK7#FAL%qA*^`g:C;.EY7!mE0tnF8`El:0&GupW-3P[~>endstream +endobj +29 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1324 +>> +stream +Gatm:gN)%,&:N_Cm%`Ul0EL%Gb]MQ`C+#Eh*Of)/(V`5r8qKJD$33YgIXRP!1UQ163/K5H%"dEsm+J]Mj:Kap\%fldi+rUq(EBr8f`HEB@[LH/63h[KZN)rqi`taC$2aS5B2aLfl"%fY@)MaZf>Z$;>`Bh/0T8!j'SM_!VG]j=`dNEePb!+GW:TP2f\[*-l"nl/@A4lp&*#.tFI.Kf)%M57@@&/D^S%Oh@K>(G4JX^S]EhP(X/Y71Q2^EMd7u4KPf[5QUQY//$->U=SDm!nO_r52;-b%-BR]f-'L-Hpk=g7D^JP-[8[jc?h\s+5<9=0bjZV>OI8:*t(kW;^Ig6T.=u^7TCD$-?I2dQ(fA3%*U=3SJC-]/OrsC%]cZZlL+5qJd;%ep%OW<-E;[?WO(`K8A(s\3]!*&(QI0haa&r;a2.?VHC;BAF5[A6!=Sb1LO:<@Ld8GG9mjq5L`@'1]I^UN2B\-'r@TZVF=Vb"KUNFc=SB1.B^i-$5&\@CNDH0uD)#U5]CTR1,RUg9)+;Of@2SI^eTN-?@i;0&PlmDPOZQ&RTrq?8)G/H3ai8.Ep;SA]Q-i7ma:I<&i(cE083XLbu_EELk7aOQ2"%?rV8_J4$@-M&p73GPU/?'#+LJJ,@i!m-<#4N&X;3eM(afjo)W_4a7Z"jiHJ`!]bnJb%G02HWDOo,I`]<)KuAT*Op19,+q0./G*_Op=^.uh.C6:!4ZM=I]^,6F442RP,R2nPQS8V'g6#Qa]ntbju?2o\E)7@9)QC]1oEo[\+jYH)kW7]-cOGf%aco?NUnS@`Lb)7JD"7DQ#)NVu/mN5Mb5ei;~>endstream +endobj +30 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1582 +>> +stream +Gb!;d8T3?E&AII3bbYhFdEj-b;,g=E9imuY2n/7=MaGgO&t*i13D'8L)c=$h-ZM$Y!Gi^@\BW5V)i%rhn]UjFRKL3j_"^%p`aLV+(6JkLTAeTOp`0nuGS7t\6:o(Z"%gDm"4\qV&.9[[ShcBa+YnlsP)Mr"9&R338QZ%ZL?Zm3!D*DgA-><+=@dFAI7OM(a>tNM&F&l\/,<^#?:r..3Y\q!+YfM'g8-39*?@1`9TE5kkae&8]DZH>uupTdMP%!+`$7_!j"Ht$FkO<",G.^%/A9.B*>jO8p,FGZ.aT[(4N\6IO0W^[DbfFcdV#/-`mQm*V/R2MX3BE'1GTBqYm9bN$8F$&13WZ%>h3OFd8e20@>(odk?S(=qdtTSp6fDti!<@^e1BWYlh`,hXXmH;Mi4?O"09rG<_'2Mo2Vb.S";RSg"$Oqm!sHcB:c8\e=!CnQ)\Ns'T>8t4qoJ[N18@#N"q@I&2%$JY;dM:qNGFR\8FdGtP#kJqqYfWT%%h.=Q%4s/4Ef;"EK=u.aB[t%D+i%B,A4s.A-f;$]@/XNsLhA1HrG2uDGcNru2kWT^Ra5tEYQkf;Ynq8DQ+62KJ?=U[bp=hIu?2fmPq:G1eX)cS0\j'X%@R;Ab$D:$h`/dL?ijkM%Y%l1D^q!\9CikJQf*MHo_RDWe1!MN)W(n*PEUMib_qjp)jS2k&Zg%Y\3`_Cg[%b9or8,cc;WXK:)fs8]l#"$)rAW+O[&%V5YcRd:VI%dSf<,Y.G>(>l:DnFc`s-SSm.U?/<>u['RqJTk!q%4'm8[\=X._Rp^:S`^m-<#Hfbk6]sRgJF;^HjB7c:QgKB1c9FLK2`b#p8nI("4lpQj=PZ'KAO)soNEK2Wq^MXam`GK>0oNk3HZI-0;NCG>Trb8JI+TfFh\.Xb1X95iC9J"-1ZpOU?:]jUib140P'9O6>!WTlgfiNdL4C@endstream +endobj +xref +0 31 +0000000000 65535 f +0000000061 00000 n +0000000122 00000 n +0000000229 00000 n +0000000341 00000 n +0000000546 00000 n +0000000651 00000 n +0000000856 00000 n +0000001061 00000 n +0000001138 00000 n +0000001343 00000 n +0000001549 00000 n +0000001755 00000 n +0000001961 00000 n +0000002167 00000 n +0000002373 00000 n +0000002579 00000 n +0000002785 00000 n +0000002855 00000 n +0000003146 00000 n +0000003277 00000 n +0000005186 00000 n +0000007092 00000 n +0000008874 00000 n +0000010692 00000 n +0000013269 00000 n +0000014981 00000 n +0000017082 00000 n +0000019149 00000 n +0000020877 00000 n +0000022293 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (opensource) + +/Info 18 0 R +/Root 17 0 R +/Size 31 +>> +startxref +23967 +%%EOF diff --git a/v2-klasB/Les02-OpenCode/Les02-Slide-Overzicht.md b/v2-klasB/Les02-OpenCode/Les02-Slide-Overzicht.md new file mode 100644 index 0000000..1fb937c --- /dev/null +++ b/v2-klasB/Les02-OpenCode/Les02-Slide-Overzicht.md @@ -0,0 +1,441 @@ +# Les 2 — OpenCode Pro: Rules, Worktrees & Scroll Animations +## Slide Overzicht (Klas B — 2 uur, online) + +**Bronnen-overzicht onderaan dit document.** + +**Lesflow:** Theorie eerst in blokken, dan dedicated **Live Demo** momenten. Geen heen-en-weer geswitch. + +--- + +## Slide 1: Title +### Les 2 — OpenCode Pro + +**Visual:** +- Background: CREAM +- "Les 2" in BLUE +- "OpenCode Pro" in BLACK +- Subtitle: "Rules · Worktrees · Een scroll-animatie site bouwen" + +**Bronnen:** [1] + +--- + +## Slide 2: Terugblik +### Waar staan we? + +**Content:** +- Les 1: kennismaking + leerlijn-opzet +- Iedereen kent ChatGPT en/of Claude (als chat) +- Eindopdracht: AI-app met Next.js + Supabase + Vercel AI SDK + +**Vandaag:** +"Van AI als chat naar AI als coding-IDE. Met regels, config en parallel worktrees." + +**Visual:** Chat-bubble → IDE icoon + +--- + +## Slide 3: Planning +### Vandaag — 120 minuten + +| Onderwerp | Duur | +|-----------|------| +| Welkom + Terugblik | 10 min | +| **Theorie 1** — Waarom OpenCode + kern features | 20 min | +| **Live Demo 1** — Desktop tour | 10 min | +| **Theorie 2** — AGENTS.md + opencode.json + plugin + stack | 15 min | +| **Live Demo 2** — Setup + worktree + bouw SmoothScroll | 15 min | +| **Pauze** | 15 min | +| Lesopdracht: bouw scroll-sectie | 35 min | +| Huiswerk + Afsluiting | (eind) | + +**Visual:** Timeline met YELLOW pauze-rij + PINK demo-rijen + +--- + +## Slide 4: Waarom OpenCode (en niet Cursor)? +### Alternatieven kennen is professioneel + +**Iedereen kent Cursor. Wij beginnen bewust met OpenCode. Waarom?** + +| | Cursor | OpenCode | +|---|--------|----------| +| Licentie | Commercieel ($20/mnd) | Open source (gratis) | +| Models | Eigen routing | Direct: OpenAI, Anthropic, Google, Groq, Ollama | +| Lock-in | Hoog | Geen | +| Scriptable | Beperkt | Volledig (CLI + plugins) | +| Lokale AI | ❌ | ✅ (via Ollama) | + +**Belangrijke punten:** +- **Provider-keuze** — jij beslist welk model, niet de tool +- **Geen vendor lock-in** — past in een pro-toolchain +- **Concept is hetzelfde** — leer OpenCode goed, dan begrijp je Cursor + Claude Code ook +- **Onderdeel van leerlijn** — later kijken we naar Cursor; nu de open variant + +**Belangrijk:** dit is **geen Cursor-bashing**. Cursor is uitstekend. We kiezen voor breedte: meerdere tools kennen > vasthangen aan één. + +**Bronnen:** [1] [4] + +--- + +## Slide 5: Wat is OpenCode? +### Een AI coding-IDE in twee smaken + +**Content:** +- Open source · 60.000+ GitHub stars · door SST +- Werkt met elke provider: OpenAI, Anthropic, Google, Groq, Ollama +- Lokaal — code blijft op jouw machine + +**Twee smaken:** + +| Desktop (vandaag) | TUI (terminal) | +|-------------------|----------------| +| Visuele diff-viewer | Snel met keyboard | +| File tree + Sessions sidebar | Werkt over SSH | +| Multi-session parallel | Goed voor scripts / CI/CD | +| Sneller leren als je begint | Worktree plugin spawnt auto terminal | + +**Vandaag:** Desktop. We tonen TUI **1 minuut** in Live Demo zodat je weet dat 't bestaat. + +**Voor jouw eindopdracht: Desktop.** + +**Bronnen:** [1] [2] [3] [5] + +--- + +## Slide 6: Plan/Build/@explore + /init +### De vier dingen die je echt moet kennen + +**Twee modes** (Tab om te wisselen): +- **plan** — read-only, denkt mee, wijzigt niks +- **build** — default, schrijft code, voert commands uit + +**Sub-agents:** +- `@explore` — read-only verkenning (spaart tokens) +- `@general` — brede taak +- `@scout` — gerichte zoekopdracht + +**`/init` commando** — eerste actie in elk nieuw project: +- Scant je repo +- Stelt vragen +- Schrijft/update je `AGENTS.md` + +**Best practice:** +> Plan eerst. Lees mee. Geef feedback. Tab → Build. + +**Bronnen:** [6] [7] [8] + +--- + +## Slide 7: LIVE DEMO 1 — Desktop Tour +### Wat je nu gaat zien (~10 min) + +**Setup:** OpenCode Desktop open op een leeg Next.js project. + +**Wat ik laat zien:** +1. **TUI 1 minuut** — `opencode` in terminal, snelle Tab/exit. "Hetzelfde, maar terminal-only." +2. **Desktop UI tour** — file tree, chat panel, diff viewer, Sessions sidebar +3. **Plan mode** — vraag `analyseer dit project`, zie dat niks wijzigt +4. **Tab → Build mode** — vraag iets simpels, zie diff +5. **`@explore`** — `@explore wat zit er in deze repo?` — aparte context +6. **`/init`** — laat AGENTS.md genereren + +**Visual:** Mockup van Desktop layout met annotaties. PINK badge "LIVE DEMO". + +--- + +## Slide 8: AGENTS.md — Projectregels +### De officiële OpenCode standaard + +**Wat is AGENTS.md?** +Markdown in project-root dat OpenCode automatisch in elke context laadt. Officieel format — alternatief voor Cursor's `.cursorrules`. + +**Twee locaties:** +- `./AGENTS.md` — per project (in repo) +- `~/.config/opencode/AGENTS.md` — globaal voor alle projecten + +**Externe regels referencen:** +``` +@rules/styling.md +@rules/security.md +``` + +**Hoe te maken:** +- Handmatig schrijven, OF +- `/init` in OpenCode — scant repo, stelt vragen, genereert + +**Tip:** schrijf concrete regels die je AI kan controleren. "Gebruik clean code" werkt niet. "useGSAP alleen, geen useEffect" wel. + +**Bronnen:** [9] + +--- + +## Slide 9: opencode.json — Config +### Model · Permissies · Plugins + +**Locaties:** +- `./opencode.json` — per project +- `~/.config/opencode/opencode.json` — globaal + +**Basis:** +```json +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/gpt-4o-mini" +} +``` + +**Permissies (uit officiele docs):** +```json +{ + "permission": { + "bash": { "*": "ask", "git *": "allow", "rm *": "deny" }, + "edit": { "*": "deny", "app/**/*.tsx": "allow" } + } +} +``` + +Drie levels: `allow` · `ask` · `deny`. Glob patterns voor bash én files. + +**Bronnen:** [10] [11] + +--- + +## Slide 10: opencode-worktree Plugin +### Auto-magisch worktrees aanmaken + +**Wat doet het?** +Plugin van **kdcokenny**. Geeft de agent twee tools: +- `worktree_create` — branch + worktree +- `worktree_delete` — commit + cleanup + +**Installatie via `ocx`:** +```bash +curl -fsSL https://kdco.dev/ocx/install.sh | sh +ocx add kdco/worktree --from https://registry.kdco.dev +``` + +**Desktop workflow:** +1. In main session: "Maak worktree feature-hero" +2. Plugin maakt worktree-folder +3. Klik **+ New Session** → **Open Folder** → kies worktree +4. Parallel agents in sidebar + +**Bronnen:** [12] [13] + +--- + +## Slide 11: Demo Stack — Next.js 16 + GSAP + Lenis +### De stack voor high-end scroll storytelling + +**Stack:** +- **Next.js 16** App Router + TypeScript (Turbopack default) +- **TailwindCSS** +- **GSAP 3.15** + `@gsap/react` — sinds 2025 100% gratis (ScrollTrigger, SplitText) +- **Lenis 1.3** (`lenis/react`) — door Darkroom Engineering + +**Waarom deze combinatie?** + +Dit is dé stack waarmee award-winning scroll-storytelling sites gebouwd worden. +Niet "een" optie — **de** standaard op pro-niveau. + +**Wie gebruikt dit?** +- Apple product pages (GSAP scroll animaties) +- Stripe, OpenAI marketing — beide GSAP-based +- Awwwards / FWA winnaars — bijna altijd GSAP +- Studios: **Active Theory**, **Locomotive**, **Resn**, **Darkroom Engineering** (makers van Lenis zelf) + +**Waarom niet Framer Motion?** +- Framer Motion is uitstekend voor **app UI** (modals, transities, micro-interacties) +- GSAP heeft betere timing-precisie en GPU-optimalisaties voor **scroll storytelling** +- Voor jouw eindopdracht (een site die "wow" doet bij scrollen): GSAP + +**Next.js 16 detail:** +- `cookies()`, `headers()`, `params`, `searchParams` zijn **async** — altijd `await` + +**Bronnen:** [14] [15] [16] [17] [18] [19] + +--- + +## Slide 12: Onze AGENTS.md +### Regels voor het scroll-animatie project + +```markdown +# Project Rules + +## Why this stack +High-end scroll storytelling site (Awwwards/FWA niveau). +GSAP + Lenis = pro standaard — Apple, Stripe, OpenAI, Active Theory, +Locomotive, Darkroom Engineering. Framer Motion is voor app UI, niet voor +scroll storytelling. Kies GSAP voor timing-precisie + GPU-perf. + +## Stack +- Next.js 16 (App Router, TypeScript, Turbopack) +- TailwindCSS · GSAP 3.15+ (incl. ScrollTrigger, SplitText) +- Lenis 1.3+ (`lenis/react`) +- @gsap/react voor de useGSAP hook + +## Hard rules +- Geen Framer Motion, react-spring, AOS +- Animaties altijd in Client Components (`"use client"`) +- useGSAP(() => {}, { scope: ref }) — nooit useEffect voor GSAP +- Lenis sync: gsap.ticker.add met autoRaf: false +- Next 16: await params, await cookies(), await headers() + +## Patterns +- Eén wrapper in app/layout.tsx +- Tailwind voor layout; GSAP voor transform/opacity + +## Done = +- Geen hydration warnings +- ScrollTrigger.refresh() na dynamische content +``` + +**De `## Why this stack` sectie** geeft de agent context. Hij snapt nu +*waarom* deze keuzes — en stelt voor dezelfde redenen geen Framer Motion voor. + +**Bronnen:** [14] [15] [19] + +--- + +## Slide 13: LIVE DEMO 2 — Setup + Worktree + SmoothScroll +### Wat je nu gaat zien (~15 min) + +**Wat ik laat zien:** +1. **AGENTS.md** invullen met onze projectregels +2. **opencode.json** maken met permissions +3. Tonen dat `rm -rf` geblokkeerd wordt +4. **`ocx` + worktree plugin** installeren (was al klaar — kort tonen) +5. Via prompt: **worktree feature-hero** aanmaken +6. **+ New Session** → openen op worktree-folder +7. In feature-hero: **SmoothScroll wrapper** laten bouwen +8. Bewijs dat **AGENTS.md regels gevolgd worden** (useGSAP, geen Framer Motion, etc.) + +**Visual:** Mockup Sessions sidebar + diff-viewer. PINK badge "LIVE DEMO". + +--- + +## Slide 14: Pauze +### Pauze! + +**Visual:** "Pauze" groot, "15 minuten" + +--- + +## Slide 15: Lesopdracht +### Bouw een scroll-sectie — 35 minuten + +**Setup (al klaar in starter):** +- Next.js 16 starter +- AGENTS.md, opencode.json, plugin +- GSAP + Lenis geïnstalleerd +- SmoothScroll wrapper bestaat al + +**Jouw taak:** +1. `/init` runnen — check AGENTS.md +2. Vraag agent: maak worktree voor jouw feature +3. Open de worktree in nieuwe Sessions tab +4. Plan-mode → review → Tab → Build +5. Test op `npm run dev` +6. Commit + push + +**Sectie keuze:** hero (SplitText) · features-grid (stagger) · testimonials (horizontaal) · gallery (parallax) + +--- + +## Slide 16: Huiswerk +### Bouw je eigen scroll-animatie site + +**Voor volgende week:** +Bouw een **kleine landing page** met 3-4 scroll secties. + +**Eisen:** +- [ ] Eigen `AGENTS.md` (onze mag als basis) +- [ ] `opencode.json` met permissions +- [ ] **2+ worktrees** gebruikt (Sessions sidebar) +- [ ] **3 verschillende scroll-animaties** +- [ ] Lenis smooth scroll +- [ ] Deploy op Vercel +- [ ] `WORKFLOW.md` reflectie (max 400 woorden) + +**Lever in:** GitHub URL + Vercel URL + screenshot van `git worktree list` + +**Bonus:** Eigen sub-agent · MCP server (context7) · Lighthouse 90+ + +--- + +## Slide 17: Afsluiting +### Volgende les — Introductie Cursor + +**Vandaag gedaan:** +- Waarom OpenCode (vs Cursor) +- Plan/Build/@explore/`/init` +- AGENTS.md + opencode.json +- opencode-worktree plugin via ocx +- Live: Next.js 16 + GSAP + Lenis scroll-site +- Parallel agents via Sessions sidebar + +**Volgende keer — Cursor:** +- De commerciele tegenhanger van OpenCode +- Cursor rules vs AGENTS.md +- Composer, Tab-completion, @-references +- Wanneer kies je welke tool? + +**Vragen? Feedback?** + +--- + +## Slide Summary + +| # | Title | Type | Bronnen | +|---|-------|------|---------| +| 1 | Title | Opening | [1] | +| 2 | Terugblik | Intro | — | +| 3 | Plan | Intro | — | +| 4 | Waarom OpenCode | Theorie | [1] [4] | +| 5 | Wat is OpenCode | Theorie | [1] [2] [3] [5] | +| 6 | Plan/Build/@explore | Theorie | [6] [7] [8] | +| 7 | **LIVE DEMO 1** | Demo | — | +| 8 | AGENTS.md | Theorie | [9] | +| 9 | opencode.json | Theorie | [10] [11] | +| 10 | Worktree plugin | Theorie | [12] [13] | +| 11 | Demo stack | Theorie | [14]–[18] | +| 12 | Onze AGENTS.md | Theorie | [14] [15] [19] | +| 13 | **LIVE DEMO 2** | Demo | — | +| 14 | Pauze | Break | — | +| 15 | Lesopdracht | Praktijk | alle | +| 16 | Huiswerk | Praktijk | alle | +| 17 | Afsluiting | Closing | — | + +--- + +## Bronnen + +**OpenCode (officieel):** +- [1] OpenCode hoofdsite — https://opencode.ai +- [2] OpenCode Download (Desktop) — https://opencode.ai/download +- [3] OpenCode docs — https://opencode.ai/docs/ +- [4] OpenCode GitHub (sst) — https://github.com/sst/opencode +- [5] DeepWiki: Desktop Applications — https://deepwiki.com/sst/opencode/6.7-desktop-applications +- [6] Modes (plan/build) — https://opencode.ai/docs/modes/ +- [7] Agents (sub-agents) — https://opencode.ai/docs/agents/ +- [8] Commands (slash) — https://opencode.ai/docs/commands/ +- [9] Rules (AGENTS.md) — https://opencode.ai/docs/rules/ +- [10] Config — https://opencode.ai/docs/config/ +- [11] Permissions — https://opencode.ai/docs/permissions/ + +**Worktree plugin:** +- [12] opencode-worktree — https://github.com/kdcokenny/opencode-worktree +- [13] ocx (extension manager) — https://github.com/kdcokenny/ocx + +**Next.js 16:** +- [14] Next.js 16 blog — https://nextjs.org/blog/next-16 +- [15] Upgrading to v16 — https://nextjs.org/docs/app/guides/upgrading/version-16 + +**GSAP:** +- [16] GSAP docs v3 — https://gsap.com/docs/v3/ +- [17] GSAP React (useGSAP) — https://gsap.com/resources/React/ +- [18] GSAP License (gratis sinds 2025) — https://gsap.com/standard-license + +**Lenis:** +- [19] Lenis GitHub — https://github.com/darkroomengineering/lenis diff --git a/v2-klasB/Les02-OpenCode/Les02-Slides.pdf b/v2-klasB/Les02-OpenCode/Les02-Slides.pdf new file mode 100644 index 0000000..c289a70 Binary files /dev/null and b/v2-klasB/Les02-OpenCode/Les02-Slides.pdf differ diff --git a/v2-klasB/Les02-OpenCode/Les02-Slides.pptx b/v2-klasB/Les02-OpenCode/Les02-Slides.pptx new file mode 100644 index 0000000..ed958f0 Binary files /dev/null and b/v2-klasB/Les02-OpenCode/Les02-Slides.pptx differ