Filter
Exclude
Time range
-
Near
Da sich einige von euch eine detaillierte Erklärung meines Setups gewünscht haben, zeige ich euch hier, wie ich es schaffe, mit riesigem Code-Output für nur wenige Cent über einen sehr langen Zeitraum zu arbeiten und wie ihr das nachmachen könnt. Theoretisch könnt ihr diesen Text als Vorlage nutzen und euch das System mit Hilfe eurer IDE (Visual Studio Code) und Roo Code (mit codex oder claude) selbst zusammenbauen. Lokales KI-Coding-Setup mit MCP, lokalem Routing, Code-Verständnis und Cloud-LLM-Ausführung Das Ziel dieses Setups ist nicht, ein großes Sprachmodell einfach „irgendwie Code schreiben“ zu lassen. Genau das ist nämlich der klassische Fehler bei vielen AI-Coding-Workflows: Man gibt einem Modell einen groben Prompt, lädt im Zweifel viel zu viel Projektkontext hinein und hofft anschließend, dass das Modell die richtige Architektur, die relevanten Dateien, die bisherigen Designentscheidungen und alle Seiteneffekte korrekt versteht. Dieses Setup verfolgt einen anderen Ansatz. Die eigentliche Codegenerierung wird weiterhin von einem starken Cloud-Modell übernommen, zum Beispiel DeepSeek, Gemini oder Claude. Diese Modelle sind sehr gut darin, komplexe Implementierungen auszuführen. Sie sind aber teuer, wenn man ihnen permanent unnötigen Kontext schickt, und sie werden unzuverlässig, wenn sie ohne saubere Vorstrukturierung auf ein großes Projekt losgelassen werden. Deshalb wird das System in mehrere Schichten aufgeteilt: Ein lokales Router-Modell entscheidet, welche Art von Aufgabe vorliegt. Ein Code-Understanding-Layer analysiert die Projektstruktur. Ein semantischer Code-Index findet relevante Dateien und Funktionen. Ein Obsidian-/Memory-System speichert frühere Fehler, Architekturentscheidungen und Projektregeln. Ein Cloud-Modell schreibt nur noch den tatsächlich benötigten Code. Build-, Browser-, Git- und Validierungs-Tools prüfen das Ergebnis automatisch. Im Kern entsteht dadurch keine einfache Chatbot-Umgebung, sondern eine kleine, orchestrierte Entwicklungsinfrastruktur. Die KI arbeitet nicht mehr direkt und blind am Projekt, sondern wird durch lokale Werkzeuge vorbereitet, begrenzt, kontrolliert und nachträglich geprüft. 1. Grundprinzip: Lokale Vorarbeit, Cloud-Ausführung, automatische Prüfung Das Setup basiert auf einer einfachen, aber sehr wirksamen Trennung: Das lokale System übernimmt alles, was günstig, wiederholbar und strukturell wichtig ist. Dazu gehören Klassifikation, Kontextfilterung, Suche im Code, Lesen früherer Projektentscheidungen, Erstellen eines technischen Plans und erste Plausibilitätsprüfungen. Das Cloud-Modell übernimmt nur den Teil, bei dem es wirklich stark ist: die eigentliche Formulierung und Implementierung von Code. Dadurch wird das Cloud-Modell nicht als allwissender Projektleiter verwendet, sondern als präziser Code-Schreiber innerhalb eines bereits vorbereiteten technischen Rahmens. Ein typischer Ablauf sieht so aus: User Prompt ↓ local-router / kleines lokales Modell ↓ Task-Klassifikation und Kontextreduktion ↓ code-understanding / AST-Analyse / semantische Suche ↓ Obsidian Memory / Architekturentscheidungen / bekannte Fehler ↓ technischer Implementierungsplan ↓ DeepSeek / Gemini / Claude schreibt Code ↓ lokale Validierung, Tests, Browser-Check, Git-Diff ↓ Memory wird aktualisiert Die Idee ist also nicht: „Das große Modell soll alles wissen.“ Die Idee ist: Das große Modell soll nur das bekommen, was für den aktuellen Task wirklich relevant ist. 2. Die wichtigsten Komponenten Das Setup besteht aus mehreren MCP-Servern und lokalen Werkzeugen. MCP steht für Model Context Protocol. Vereinfacht gesagt ist ein MCP-Server eine standardisierte Schnittstelle, über die ein KI-Coding-Editor wie Roo Code, Cline oder ein ähnliches Agentensystem auf lokale Werkzeuge zugreifen kann. Im ursprünglichen Setup sind unter anderem folgende MCP-Komponenten vorgesehen: local-router, codet5-jepa, code-understanding, obsidian-brain, browser-qa, filesystem, command-shell, git-manager, web-search und hermes-bridge. Diese Komponenten erfüllen unterschiedliche Rollen. 3. local-router: Lokales Modell für Klassifikation und Kontextreduktion Der local-router ist ein kleines lokales Sprachmodell, zum Beispiel Mistral 3B oder ein vergleichbares Instruct-Modell. Es läuft lokal über LM Studio, Ollama oder eine ähnliche Umgebung. Dieses Modell muss keinen perfekten Code schreiben. Das ist nicht seine Aufgabe. Es soll stattdessen schnell und günstig entscheiden, was mit einem Prompt passieren muss. Beispiel: User: "Baue in der mobilen App eine Sprachsteuerung ein, mit der ein Handwerker Stunden auf einen Auftrag buchen kann." Der Router analysiert daraus: { "task_type": "multi_file_feature", "difficulty": 4, "needs_cloud_model": true, "needs_code_search": true, "needs_browser_test": true, "needs_memory_lookup": true, "expected_files": [ "mobile/audio/*", "server/routes/time-booking/*", "shared/types/*" ], "reason": "Die Aufgabe betrifft Frontend-Audioaufnahme, Backend-Verarbeitung, Datenmodell und UI-Feedback." } Damit ist der erste große Vorteil erreicht: Das System weiß, dass es sich nicht um eine kleine Textänderung handelt, sondern um eine Multi-File-Funktion mit Frontend, Backend und Datenmodell. Der Router kann außerdem einfache Prompts abfangen, die gar kein großes Modell benötigen. Beispiel: { "task_type": "simple_refactor", "difficulty": 1, "needs_cloud_model": false, "needs_code_search": true, "needs_browser_test": false, "reason": "Nur Umbenennung einer lokalen Variable in einer einzelnen Datei." } So werden Cloud-Kosten gesenkt, weil nicht jeder kleine Task direkt an DeepSeek oder Gemini geschickt wird. 4. code-understanding: Strukturelles Verständnis des Projekts Ein normales LLM liest Code oft wie Text. Für größere Projekte reicht das nicht. Man braucht eine Schicht, die die tatsächliche Struktur des Codes erkennt. Der code-understanding-Server analysiert deshalb das Projekt über ASTs, also abstrakte Syntaxbäume. Dadurch kann er erkennen: Welche Funktionen existieren. Welche Komponenten welche Props erwarten. Welche API-Routen vorhanden sind. Welche Dateien voneinander abhängen. Welche Imports genutzt werden. Welche State-Management-Patterns existieren. Wo ähnliche Funktionen bereits implementiert sind. Beispielhafte Ausgabe: { "file": "src/features/timeBooking/TimeBookingPanel.tsx", "exports": [ { "name": "TimeBookingPanel", "type": "ReactComponent", "props": ["jobId", "employeeId", "onBooked"] } ], "imports": [ "@/components/ui/button", "@/lib/api", "@/types/jobs" ], "state": [ "selectedDate", "hours", "description", "isSubmitting" ], "api_calls": [ "POST /api/time-bookings" ] } Das Cloud-Modell muss dadurch nicht mehr erraten, wie das Projekt aufgebaut ist. Es bekommt eine technische Zusammenfassung der relevanten Strukturen. 5. Semantische Codesuche mit CodeT5 / JEPA-ähnlichem Layer Der semantische Code-Layer ist dafür zuständig, ähnliche oder relevante Codebereiche zu finden. Eine reine Textsuche reicht dafür nicht aus. Wenn der Nutzer zum Beispiel schreibt: "Baue eine Funktion ein, mit der ein Meister per Sprache Arbeitszeit auf einen Auftrag buchen kann." Dann kann eine normale Suche nach „Sprache“ oder „Arbeitszeit“ scheitern, wenn die vorhandenen Dateien anders heißen, zum Beispiel: voiceInput.ts jobDuration.ts workLogEntry.ts taskTimeRecorder.tsx Ein semantisches Code-Modell erkennt dagegen inhaltliche Ähnlichkeit. Es versteht, dass „Arbeitszeit buchen“, „time logging“, „work entry“ und „duration tracking“ wahrscheinlich zusammengehören. Beispielhafte Ausgabe: { "query": "voice based time booking for construction job", "matches": [ { "file": "src/features/worklog/WorkLogForm.tsx", "score": 0.91, "reason": "Existing form for booking work hours to a job." }, { "file": "src/server/routes/worklog.ts", "score": 0.88, "reason": "Backend endpoint for storing worklog entries." }, { "file": "src/mobile/audio/VoiceRecorder.tsx", "score": 0.82, "reason": "Existing audio recording component." } ] } Das ist einer der zentralen Punkte des Setups: Das Cloud-Modell bekommt nicht „das ganze Projekt“, sondern die relevantesten Dateien und eine Begründung, warum diese Dateien relevant sind. 6. Obsidian als technisches Langzeitgedächtnis Viele AI-Coding-Setups scheitern daran, dass das Modell keine stabile Erinnerung an frühere Entscheidungen hat. Es weiß nicht, warum bestimmte Dinge so gebaut wurden. Es kennt alte Bugs nicht. Es schlägt Lösungen vor, die bereits ausgeschlossen wurden. Deshalb gibt es ein Obsidian-basiertes Memory-System. Dort werden gespeichert: Architekturentscheidungen. Gelöste Bugs. Projektregeln. bekannte technische Einschränkungen. Datenbankentscheidungen. API-Konventionen. UI-Konventionen. Lessons Learned aus früheren Tasks. Beispiel einer Memory-Datei: # 2026-05-26-time-booking-timezone-fix ## Kontext Beim Buchen von Arbeitszeiten kam es zu Inkonsistenzen zwischen Frontend-Zeitangaben und SQLite-Speicherung. ## Entscheidung Alle Zeitstempel werden im Frontend als ISO-8601-String erzeugt und vor dem Speichern im Backend nach UTC normalisiert. ## Regel Keine lokalen Zeitzonen-Offsets direkt in SQLite speichern. ## Betroffene Dateien - src/features/timeBooking/TimeBookingPanel.tsx - src/server/routes/timeBookings.ts - src/lib/dateUtils.ts ## Fehlerbild SQLite sortierte Zeitwerte inkonsistent, wenn Strings mit unterschiedlichen Zeitzonen-Offsets gespeichert wurden. ## Lösung Zeitwerte werden vor dem Persistieren vereinheitlicht: ```ts const normalized = new Date(inputDate).toISOString(); Wichtig für zukünftige Tasks Wenn eine neue Funktion Arbeitszeiten, Termine oder Baustellenberichte speichert, immer toISOString() verwenden und keine lokalen Datumsstrings speichern. Wenn später erneut an der Zeiterfassung gearbeitet wird, kann der Agent diese Memory-Datei lesen und vermeidet denselben Fehler. Das ist ein wesentlicher Unterschied zu normalem Prompting: Das Projekt entwickelt über Zeit ein eigenes technisches Gedächtnis. --- ## 7. Beispiel für den vollständigen Agenten-Ablauf Angenommen, der Nutzer gibt folgenden Prompt ein: ```text "Baue eine Sprachfunktion ein, mit der ein Handwerker sagen kann: 'Buche drei Stunden auf Auftrag Meier Elektro', und die App trägt das automatisch in die Arbeitszeittabelle ein." Dann sollte das System nicht direkt Code schreiben, sondern in mehreren Schritten arbeiten. Schritt 1: Task-Klassifikation { "task_type": "multi_file_feature", "domain": "mobile_voice_input", "difficulty": 4, "requires_frontend": true, "requires_backend": true, "requires_database": true, "requires_browser_test": true, "requires_memory_lookup": true } Schritt 2: Relevante Dateien suchen { "relevant_files": [ "src/mobile/components/VoiceInputButton.tsx", "src/features/worklog/WorkLogForm.tsx", "src/server/routes/worklog.ts", "src/server/db/schema.ts", "src/shared/types/worklog.ts" ] } Schritt 3: Memory prüfen { "memory_hits": [ { "title": "time-booking-timezone-fix", "rule": "Zeitstempel vor Speicherung immer in UTC normalisieren." }, { "title": "mobile-api-streaming-pattern", "rule": "Mobile App empfängt Serverantworten über SSE-Streaming." } ] } Schritt 4: Technischer Plan ## Implementierungsplan 1. Bestehenden VoiceInputButton erweitern. 2. Audioaufnahme als Base64 an Backend senden. 3. Backend-Endpunkt `/api/voice/worklog` erstellen. 4. Lokale Whisper-Transkription oder API-Transkription einbinden. 5. Transkribierten Text in strukturierte Buchungsdaten umwandeln: - Auftrag - Stundenanzahl - Tätigkeit - Datum 6. Daten validieren. 7. Arbeitszeit in bestehende Worklog-Tabelle schreiben. 8. Mobile UI zeigt bestätigte Aktionskarte. 9. Browser-/UI-Test ausführen. 10. Memory-Datei zum neuen Pattern speichern. Schritt 5: Erst jetzt schreibt das Cloud-Modell Code Das Cloud-Modell bekommt nicht den Originalprompt plus riesiges Repository, sondern ein kompaktes Paket: { "task": "Implement voice-based worklog booking", "files_to_edit": [ "src/mobile/components/VoiceInputButton.tsx", "src/server/routes/worklogVoice.ts", "src/shared/types/worklog.ts" ], "rules": [ "Use existing API client pattern.", "Normalize all timestamps with toISOString().", "Do not introduce a new state management library.", "Use existing WorkLogEntry type if possible.", "Return streamed status updates via SSE." ], "memory": [ "Time values must be normalized to UTC before SQLite insert.", "Mobile action confirmations use ActionCard component." ] } Das ist viel sauberer als ein chaotischer Prompt mit „mach mal Sprachsteuerung“. 8. Beispiel: MCP-Konfiguration Eine einfache .roo/mcp.json könnte ungefähr so aussehen: { "mcpServers": { "filesystem": { "command": "node", "args": ["./mcp/filesystem-server.js"], "env": { "PROJECT_ROOT": "C:/Users/Theo/projects/werkai" } }, "command-shell": { "command": "node", "args": ["./mcp/command-shell-server.js"], "env": { "PROJECT_ROOT": "C:/Users/Theo/projects/werkai" } }, "git-manager": { "command": "node", "args": ["./mcp/git-manager-server.js"], "env": { "PROJECT_ROOT": "C:/Users/Theo/projects/werkai" } }, "local-router": { "command": "python", "args": ["./mcp/local-router/server.py"], "env": { "LM_STUDIO_URL": "http://localhost:1234/v1/chat/completions" } }, "code-understanding": { "command": "python", "args": ["./mcp/code-understanding/server.py"], "env": { "PROJECT_ROOT": "C:/Users/Theo/projects/werkai" } }, "obsidian-brain": { "command": "python", "args": ["./mcp/obsidian-brain/server.py"], "env": { "OBSIDIAN_VAULT": "C:/Users/Theo/Obsidian/WerkAI" } }, "browser-qa": { "command": "node", "args": ["./mcp/browser-qa/server.js"] }, "web-search": { "command": "node", "args": ["./mcp/web-search/server.js"] }, "hermes-bridge": { "command": "python", "args": ["./mcp/hermes-bridge/server.py"], "env": { "DATABASE_URL": "sqlite:///werkai.db" } } } } Das ist keine finale Produktionskonfiguration, aber sie zeigt das Prinzip: Jeder MCP-Server übernimmt eine klar abgegrenzte Aufgabe. 9. Beispiel für eine .roomodes-Konfiguration Damit Roo Code oder ein ähnlicher Editor nicht wahllos Tools nutzt, braucht es einen eigenen Modus mit strengen Regeln. { "customModes": [ { "slug": "jepa-developer", "name": "JEPA Developer", "roleDefinition": "You are a structured AI coding agent. You must never edit code before routing the task, retrieving relevant context, checking memory, and producing an implementation plan.", "groups": [ "read", "edit", "browser", "command", "mcp" ], "customInstructions": """ Before every implementation: 1. Use local-router to classify the task. 2. Use code-understanding to identify relevant files. 3. Use obsidian-brain to search for prior decisions and known bugs. 4. Produce a short implementation plan. 5. Only then call the cloud coding model. 6. After code changes, run tests or build commands if available. 7. Inspect git diff. 8. Store important decisions or new bug fixes in obsidian-brain. Never send the whole repository to the cloud model. Never introduce new dependencies without explicit justification. Never ignore existing project patterns. Never overwrite files blindly. """ } ] } Diese Regeln sind entscheidend. Ohne solche Regeln wird ein Coding-Agent oft aus Bequemlichkeit wieder direkt Dateien lesen, große Kontexte laden und ohne ausreichende Prüfung Code ändern. 10. Token-Sparen ohne Qualitätsverlust Der wichtigste wirtschaftliche Vorteil dieses Setups ist die Reduktion unnötiger Cloud-Tokens. Normalerweise entstehen hohe Kosten durch: zu viel Projektkontext, wiederholtes Lesen derselben Dateien, unklare Prompts, falsche erste Implementierungen, fehlende Memory-Struktur, lange Korrekturschleifen, unnötige Cloud-Nutzung bei einfachen Tasks. Dieses Setup reduziert diese Kosten an mehreren Stellen. Erstens: Lokales Routing Einfache Aufgaben werden lokal erkannt und müssen nicht immer an ein großes Modell. Zweitens: Relevanzfilterung Nur relevante Dateien werden in den Prompt aufgenommen. Drittens: Strukturierte Code-Zusammenfassungen Statt ganze Dateien zu schicken, können häufig AST-Zusammenfassungen reichen. Beispiel: { "component": "WorkLogForm", "props": ["jobId", "employeeId"], "submitFunction": "submitWorkLog", "apiEndpoint": "POST /api/worklog", "stateFields": ["hours", "description", "date"] } Das ist wesentlich billiger als 400 Zeilen React-Code, wenn das Modell nur wissen muss, wie die Komponente grundsätzlich funktioniert. Viertens: Memory statt Wiederholung Wenn eine Entscheidung bereits gespeichert wurde, muss sie nicht jedes Mal neu erklärt werden. Fünftens: Tests und Git-Diff Fehler werden lokal erkannt, bevor sie weitere teure Korrekturrunden verursachen. 11. Beispiel für einen komprimierten Cloud-Prompt Ein guter Cloud-Prompt in diesem Setup sieht nicht so aus: Hier ist mein ganzes Projekt. Baue Sprachsteuerung ein. Sondern eher so: ## Task Implement voice-based worklog booking for the mobile app. ## Functional Requirement The user can say: "Buche drei Stunden auf Auftrag Meier Elektro." The system should: 1. Transcribe the audio. 2. Extract structured worklog data. 3. Validate job name and hours. 4. Save the entry using the existing worklog backend. 5. Show a confirmation card in the mobile UI. ## Relevant Existing Patterns ### WorkLogForm - File: `src/features/worklog/WorkLogForm.tsx` - Uses `POST /api/worklog` - State fields: `hours`, `description`, `date`, `jobId` - Uses `ActionCard` after successful submission ### Backend Worklog Route - File: `src/server/routes/worklog.ts` - Validates input with `WorkLogSchema` - Stores data in SQLite - Requires UTC timestamps ## Project Rules - Do not create a new worklog table. - Reuse existing `WorkLogEntry` type. - Normalize all timestamps with `toISOString()`. - Use SSE for streamed status updates. - Do not add a new state management library. ## Files to Edit - `src/mobile/components/VoiceInputButton.tsx` - `src/server/routes/worklogVoice.ts` - `src/shared/types/worklog.ts` ## Output Return a minimal patch and explain changed files briefly. Das Modell bekommt dadurch einen sehr engen, sauberen Arbeitsrahmen. 12. Automatische Prüfung nach der Implementierung Nach der Codegenerierung sollte das Setup nicht einfach stoppen. Der Agent muss prüfen: npm run typecheck npm run lint npm run build npm test Falls eine UI betroffen ist, kann zusätzlich Playwright oder ein Browser-MCP genutzt werden: import { test, expect } from "@playwright/test"; test("voice worklog button opens recording state", async ({ page }) => { await page.goto("http://localhost:5173/mobile"); await page.getByRole("button", { name: /sprache/i }).click(); await expect(page.getByText(/aufnahme läuft/i)).toBeVisible(); }); Danach wird der Git-Diff geprüft: git diff Der Agent soll erkennen: Welche Dateien wurden geändert? Sind unerwartete Dateien betroffen? Wurden neue Dependencies eingeführt? Gibt es große ungewollte Umbauten? Wurde bestehende Logik entfernt? Erst wenn diese Prüfung sauber ist, gilt der Task als abgeschlossen. 13. Beispiel für eine Memory-Aktualisierung nach dem Task Nach erfolgreicher Implementierung sollte automatisch eine neue Memory-Datei entstehen: # 2026-05-26-voice-worklog-booking ## Task Sprachbasierte Arbeitszeitbuchung für die mobile App implementiert. ## Ergebnis Der Nutzer kann per Sprache Arbeitszeiten auf einen Auftrag buchen. Die App sendet Audio an das Backend, das Backend transkribiert den Inhalt, extrahiert Stunden und Auftrag, validiert die Daten und schreibt einen Worklog-Eintrag. ## Betroffene Dateien - src/mobile/components/VoiceInputButton.tsx - src/server/routes/worklogVoice.ts - src/shared/types/worklog.ts ## Architekturentscheidung Die Sprachfunktion nutzt die bestehende Worklog-Struktur und erstellt keine neue Tabelle. ## Wichtige Regel Zeitstempel werden weiterhin über `toISOString()` normalisiert. ## Bekannte Einschränkung Bei mehrdeutigen Auftragsnamen muss der Nutzer eine Bestätigung erhalten, bevor der Eintrag gespeichert wird. ## Nächster sinnvoller Ausbau - Fuzzy Matching für Auftragsnamen - Rückfrage bei unklarer Stundenanzahl - Offline-Warteschlange für Baustellen ohne Internet So wächst das Projektwissen mit jeder Aufgabe. 14. Warum dieses Setup für WerkAI besonders sinnvoll ist WerkAI ist kein kleines Demo-Projekt. Das System soll perspektivisch viele verschiedene Aufgaben für Handwerksbetriebe übernehmen: E-Mails beantworten, Aufträge verwalten, Arbeitszeiten buchen, Excel- und Datenbanklogik verbinden, Dokumente analysieren, Fördermittel prüfen, Angebote vorbereiten, Baustelleninformationen strukturieren, Kundenkommunikation vereinfachen, lokale Datenbestände nutzen, möglicherweise offline oder hybrid laufen. Für so ein System reicht ein normaler Chatbot-Workflow nicht aus. Man braucht eine Entwicklungsumgebung, die mit wachsender Komplexität umgehen kann. Genau dafür ist dieses Setup gedacht. Es sorgt dafür, dass: Code nicht ohne Kontext geschrieben wird, das Modell bestehende Architektur respektiert, frühere Fehler nicht ständig wiederholt werden, Cloud-Kosten sinken, lokale Modelle sinnvoll eingesetzt werden, Tests und Validierung Teil des Workflows werden, der Agent nicht nur schreibt, sondern versteht, prüft und dokumentiert. Der entscheidende Gedanke ist: Nicht das größte Modell gewinnt, sondern die beste Orchestrierung. Ein großes Modell ist stark, aber ohne Kontextkontrolle chaotisch. Ein kleines lokales Modell ist begrenzt, aber sehr nützlich für Routing, Komprimierung und Voranalyse. Ein semantischer Code-Index findet relevante Stellen besser als reine Textsuche. Ein Memory-System gibt dem Projekt Kontinuität. MCP verbindet diese Werkzeuge zu einem praktischen Workflow. Erst zusammen ergibt das ein wirklich produktives AI-Coding-System. 15. Kurz gesagt Dieses Setup verwandelt AI-Coding von einem Chatfenster in eine strukturierte Entwicklungsumgebung. Das Cloud-Modell schreibt nicht mehr blind Code, sondern arbeitet innerhalb eines lokal vorbereiteten technischen Rahmens. Lokale Modelle und MCP-Server übernehmen Routing, Kontextfilterung, Codeanalyse, Memory, Browserprüfung, Terminalausführung und Git-Kontrolle. Dadurch wird das System günstiger, stabiler und besser skalierbar. Das Ziel ist also nicht einfach: „KI schreibt Code.“ Das Ziel ist: Eine kontrollierte Entwicklungsarchitektur, in der KI-Modelle gezielt eingesetzt werden, statt unkontrolliert auf das gesamte Projekt losgelassen zu werden.
3
2
26
1,012
It is! Our implementation contains with `reactify` and `solidify` two functions that makes your component compatible with the other framework. It looks like `const SolidComponent = solidify(ReactComponent)`.
6
721
Which one do you prefer? export default ReactComponent or export const ReactComponent?
2
47
27 Feb 2025
React公式チュートリアルの三目並べをF# Felizで書き換え。Javascriptは得意ではないのでF#を勉強しながらでもあまり手間は変わらない気がする。 module Tutorial open Feliz open Fable.Core.JsInterop let triggerDebugger() = emitJsStatement () "debugger" let MaxRowCount = 3 let MaxColCount = 3 [<ReactComponent>] let Square(props:{|value:string option; onSquareClick:unit ->unit|}) = Html.button [ prop.className "square" prop.onClick (fun _-> props.onSquareClick()) match props.value with | Some v -> prop.text (v.ToString()) | None -> prop.text "" ] [<ReactComponent>] let Board (props:{| xIsNext: bool; squares: string option array; onPlay: string option array -> unit|}) = let calculateWinner (squares: string option array) = let lines = [ [0;1;2]; [3;4;5]; [6;7;8]; [0;3;6]; [1;4;7]; [2;5;8]; [0;4;8]; [2;4;6] ] lines |> List.tryPick (fun line -> match line with | [a;b;c] when squares[a].IsSome && squares[a] = squares[b] && squares[a] = squares[c] -> squares[a] | _ -> None ) let handleClick (index: int) = // Browser.Dom.console.log("デバッグポイント") // triggerDebugger() if props.squares.[index].IsSome || (calculateWinner props.squares).IsSome then () // すでに埋まっている or 勝者が決まっている場合、何もしない else let next = if props.xIsNext then "X" else "O" props.onPlay (props.squares |> Array.updateAt index (Some next)) let winner = calculateWinner props.squares let statusMessage = match winner, props.xIsNext with | Some v, _ -> $"Winner: {v}" | None, true -> $"Next Player: X" | None, false -> $"Next Player: O" let renderRow rowIndex = Html.div [ prop.className "board-row" prop.children [ for colIndex in 0 .. (MaxColCount - 1) do let index = rowIndex * MaxColCount colIndex Square {| value = props.squares.[index]; onSquareClick = fun _ -> handleClick index |} ] ] React.fragment [ Html.div [ prop.className "status" prop.text statusMessage ] Html.div [ for rowIndex in 0 .. (MaxRowCount - 1) do renderRow rowIndex ] ] [<ReactComponent>] let Game() = let history, setHistory = React.useState (fun () -> [Array.create (MaxRowCount*MaxColCount) (None: string option)] ) let currentMove, setCurrentMove = React.useState(0) let currentSquares = history.[currentMove] let xIsNext = currentMove % 2 = 0 let handlePlay (nextSquares : string option array) = let nextHistory = history |> List.take (currentMove 1) |> fun l-> l @ [nextSquares] setHistory nextHistory let newMove = nextHistory.Length - 1 setCurrentMove newMove let jumpTo (nextMove:int) = setCurrentMove nextMove let moves = history |> List.mapi (fun move squares -> let description = if move > 0 then $"Go to move #{move}" else "Go to game start" Html.li [ prop.key move prop.children [ Html.button [ prop.onClick (fun _ -> jumpTo move) prop.text description ] ] ] ) Html.div [ prop.className "game" prop.children [ Html.div [ prop.className "game-board" prop.children [ Board {| xIsNext = xIsNext; squares = currentSquares; onPlay = handlePlay|} ] ] Html.div [ prop.className "game-info" prop.children [ Html.ol [ prop.children moves ] ] ] ] ]

