Step 21 - ai
2 min czytania
Add AI diagram generation:
the user describes what they want, an LLM returns STRUCTURE as JSON, and a deterministic client-side layout engine converts it to positioned elements. The model must never produce coordinates - models asked for pixels draw shapes on top of each other.
Cloud Function generateDiagram (callable):
- Input: { kind, description, boardId }. Validate auth, membership, description length; check the board's aiLocked flag (see below); meter usage if you have a credits system.
- Kinds: 'brainstorm' | 'mindmap' | 'process' | 'retro' | 'swot' (add more later). Per kind, the prompt states: a one-line facilitator role, the user's description, "Return ONLY a JSON object, no markdown, matching this schema:", then the exact schema with content limits: brainstorm {topic, groups[{name, ideas[]}]} 2-6 groups x 3-8 ideas mindmap {topic, branches[{name, children[]}]} 3-8 branches x 0-6 children retro {columns[{name, items[]}]} 3-5 columns x 2-6 items swot {quadrants[{key: strengths|weaknesses|opportunities|threats, title, items[]}]} process {nodes[{id, label}], flows[{from, to, label?}]} 3-20 nodes
- Ask for labels in the user's UI language. JSON.parse the reply INSIDE the retry loop so a malformed answer retries instead of failing. Return { raw } - the raw JSON string; the server does no layout.
Client layout engine (pure module + vitest specs per kind):
- A tolerant JSON extractor first (strip code fences, find the outermost object, repair a truncated tail) - never bare JSON.parse on model output.
- Sanity-cap all counts (max 8 groups, 12 items, 10 branches, 40 nodes...). Layout constants: note gap 24, frame padding 28, frame gap 48, 2 notes per row in group frames.
- brainstorm/retro: one frame per group/column with tiled notes. swot: fixed 2x2 quadrant slots with per-quadrant note colors. mindmap: center note, branches on a radius ring max(360, 70n+160), children fanned on a second ring, wired with connections. process: longest-path column assignment per node (bounded iterations so a model-drawn cycle cannot loop forever), stack within columns, connect flows; if the model returned no flows, chain nodes in listed order.
- Return BoardElement[] with empty authorId/createdAt.
Insertion (board component): center the generated bundle on the current viewport, stamp authorId/name and createdAt+index, insert everything as ONE undo batch, and zoom OUT only if the diagram overflows the current view (never zoom in).
Controls: a generate dialog (kind picker + description); an aiLocked flag on the board that only the owner/teacher can toggle, letting a facilitator disable AI for participants - enforce in the function AND hide the dialog live when flipped.
Acceptance: each kind generates a readable, non-overlapping diagram from a one-sentence description; a malformed model reply surfaces a friendly error; undo removes the whole diagram in one step; the lock blocks participants server-side.
Dyskusja
Komentarze: 0Brak komentarzy. Rozpocznij dyskusję.