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: 0

Brak komentarzy. Rozpocznij dyskusję.