1
56
Key Points 🚀 -------------------------------------------- 🔹 #ReactJS🔹 #React🔹 #ReactDeveloper 🔹 #ReactNative🔹 #ReactComponent
1
2
63
Passing JSX as Props (Children Prop) 🚀 -------------------------------------------- 🔹 #ReactJS🔹 #React🔹 #ReactDeveloper 🔹 #ReactNative🔹 #ReactComponent
1
2
53
Passing Functions as Props (Callback) 🚀 -------------------------------------------- 🔹 #ReactJS🔹 #React🔹 #ReactDeveloper 🔹 #ReactNative🔹 #ReactComponent
1
2
57
React Props🚀 -------------------------------------------- 🔹 #ReactJS🔹 #React🔹 #ReactDeveloper 🔹 #ReactNative🔹 #ReactComponent
2
2
200
React Props🚀 -------------------------------------------- 🔹 #ReactJS🔹 #React🔹 #ReactDeveloper 🔹 #ReactNative🔹 #ReactComponent
2
3
79
React Props🚀 -------------------------------------------- That's a wrap! I hope you enjoyed this thread. Follow @pushpendratips for more amazing content. 💬 Share/like if you found it helpful! 💖​ 📌 Save this for later or you'll miss out! 😉 🔹 #ReactJS🔹 #React🔹 #ReactDeveloper 🔹 #ReactNative🔹 #ReactComponent
32
2
30
1,235
30 Jan 2025
Replying to @aidenybai
"ReactComponent" is totally fine as long as it's consistent throughout the project.
2
31
29 Jan 2025
Replying to @aidenybai
If your component name is ReactComponent, the file MUST be named ReactComponent.jsx Unless you use a case insensitive file system, which is in fact serious mental illness. In that case I guess react-component.jsx it's not the biggest of your problems
1
14
1,429
Class components were the go-to before hooks. They handle complex state with lifecycle methods. Do you still use class components, or have you fully switched to functional components with hooks? #ReactComponent #WebDevelopment #javascript
2
34
Replying to @kooo5252
あ... ミスった、正くは function Hoge$$ReactComponent() { React.createElement("div") } みたいな感じですかね...
2
28
Storybook、自分は環境構築で挫折したやつだ…! ReactComponent単位でデザイナーさんがプレビューできるのは、やっぱり便利そう
開発で Storybook をどのように活用しているのか書いてみましたー Storybook の活用方法:STYLY の場合 qiita.com/Shilaca/items/276e… #Qiitaアドカレ #Qiita @shilaca_より
1
4
667
"I love it when a {ReactComponent} comes together." - Hannibal Smith
2
38
20 Sep 2024
With Panel 1.5.0 we've made it "easy" and "efficient" to create Panel custom components using @reactjs . We support ESM modules, import maps and hot-reload. Below we create a simple ConfettiButton using the new ReactComponent. 🧵👇 #python #dataviz #datascience #Analytics
1
1
278
キチッとしていて、とてもいいなって思う気持ちと。これをみんなができるチーム羨ましいなって気持ち。 設計と実装を分離して、ReactComponentの保守性を高める|Yuto Ohshima zenn.dev/chillnn_tech/articl… #zenn

1
9
1,195
11 Sep 2024
🫣 ピックアップ ✨ 設計と実装を分離して、ReactComponentの保守性を高める by @yu_19_01 zenn.dev/chillnn_tech/articl…
12
104
10,277