{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"concept-map-builder","type":"registry:component","title":"Concept Map Builder","description":"Sketch a central idea and its related branches with editable connectors.","author":"Notion Arena","categories":["visuals"],"tags":["widget","free","concept map","mind map","diagram","learning","visual thinking"],"meta":{"kind":"widget","tier":"free","version":"1.0.0","updatedAt":"2026-10-11","notionPlan":"Free","emoji":"🧠","delivery":["embed","html-block","download"],"capabilities":{"interactive":true,"offlineSafe":true,"persistsLocally":true,"writesToNotion":false,"needsNetwork":false},"embedUrl":"https://notionarena.vercel.app/embed/concept-map-builder","fileUrl":"https://notionarena.vercel.app/widgets/concept-map-builder.html","downloadUrl":"https://notionarena.vercel.app/api/widget/concept-map-builder","height":370,"minHeight":280,"maxHeight":560,"params":[{"key":"label","label":"Widget title","type":"text","default":"Concept map"},{"key":"accent","label":"Accent","type":"color","default":"#9065b0"},{"key":"theme","label":"Theme","type":"select","default":"auto","options":[{"value":"auto","label":"Match Notion"},{"value":"light","label":"Light"},{"value":"dark","label":"Dark"}]}]},"files":[{"path":"widgets/concept-map-builder.html","type":"registry:file","target":"notion/widgets/concept-map-builder.html","content":"<!doctype html>\n<html lang=\"en\" data-theme=\"auto\">\n<head>\n<meta charset=\"utf-8\" /><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" />\n<title>Concept Map Builder</title>\n<style>\n:root{--bg:#fff;--fg:#37352f;--muted:#6b6a68;--surface:#f7f7f5;--border:#e9e9e7;--accent:#9065b0;--accent-ink:#fff;--accent-text:var(--accent);--connector:#c9c9c6;--danger:#c24136}\nhtml[data-theme=\"dark\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#252525;--border:#343434;--connector:#545450;--danger:#ff7369}\n@media(prefers-color-scheme:dark){html[data-theme=\"auto\"]{--bg:#191919;--fg:#e6e6e4;--muted:#9b9a97;--surface:#252525;--border:#343434;--connector:#545450;--danger:#ff7369}}\n*{box-sizing:border-box}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}html,body{margin:0;height:100%}body{background:var(--bg);color:var(--fg);font-family:ui-sans-serif,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;overflow:hidden}.wrap{height:100%;min-height:275px;display:flex;flex-direction:column;gap:8px;padding:13px;overflow:auto}.head{display:flex;align-items:center;justify-content:space-between;gap:8px}.kicker{font-size:9px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);font-weight:650}.title{font-size:14px;font-weight:650;margin-top:2px}.count{font-size:9px;color:var(--muted);text-align:right}.count b{display:block;color:var(--accent-text);font-size:12px}.form{display:grid;grid-template-columns:minmax(0,1fr) minmax(100px,1.2fr) auto;gap:5px}.form input{min-width:0;height:30px;border:1px solid var(--border);border-radius:7px;background:var(--surface);color:var(--fg);padding:4px 7px;font:inherit;font-size:9px}.form input:focus-visible,.core input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.form button{height:30px;border:1px solid var(--accent);border-radius:7px;background:var(--accent);color:var(--accent-ink);font:inherit;font-size:9px;padding:4px 8px;cursor:pointer}.canvas{position:relative;flex:1;min-height:85px;overflow:auto;border:1px solid var(--border);border-radius:9px;background:var(--bg);padding:10px}.map{position:relative;min-height:100%;display:flex;flex-direction:column;align-items:center;gap:30px}.connectors{position:absolute;top:0;left:0;z-index:0;pointer-events:none;overflow:visible;color:var(--connector)}.core{position:relative;z-index:1;display:flex;align-items:center;justify-content:center;min-height:39px;width:min(78%,240px);padding:6px 9px;border:1.5px solid var(--accent);border-radius:11px;background:var(--surface);box-shadow:0 2px 8px #0000000a}.core input{width:100%;height:23px;border:0;outline:0;background:transparent;color:var(--fg);font:inherit;font-size:11px;font-weight:650;text-align:center}.branches{position:relative;z-index:1;display:flex;flex-wrap:wrap;justify-content:center;align-items:stretch;gap:13px;width:100%;padding-bottom:4px}.branch{position:relative;width:calc(50% - 8px);min-width:115px;max-width:215px;min-height:52px;padding:7px 8px;border:1px solid var(--border);border-radius:8px;background:var(--surface)}.branch-head{display:flex;align-items:flex-start;justify-content:space-between;gap:6px}.branch-title{min-width:0;overflow-wrap:anywhere;font-size:9.5px;font-weight:650}.branch-note{margin-top:4px;color:var(--muted);font-size:8.5px;line-height:1.35;overflow-wrap:anywhere}.remove{flex:0 0 19px;width:19px;height:19px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--muted);cursor:pointer}.remove:hover{border-color:var(--danger);color:var(--danger)}.empty{text-align:center;padding:12px;color:var(--muted);font-size:9px}.foot{display:flex;align-items:center;justify-content:space-between;gap:8px}.status,.note{font-size:9px;color:var(--muted);line-height:1.35}.clear{min-height:26px;border:1px solid var(--border);border-radius:7px;background:var(--surface);color:var(--fg);font:inherit;font-size:9px;padding:4px 7px;cursor:pointer}@media(max-width:340px){.wrap{padding:10px}.form{grid-template-columns:minmax(0,1fr) auto}.form input:nth-child(2){grid-column:1}.form button{grid-column:2}.branch{width:100%;max-width:none}.map{gap:24px}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}\n</style>\n</head>\n<body><main class=\"wrap\">\n  <header class=\"head\"><div><div class=\"kicker\">A small visual thinking space</div><div class=\"title\" id=\"title\">Concept map</div></div><div class=\"count\"><b id=\"count\">0</b>branches</div></header>\n  <form class=\"form\" id=\"form\"><label class=\"sr-only\" for=\"branch\">Branch idea</label><input id=\"branch\" maxlength=\"42\" placeholder=\"Add a branch\" required /><label class=\"sr-only\" for=\"note\">Short note</label><input id=\"note\" maxlength=\"90\" placeholder=\"Short note (optional)\" /><button type=\"submit\">Add branch</button></form>\n  <section class=\"canvas\" id=\"canvas\" aria-label=\"Concept map canvas\"><div class=\"map\" id=\"map\"><svg class=\"connectors\" id=\"connectors\" aria-hidden=\"true\"></svg><div class=\"core\"><label class=\"sr-only\" for=\"root\">Central topic</label><input id=\"root\" maxlength=\"56\" value=\"New concept\" /></div><div class=\"branches\" id=\"branches\" role=\"list\" aria-label=\"Concept map branches\"></div></div></section>\n  <footer class=\"foot\"><span class=\"status\" id=\"status\" aria-live=\"polite\">Map stays in this browser. No collaboration or sync.</span><button class=\"clear\" id=\"clear\" type=\"button\">Clear branches</button></footer>\n  <div class=\"note\">A lightweight personal sketch, not a shared whiteboard or verified knowledge model.</div>\n</main>\n<script>\n(function(){\n  var cfgEl=document.getElementById('bk-config'),base={};try{base=cfgEl?JSON.parse(cfgEl.textContent||'{}'):{};}catch(e){}var q=new URLSearchParams(location.search),c={};for(var k in base)c[k]=base[k];q.forEach(function(v,k){c[k]=v;});\n  function safeColor(v,d){return /^#[0-9a-f]{6}$/i.test(String(v||''))?String(v):d;}function rgb(h){var s=String(h).slice(1);return[0,2,4].map(function(i){return parseInt(s.slice(i,i+2),16);});}function lum(a){return a.map(function(v){v/=255;return v<=.04045?v/12.92:Math.pow((v+.055)/1.055,2.4);}).reduce(function(s,v,i){return s+v*[.2126,.7152,.0722][i];},0);}function ratio(a,b){var x=lum(a),y=lum(b);return(Math.max(x,y)+.05)/(Math.min(x,y)+.05);}function ink(h){var a=rgb(h);return ratio(a,[255,255,255])>=ratio(a,[0,0,0])?'#fff':'#000';}function textColor(h){var dark=theme==='dark'||(theme==='auto'&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches),bg=rgb(dark?'#252525':'#f7f7f5'),a=rgb(h),to=dark?[255,255,255]:[0,0,0];if(ratio(a,bg)>=4.5)return h;for(var t=.02;t<=1;t+=.02){var m=a.map(function(v,i){return Math.round(v+(to[i]-v)*t);});if(ratio(m,bg)>=4.5)return'#'+m.map(function(v){return v.toString(16).padStart(2,'0');}).join('');}return dark?'#fff':'#191919';}function setText(){document.documentElement.style.setProperty('--accent-text',textColor(ACCENT));}\n  var theme=['auto','light','dark'].indexOf(String(c.theme||'auto'))>=0?String(c.theme||'auto'):'auto',ACCENT=safeColor(c.accent,'#9065b0'),LABEL=String(c.label||'Concept map').slice(0,48),KEY='notion-arena.concept-map-builder.v1',state={root:'New concept',branches:[]};\n  document.documentElement.setAttribute('data-theme',theme);document.documentElement.style.setProperty('--accent',ACCENT);document.documentElement.style.setProperty('--accent-ink',ink(ACCENT));setText();if(theme==='auto'&&window.matchMedia){var mq=window.matchMedia('(prefers-color-scheme: dark)');if(mq.addEventListener)mq.addEventListener('change',setText);else if(mq.addListener)mq.addListener(setText);}document.getElementById('title').textContent=LABEL;document.title=LABEL;\n  try{var a=JSON.parse(localStorage.getItem(KEY)||'null');if(a&&Array.isArray(a.branches)){state.root=typeof a.root==='string'?a.root.slice(0,56):'New concept';state.branches=a.branches.filter(function(x){return x&&typeof x.label==='string';}).slice(0,24);}}catch(e){}\n  var root=document.getElementById('root'),branches=document.getElementById('branches'),map=document.getElementById('map'),canvas=document.getElementById('canvas'),svg=document.getElementById('connectors'),status=document.getElementById('status');root.value=state.root;\n  function save(msg){state.root=root.value.trim().slice(0,56)||'New concept';try{localStorage.setItem(KEY,JSON.stringify(state));status.textContent=msg||'Map stays in this browser. No collaboration or sync.';}catch(e){status.textContent='Storage is unavailable in this browser session.';}}\n  function draw(){var w=map.clientWidth,h=Math.max(map.scrollHeight,map.clientHeight);if(!w||!h)return;svg.setAttribute('width',w);svg.setAttribute('height',h);svg.setAttribute('viewBox','0 0 '+w+' '+h);while(svg.firstChild)svg.removeChild(svg.firstChild);var core=document.querySelector('.core');if(!core||!state.branches.length)return;var mr=map.getBoundingClientRect(),cr=core.getBoundingClientRect(),sx=cr.left+cr.width/2-mr.left+map.scrollLeft,sy=cr.bottom-mr.top+map.scrollTop;Array.prototype.forEach.call(branches.children,function(card){var r=card.getBoundingClientRect(),ex=r.left+r.width/2-mr.left+map.scrollLeft,ey=r.top-mr.top+map.scrollTop,path=document.createElementNS('http://www.w3.org/2000/svg','path'),curve=Math.max(8,(ey-sy)*.52);path.setAttribute('d','M '+sx+' '+sy+' C '+sx+' '+(sy+curve)+', '+ex+' '+(ey-curve)+', '+ex+' '+ey);path.setAttribute('fill','none');path.setAttribute('stroke','currentColor');path.setAttribute('stroke-width','1.3');path.setAttribute('stroke-linecap','round');svg.appendChild(path);});}\n  function scheduleDraw(){if(window.requestAnimationFrame)requestAnimationFrame(draw);else setTimeout(draw,0);}\n  function render(){branches.textContent='';document.getElementById('count').textContent=String(state.branches.length);if(!state.branches.length){var empty=document.createElement('div');empty.className='empty';empty.textContent='Add a branch to explore a concept.';empty.setAttribute('role','listitem');branches.appendChild(empty);}else state.branches.forEach(function(item,index){var card=document.createElement('article');card.className='branch';card.setAttribute('role','listitem');var head=document.createElement('div');head.className='branch-head';var name=document.createElement('div');name.className='branch-title';name.textContent=item.label;var remove=document.createElement('button');remove.className='remove';remove.type='button';remove.textContent='×';remove.setAttribute('aria-label','Remove branch: '+item.label);remove.addEventListener('click',function(){state.branches.splice(index,1);save();render();});head.append(name,remove);card.appendChild(head);if(item.note){var note=document.createElement('div');note.className='branch-note';note.textContent=item.note;card.appendChild(note);}branches.appendChild(card);});scheduleDraw();}\n  root.addEventListener('input',function(){save();});document.getElementById('form').addEventListener('submit',function(e){e.preventDefault();var label=document.getElementById('branch').value.trim().slice(0,42),note=document.getElementById('note').value.trim().slice(0,90);if(!label)return;if(state.branches.length>=24){status.textContent='Branch limit reached.';return;}state.branches.push({label:label,note:note});document.getElementById('branch').value='';document.getElementById('note').value='';save();render();document.getElementById('branch').focus();});document.getElementById('clear').addEventListener('click',function(){state.branches=[];save('Branches cleared.');render();});if(window.ResizeObserver){var ro=new ResizeObserver(scheduleDraw);ro.observe(map);ro.observe(canvas);}window.addEventListener('resize',scheduleDraw);canvas.addEventListener('scroll',scheduleDraw);render();\n})();\n</script>\n</body>\n</html>\n"}]}