<!doctype html>
<html lang="fa">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>نمودار ساده — Grid + Segments (v3-restored + features)</title>
<style>
  :root{--bg:#0f1720;--panel:#0b1220;--accent:#06b6d4;--muted:#94a3b8}
  html,body{height:100%;margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,"Helvetica Neue",Arial}
  body{background:linear-gradient(180deg,#071028 0%, #081125 100%);color:#e6eef6;display:flex;gap:12px;align-items:stretch;padding:14px;box-sizing:border-box}
  .app{display:flex;flex-direction:column;flex:1;min-width:320px;border-radius:10px;overflow:hidden;background:var(--panel);box-shadow:0 6px 30px rgba(2,6,23,.6)}
  header{padding:10px 14px;border-bottom:1px solid rgba(255,255,255,.04);display:flex;gap:10px;align-items:center}
  header h1{font-size:15px;margin:0;font-weight:600}
  .controls{display:flex;flex-wrap:wrap;gap:8px;padding:10px;background:linear-gradient(180deg,rgba(255,255,255,.01),transparent);align-items:center}
  .controls > *{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.04);padding:6px 8px;border-radius:6px;font-size:13px;color:inherit}
  label{display:flex;gap:6px;align-items:center}
  .main{display:flex;flex:1;overflow:hidden}
  .workspace{flex:1;position:relative;display:flex;align-items:stretch;justify-content:stretch;overflow:hidden;border-right:1px solid rgba(255,255,255,.02)}
  canvas{flex:1;background:linear-gradient(180deg,#051327 0%, #04131f 100%);display:block;width:100%;height:100%}
  .sidebar{width:380px;display:flex;flex-direction:column;gap:10px;padding:10px;background:linear-gradient(180deg,rgba(255,255,255,.01),transparent)}
  .list{background:rgba(255,255,255,.02);padding:8px;border-radius:8px;flex:1;overflow:auto}
  .seg{padding:8px;border-radius:8px;margin-bottom:8px;background:rgba(255,255,255,.01);font-size:13px;border:1px solid rgba(255,255,255,.02)}
  .seg.selected{outline:2px solid rgba(6,182,212,0.14);background:linear-gradient(90deg, rgba(6,182,212,0.03), rgba(6,182,212,0.01));border-left:4px solid var(--accent);box-shadow:0 6px 20px rgba(2,6,23,.6), inset 0 0 0 1px rgba(6,182,212,0.03);transform:translateX(0px)}
  .seg.selected .row > div:first-child{font-weight:700;color:var(--accent)}
  .seg h4{margin:0 0 6px 0;font-size:13px}
  .row{display:flex;gap:8px;align-items:center}
  button{cursor:pointer;background:var(--accent);border:none;color:#022;padding:6px 10px;border-radius:8px;font-weight:600}
  .muted{color:var(--muted);font-size:13px}
  .small{padding:6px 8px;font-size:13px;border-radius:6px;background:rgba(255,255,255,.02);border:1px solid rgba(255,255,255,.03)}
  footer{padding:8px;border-top:1px solid rgba(255,255,255,.02);font-size:13px;color:var(--muted);display:flex;justify-content:space-between;align-items:center}
  input[type=color]{width:44px;height:30px;padding:2px;border-radius:6px;border:1px solid rgba(255,255,255,.04);background:white}
  .label{font-size:13px;color:var(--muted);margin-bottom:6px}
  .controlsTop{display:flex;gap:8px;align-items:center;flex-wrap:wrap}

/* Wider, visible scrollbars for lists (WebKit + Firefox) */
.list::-webkit-scrollbar { width: 14px; height:14px; }
.list::-webkit-scrollbar-track { background: rgba(255,255,255,0.01); border-radius: 10px; }
.list::-webkit-scrollbar-thumb { background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.03)); border-radius: 10px; border: 3px solid rgba(0,0,0,0.05); }
.list { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.06) transparent; }

/* make circle list scrollbar more visible as well */
#circleList::-webkit-scrollbar { width: 14px; }
#circleList::-webkit-scrollbar-thumb { background: rgba(6,182,212,0.12); border-radius:10px; border:3px solid rgba(0,0,0,0.04); }

/* small input overlay for text editing */
.text-input-overlay { position:absolute; z-index:999; border:1px solid rgba(255,255,255,0.06); background:rgba(0,0,0,0.6); color:#fff; padding:4px; border-radius:6px; box-shadow:0 8px 20px rgba(2,6,23,.6); }
.text-input-overlay input, .text-input-overlay textarea { background:transparent; border:none; outline:none; color:#fff; font-family:Cambria, "Times New Roman", serif; font-size:16px; width:200px; min-width:120px; }
.small-btn { padding:4px 6px; font-size:12px; border-radius:6px; }
</style>
</head>
<body>
<div class="app" role="application" aria-label="نمودار ساده">
  <header>
    <h1>نمودار ساده — v3 (بازگشتی از v2 + تنظیمات + Circles + Undo/Redo + Save/Load)</h1>
  </header>

  <div class="controls">
    <div class="controlsTop">
      <label class="small">حالت:
        <select id="mode">
          <option value="line">خطی (Straight)</option>
          <option value="smooth" selected>منحنی صاف (Smooth)</option>
        </select>
      </label>
      <label class="small">Snap:
        <input id="snap" type="checkbox" checked />
      </label>
      <label class="small">گرید:
        <input id="gridSize" type="range" min="8" max="64" value="32" />
      </label>
      <span class="muted" id="gridLabel">32 px</span>
      <div style="margin-left:auto;display:flex;gap:8px;align-items:center">
        <button id="newSegment">New Segment</button>
        <button id="addCircleBtn">Add Circle</button>
        <label class="small">Circle size: <input id="circleSize" type="range" min="4" max="80" value="16" /></label>
        <input id="circleColor" type="color" value="#06b6d4" />
        <button id="addTextBtn">Add Text</button>
        <label class="small">Font size: <input id="defaultTextSize" type="range" min="8" max="128" value="24" /></label>
        <input id="defaultTextColor" type="color" value="#f9f9f9" />
        <button id="exportPNG">Export PNG</button>
        <button id="exportSVG">Export SVG</button>
        <button id="undo">Undo</button>
        <button id="redo">Redo</button>
        <button id="clear">Clear</button>
      </div>
    </div>
  </div>

  <div class="main">
    <div class="workspace" id="workspace">
      <canvas id="c"></canvas>
    </div>

    <div class="sidebar" aria-hidden="false">
      <div style="font-weight:700">Segments</div>
      <div class="list" id="seglist"></div>

      <div id="segSettings" style="display:none" class="seg">
        <div class="label">تنظیمات segment انتخاب‌شده</div>
        <div class="row" style="align-items:center">
          <div style="flex:1">
            <div class="muted">ضخامت</div>
            <input id="segStroke" type="range" min="1" max="20" value="2" />
            <div class="muted" id="segStrokeLabel">2 px</div>
          </div>
          <div style="width:10px"></div>
          <div style="width:110px">
            <div class="muted">رنگ</div>
            <input id="segColor" type="color" value="#f9f9f9" />
          </div>
        </div>

        <div style="margin-top:8px">
          <div class="muted">Marker</div>
          <div class="row" style="margin-top:6px">
            <select id="segMarkerType" class="small">
              <option value="none">none</option>
              <option value="circle">circle</option>
              <option value="triangle">triangle</option>
            </select>

            <select id="segMarkerPos" class="small">
              <option value="start">start</option>
              <option value="middle">middle</option>
              <option value="end">end</option>
            </select>

            <button id="removeMarker" style="background:#ef4444">Remove</button>
          </div>
        </div>

        <div style="margin-top:8px">
          <div class="muted">نوع segment</div>
          <select id="segShape" class="small">
            <option value="auto">Use global mode</option>
            <option value="line">خطی (sharp)</option>
            <option value="smooth">منحنی (smooth)</option>
          </select>
        </div>

        <div style="margin-top:10px;display:flex;gap:8px">
          <button id="deleteSegment" style="background:#ef4444">Delete segment</button>
          <div style="flex:1" class="muted">انتخاب شده: <span id="segIndex">—</span></div>
        </div>
      </div>

      <div style="font-weight:700">Circles</div>
      <div class="list" id="circleList" style="max-height:220px;overflow:auto"></div>

      <div id="circleSettings" style="display:none" class="seg">
        <div class="label">تنظیمات Circle</div>
        <div class="row">
          <div style="flex:1">
            <div class="muted">اندازه</div>
            <input id="circleSizeSetting" type="range" min="4" max="80" value="16" />
            <div class="muted" id="circleSizeLabel">16 px</div>
          </div>
          <div style="width:10px"></div>
          <div style="width:110px">
            <div class="muted">رنگ</div>
            <input id="circleColorSetting" type="color" value="#06b6d4" />
          </div>
        </div>
        <div style="margin-top:10px;display:flex;gap:8px">
          <button id="deleteCircle" style="background:#ef4444">Delete circle</button>
          <div style="flex:1" class="muted">انتخاب شده: <span id="circleIndex">—</span></div>
        </div>
      </div>

      <div style="font-weight:700">Texts</div>
      <div class="list" id="textList" style="max-height:220px;overflow:auto"></div>

      <div id="textSettings" style="display:none" class="seg">
        <div class="label">تنظیمات متن</div>
        <div style="margin-bottom:8px">
          <div class="muted">متن</div>
          <textarea id="textContent" rows="2" style="width:100%;background:transparent;border:1px solid rgba(255,255,255,0.03);color:inherit;padding:6px;border-radius:6px;font-family:Cambria, 'Times New Roman', serif"></textarea>
        </div>
        <div class="row">
          <div style="flex:1">
            <div class="muted">اندازه قلم</div>
            <input id="textSizeSetting" type="range" min="8" max="128" value="24" />
            <div class="muted" id="textSizeLabel">24 px</div>
          </div>
          <div style="width:10px"></div>
          <div style="width:110px">
            <div class="muted">رنگ</div>
            <input id="textColorSetting" type="color" value="#f9f9f9" />
          </div>
        </div>
        <div style="margin-top:10px;display:flex;gap:8px">
          <button id="deleteText" style="background:#ef4444">Delete text</button>
          <div style="flex:1" class="muted">انتخاب شده: <span id="textIndex">—</span></div>
        </div>
      </div>

      <div style="margin-top:8px;display:flex;gap:8px">
        <button id="saveJSON">Save (.json)</button>
        <button id="loadJSON">Load (.json)</button>
        <button id="exportHTML">Export HTML (view-only)</button>
        <input id="fileInput" type="file" accept="application/json" style="display:none" />
      </div>

      <div class="muted">راهنما: کلیک برای اضافه، درگ برای جابجایی، کلیک راست حذف نقطه یا دایره. برای ویرایش یک segment یا circle یا متن آن را انتخاب کن. Undo/Redo: Ctrl+Z / Ctrl+Y</div>
    </div>
  </div>

  <footer>
    <div class="muted">Markerها توپر و همرنگ خط — مثلث جهت‌دار متساوی‌الساقین</div>
    <div>نسخه v3-restored — اجرا در مرورگر</div>
  </footer>
</div>

<script>
(() => {
  const canvas = document.getElementById('c');
  const ctx = canvas.getContext('2d');
  const seglist = document.getElementById('seglist');
  const segSettings = document.getElementById('segSettings');
  const segStroke = document.getElementById('segStroke');
  const segStrokeLabel = document.getElementById('segStrokeLabel');
  const segColor = document.getElementById('segColor');
  const segMarkerType = document.getElementById('segMarkerType');
  const segMarkerPos = document.getElementById('segMarkerPos');
  const removeMarker = document.getElementById('removeMarker');
  const deleteSegment = document.getElementById('deleteSegment');
  const segIndex = document.getElementById('segIndex');
  const segShape = document.getElementById('segShape');

  const circleList = document.getElementById('circleList');
  const circleSettings = document.getElementById('circleSettings');
  const circleSizeSetting = document.getElementById('circleSizeSetting');
  const circleSizeLabel = document.getElementById('circleSizeLabel');
  const circleColorSetting = document.getElementById('circleColorSetting');
  const deleteCircle = document.getElementById('deleteCircle');
  const circleIndex = document.getElementById('circleIndex');

  const textList = document.getElementById('textList');
  const textSettings = document.getElementById('textSettings');
  const textContent = document.getElementById('textContent');
  const textSizeSetting = document.getElementById('textSizeSetting');
  const textSizeLabel = document.getElementById('textSizeLabel');
  const textColorSetting = document.getElementById('textColorSetting');
  const deleteText = document.getElementById('deleteText');
  const textIndex = document.getElementById('textIndex');

  const modeEl = document.getElementById('mode');
  const snapEl = document.getElementById('snap');
  const gridSizeEl = document.getElementById('gridSize');
  const gridLabel = document.getElementById('gridLabel');
  const exportPNG = document.getElementById('exportPNG');
  const exportSVG = document.getElementById('exportSVG');
  const undoBtn = document.getElementById('undo');
  const redoBtn = document.getElementById('redo');
  const clearBtn = document.getElementById('clear');
  const newSegBtn = document.getElementById('newSegment');
  const addCircleBtn = document.getElementById('addCircleBtn');
  const circleSizeEl = document.getElementById('circleSize');
  const circleColorEl = document.getElementById('circleColor');
  const saveJSON = document.getElementById('saveJSON');
  const loadJSON = document.getElementById('loadJSON');
  const exportHTML = document.getElementById('exportHTML');
  const fileInput = document.getElementById('fileInput');

  const addTextBtn = document.getElementById('addTextBtn');
  const defaultTextSize = document.getElementById('defaultTextSize');
  const defaultTextColor = document.getElementById('defaultTextColor');
  const workspace = document.getElementById('workspace');

  const DPR = Math.max(1, window.devicePixelRatio || 1);

  // segments: each is { points:[{x,y}], stroke, color, marker: {type,pos} | null, shape: 'auto'|'line'|'smooth' }
  let segments = [];
  let circles = []; // circles: {x,y,r,color}
  let texts = []; // texts: {x,y,text,size,color}
  let currentSeg = -1;
  let currentCircle = -1;
  let currentText = -1;
  let drag = {type:null, seg:-1, idx:-1, circle:-1, text:-1};

  // History for undo/redo (stores deep snapshots as strings)
  let undoStack = [];
  let redoStack = [];
  function snapshot(){ return JSON.stringify({segments:segments, circles:circles, texts:texts, currentSeg:currentSeg, currentCircle:currentCircle, currentText:currentText}); }
  function pushHistory(){ undoStack.push(snapshot()); if(undoStack.length>200) undoStack.shift(); redoStack=[]; }
  function restoreFromString(s){ try{ const obj = JSON.parse(s); segments = obj.segments || []; circles = obj.circles || []; texts = obj.texts || []; currentSeg = typeof obj.currentSeg==='number'?obj.currentSeg:-1; currentCircle = typeof obj.currentCircle==='number'?obj.currentCircle:-1; currentText = typeof obj.currentText==='number'?obj.currentText:-1; rebuildList(); rebuildCircleList(); rebuildTextList(); showSettingsFor(currentSeg); showCircleSettings(currentCircle); showTextSettings(currentText); draw(); }catch(e){ console.error('Failed to restore', e); } }
  function undo(){ if(undoStack.length<=1) return; const top = undoStack.pop(); redoStack.push(top); const prev = undoStack[undoStack.length-1]; if(prev) restoreFromString(prev); }
  function redo(){ if(redoStack.length===0) return; const s = redoStack.pop(); undoStack.push(s); restoreFromString(s); }

  function resize(){
    const rect = canvas.getBoundingClientRect();
    canvas.width = Math.floor(rect.width * DPR);
    canvas.height = Math.floor(rect.height * DPR);
    canvas.style.width = rect.width + 'px';
    canvas.style.height = rect.height + 'px';
    ctx.setTransform(DPR,0,0,DPR,0,0);
    draw();
  }
  window.addEventListener('resize', resize);
  resize();

  function snapToGrid(p){
    if(!snapEl.checked) return p;
    const g = Number(gridSizeEl.value);
    return { x: Math.round(p.x / g) * g, y: Math.round(p.y / g) * g };
  }

  function canvasPos(e){
    const rect = canvas.getBoundingClientRect();
    return { x: (e.clientX - rect.left), y: (e.clientY - rect.top) };
  }

  function findPoint(p, radius=10){
    for(let s=segments.length-1;s>=0;s--){
      for(let i=segments[s].points.length-1;i>=0;i--){
        const pt = segments[s].points[i];
        const dx = p.x - pt.x; const dy = p.y - pt.y;
        if(Math.hypot(dx,dy) <= radius) return {type:'point', seg:s, idx:i};
      }
    }
    return null;
  }

  function findCircleAt(p){
    for(let i=circles.length-1;i>=0;i--){
      const c = circles[i];
      if(Math.hypot(p.x-c.x,p.y-c.y) <= Math.max(8,c.r)) return {type:'circle', circle:i};
    }
    return null;
  }

  function findTextAt(p){
    for(let i=texts.length-1;i>=0;i--){
      const t = texts[i];
      const size = t.size || 24;
      // we'll assume textBaseline='top' for measuring
      ctx.font = `${size}px Cambria, 'Times New Roman', serif`;
      const metrics = ctx.measureText(t.text || '');
      const w = metrics.width;
      const h = size;
      if(p.x >= t.x && p.x <= t.x + w && p.y >= t.y && p.y <= t.y + h) return {type:'text', text:i};
    }
    return null;
  }

  function addSegment(){
    const seg = { points: [], stroke: 2, color: '#f9f9f9', marker: null, shape: 'auto' };
    segments.push(seg); currentSeg = segments.length - 1; rebuildList(); draw(); showSettingsFor(currentSeg); pushHistory();
  }

  function addPointToCurrent(p){
    if(currentSeg < 0) addSegment();
    const pt = snapToGrid(p);
    segments[currentSeg].points.push(pt);
    rebuildList(); draw(); pushHistory();
  }

  function addCircleAt(p, r, color){
    const c = snapToGrid({x:p.x,y:p.y}); c.r = r||Number(circleSizeEl.value); c.color = color||circleColorEl.value; circles.push(c); currentCircle = circles.length-1; rebuildCircleList(); draw(); showCircleSettings(currentCircle); pushHistory();
  }

  function addTextAt(p, text, size, color){
    const t = snapToGrid({x:p.x,y:p.y}); t.text = text||'Text'; t.size = size||Number(defaultTextSize.value); t.color = color||defaultTextColor.value; texts.push(t); currentText = texts.length-1; rebuildTextList(); draw(); showTextSettings(currentText); pushHistory();
  }

  canvas.addEventListener('pointerdown', (e) => {
    if(e.button === 2) return;
    const pos = canvasPos(e);
    // prefer points first
    const foundPoint = findPoint(pos, 10);
    const foundCircle = findCircleAt(pos);
    const foundText = findTextAt(pos);
    if(foundPoint){ drag = {type:'point', seg:foundPoint.seg, idx:foundPoint.idx}; canvas.setPointerCapture(e.pointerId); }
    else if(foundCircle){ drag = {type:'circle', circle:foundCircle.circle}; canvas.setPointerCapture(e.pointerId); }
    else if(foundText){ drag = {type:'text', text:foundText.text}; currentText = foundText.text; currentSeg = -1; currentCircle = -1; rebuildTextList(); rebuildList(); rebuildCircleList(); showTextSettings(currentText); canvas.setPointerCapture(e.pointerId); }
    else {
      // check if we're in circle-adding mode
      if(addCircleBtn.dataset.active === '1'){
        addCircleAt(pos, Number(circleSizeEl.value), circleColorEl.value);
      } else if(addTextBtn.dataset.active === '1'){
        // show inline input overlay to type text
        openTextInputOverlay(pos);
      } else {
        addPointToCurrent(pos);
      }
    }
  });

  canvas.addEventListener('pointermove', (e) => {
    const pos = canvasPos(e);
    if(drag.type==='point' && drag.seg>=0){ segments[drag.seg].points[drag.idx] = snapToGrid(pos); rebuildList(); draw(); }
    else if(drag.type==='circle' && drag.circle>=0){ circles[drag.circle].x = snapToGrid(pos).x; circles[drag.circle].y = snapToGrid(pos).y; rebuildCircleList(); draw(); }
    else if(drag.type==='text' && drag.text>=0){ texts[drag.text].x = snapToGrid(pos).x; texts[drag.text].y = snapToGrid(pos).y; rebuildTextList(); draw(); }
  });
  canvas.addEventListener('pointerup', (e) => { if(drag.type){ pushHistory(); } drag = {type:null, seg:-1, idx:-1, circle:-1, text:-1}; });

  canvas.addEventListener('contextmenu', (e) => {
    e.preventDefault();
    const pos = canvasPos(e);
    const f = findPoint(pos, 12);
    if(f && f.type==='point'){ segments[f.seg].points.splice(f.idx,1); if(segments[f.seg].points.length===0){ /* keep empty seg */ } rebuildList(); draw(); pushHistory(); return; }
    const fc = findCircleAt(pos);
    if(fc){ circles.splice(fc.circle,1); rebuildCircleList(); draw(); pushHistory(); return; }
    const ft = findTextAt(pos);
    if(ft){ texts.splice(ft.text,1); rebuildTextList(); draw(); pushHistory(); return; }
  });

  gridSizeEl.addEventListener('input', ()=>{ gridLabel.textContent = gridSizeEl.value + ' px'; draw(); });
  modeEl.addEventListener('change', draw);

  newSegBtn.addEventListener('click', ()=> addSegment());
  // wire undo/redo buttons
  undoBtn.addEventListener('click', ()=> undo());
  redoBtn.addEventListener('click', ()=> redo());

  clearBtn.addEventListener('click', ()=>{ segments = []; circles = []; texts = []; currentSeg = -1; currentCircle = -1; currentText = -1; rebuildList(); rebuildCircleList(); rebuildTextList(); draw(); hideSettings(); hideCircleSettings(); hideTextSettings(); pushHistory(); });

  addCircleBtn.addEventListener('click', ()=>{ if(addCircleBtn.dataset.active==='1'){ addCircleBtn.dataset.active='0'; addCircleBtn.style.opacity=1; addCircleBtn.textContent='Add Circle'; } else { addCircleBtn.dataset.active='1'; addCircleBtn.style.opacity=0.9; addCircleBtn.textContent='Place Circle (click)'; addTextBtn.dataset.active='0'; addTextBtn.style.opacity=1; addTextBtn.textContent='Add Text'; } });
  circleSizeEl.addEventListener('input', ()=>{ /* visual only */ });

  addTextBtn.addEventListener('click', ()=>{ if(addTextBtn.dataset.active==='1'){ addTextBtn.dataset.active='0'; addTextBtn.style.opacity=1; addTextBtn.textContent='Add Text'; } else { addTextBtn.dataset.active='1'; addTextBtn.style.opacity=0.95; addTextBtn.textContent='Click to place text'; addCircleBtn.dataset.active='0'; addCircleBtn.style.opacity=1; addCircleBtn.textContent='Add Circle'; } });

  function rebuildList(){
    seglist.innerHTML = '';
    segments.forEach((seg,i)=>{
      const div = document.createElement('div'); div.className='seg' + (i===currentSeg ? ' selected' : '');
      const h = document.createElement('div'); h.className='row'; h.style.justifyContent='space-between';
      const title = document.createElement('div'); title.textContent = `Segment ${i+1} (${seg.points.length})`; title.style.flex='1';
      title.style.cursor='pointer'; title.onclick = ()=>{ currentSeg = i; currentCircle = -1; currentText = -1; rebuildList(); rebuildCircleList(); rebuildTextList(); draw(); showSettingsFor(i); };
      const btns = document.createElement('div'); btns.style.display='flex'; btns.style.gap='6px';
      const sel = document.createElement('button'); sel.textContent='Select'; sel.onclick = (ev)=>{ ev.stopPropagation(); currentSeg=i; currentCircle=-1; currentText=-1; rebuildList(); rebuildCircleList(); rebuildTextList(); draw(); showSettingsFor(i); };
      const del = document.createElement('button'); del.textContent='Delete'; del.style.background='#ef4444'; del.onclick = (ev)=>{ ev.stopPropagation(); if(confirm('Delete this segment?')){ segments.splice(i,1); if(segments.length===0){ segments=[]; currentSeg=-1; hideSettings(); } else { currentSeg = Math.min(currentSeg, segments.length-1); showSettingsFor(currentSeg); } rebuildList(); draw(); pushHistory(); } };
      btns.appendChild(sel); btns.appendChild(del);
      h.appendChild(title); h.appendChild(btns);
      div.appendChild(h);
      // mini preview line
      const preview = document.createElement('div'); preview.style.height='40px'; preview.style.marginTop='8px'; preview.style.background='transparent';
      if(seg.points.length>0){ const canvasPreview = document.createElement('canvas'); canvasPreview.width = 300; canvasPreview.height = 40; canvasPreview.style.width='100%'; canvasPreview.style.height='40px'; preview.appendChild(canvasPreview);
        const pctx = canvasPreview.getContext('2d'); pctx.fillStyle='#071827'; pctx.fillRect(0,0,300,40); pctx.strokeStyle = seg.color; pctx.lineWidth = Math.max(1, seg.stroke); pctx.beginPath();
        // draw scaled preview
        const sx = (seg.points[seg.points.length-1].x - seg.points[0].x) || 1;
        for(let k=0;k<seg.points.length;k++){
          const px = ((seg.points[k].x - seg.points[0].x) / (sx)) * 280 + 10;
          const py = (seg.points[k].y / window.innerHeight) * 35 + 2;
          if(k===0) pctx.moveTo(px,py); else pctx.lineTo(px,py);
        }
        pctx.stroke();
      } else { preview.textContent = '(empty)'; preview.style.color='rgba(255,255,255,0.2)'; }
      div.appendChild(preview);

      seglist.appendChild(div);
    });
  }

  function showSettingsFor(i){
    if(i<0 || i>=segments.length){ hideSettings(); return; }
    const seg = segments[i];
    segSettings.style.display='block';
    segStroke.value = seg.stroke || 2;
    segStrokeLabel.textContent = seg.stroke + ' px';
    segColor.value = seg.color || '#f9f9f9';
    segMarkerType.value = (seg.marker && seg.marker.type) ? seg.marker.type : 'none';
    segMarkerPos.value = (seg.marker && seg.marker.pos) ? seg.marker.pos : 'end';
    segShape.value = seg.shape || 'auto';
    segIndex.textContent = (i+1);
  }

  function hideSettings(){ segSettings.style.display='none'; segIndex.textContent='—'; }

  segStroke.addEventListener('input', ()=>{ if(currentSeg>=0){ segments[currentSeg].stroke = Number(segStroke.value); segStrokeLabel.textContent = segStroke.value + ' px'; draw(); pushHistory(); } });
  segColor.addEventListener('input', ()=>{ if(currentSeg>=0){ segments[currentSeg].color = segColor.value; draw(); pushHistory(); } });
  segMarkerType.addEventListener('change', ()=>{ if(currentSeg>=0){ const t = segMarkerType.value; if(t==='none') segments[currentSeg].marker = null; else segments[currentSeg].marker = {type:t, pos:segMarkerPos.value}; draw(); pushHistory(); } });
  segMarkerPos.addEventListener('change', ()=>{ if(currentSeg>=0){ if(!segments[currentSeg].marker) segments[currentSeg].marker = {type:'circle', pos:segMarkerPos.value}; else segments[currentSeg].marker.pos = segMarkerPos.value; draw(); pushHistory(); } });
  removeMarker.addEventListener('click', ()=>{ if(currentSeg>=0){ segments[currentSeg].marker = null; segMarkerType.value='none'; draw(); pushHistory(); } });
  deleteSegment.addEventListener('click', ()=>{ if(currentSeg>=0 && confirm('Delete this segment?')){ segments.splice(currentSeg,1); if(segments.length===0){ segments=[]; currentSeg=-1; hideSettings(); } else { currentSeg = Math.min(currentSeg, segments.length-1); showSettingsFor(currentSeg); } rebuildList(); draw(); pushHistory(); } });
  segShape.addEventListener('change', ()=>{ if(currentSeg>=0){ segments[currentSeg].shape = segShape.value; draw(); pushHistory(); } });

  // Circle list & settings
  function rebuildCircleList(){
    circleList.innerHTML = '';
    circles.forEach((c,i)=>{
      const div = document.createElement('div'); div.className='seg' + (i===currentCircle ? ' selected' : '');
      const h = document.createElement('div'); h.className='row'; h.style.justifyContent='space-between';
      const title = document.createElement('div'); title.textContent = `Circle ${i+1} (r=${c.r})`; title.style.flex='1'; title.style.cursor='pointer'; title.onclick = ()=>{ currentCircle=i; currentSeg=-1; currentText=-1; rebuildCircleList(); rebuildList(); rebuildTextList(); draw(); showCircleSettings(i); };
      const btns = document.createElement('div'); btns.style.display='flex'; btns.style.gap='6px';
      const sel = document.createElement('button'); sel.textContent='Select'; sel.onclick = (ev)=>{ ev.stopPropagation(); currentCircle=i; currentSeg=-1; currentText=-1; rebuildCircleList(); rebuildList(); rebuildTextList(); draw(); showCircleSettings(i); };
      const del = document.createElement('button'); del.textContent='Delete'; del.style.background='#ef4444'; del.onclick = (ev)=>{ ev.stopPropagation(); if(confirm('Delete this circle?')){ circles.splice(i,1); if(circles.length===0){ circles=[]; currentCircle=-1; hideCircleSettings(); } else { currentCircle = Math.min(currentCircle, circles.length-1); showCircleSettings(currentCircle); } rebuildCircleList(); draw(); pushHistory(); } };
      btns.appendChild(sel); btns.appendChild(del);
      h.appendChild(title); h.appendChild(btns);
      div.appendChild(h);
      circleList.appendChild(div);
    });
  }

  function showCircleSettings(i){
    if(i<0 || i>=circles.length){ hideCircleSettings(); return; }
    const c = circles[i];
    circleSettings.style.display='block';
    circleSizeSetting.value = c.r; circleSizeLabel.textContent = c.r + ' px';
    circleColorSetting.value = c.color || '#06b6d4';
    circleIndex.textContent = (i+1);
  }
  function hideCircleSettings(){ circleSettings.style.display='none'; circleIndex.textContent='—'; }

  circleSizeSetting.addEventListener('input', ()=>{ if(currentCircle>=0){ circles[currentCircle].r = Number(circleSizeSetting.value); circleSizeLabel.textContent = circleSizeSetting.value + ' px'; draw(); pushHistory(); } });
  circleColorSetting.addEventListener('input', ()=>{ if(currentCircle>=0){ circles[currentCircle].color = circleColorSetting.value; draw(); pushHistory(); } });
  deleteCircle.addEventListener('click', ()=>{ if(currentCircle>=0 && confirm('Delete this circle?')){ circles.splice(currentCircle,1); if(circles.length===0){ circles=[]; currentCircle=-1; hideCircleSettings(); } else { currentCircle = Math.min(currentCircle, circles.length-1); showCircleSettings(currentCircle); } rebuildCircleList(); draw(); pushHistory(); } });

  // Text list & settings
  function rebuildTextList(){
    textList.innerHTML = '';
    texts.forEach((t,i)=>{
      const div = document.createElement('div'); div.className='seg' + (i===currentText ? ' selected' : '');
      const h = document.createElement('div'); h.className='row'; h.style.justifyContent='space-between';
      const title = document.createElement('div'); title.textContent = `Text ${i+1}: "${String(t.text).slice(0,20)}"`; title.style.flex='1'; title.style.cursor='pointer'; title.onclick = ()=>{ currentText=i; currentSeg=-1; currentCircle=-1; rebuildTextList(); rebuildList(); rebuildCircleList(); draw(); showTextSettings(i); };
      const btns = document.createElement('div'); btns.style.display='flex'; btns.style.gap='6px';
      const sel = document.createElement('button'); sel.textContent='Select'; sel.onclick = (ev)=>{ ev.stopPropagation(); currentText=i; currentSeg=-1; currentCircle=-1; rebuildTextList(); rebuildList(); rebuildCircleList(); draw(); showTextSettings(i); };
      const del = document.createElement('button'); del.textContent='Delete'; del.style.background='#ef4444'; del.onclick = (ev)=>{ ev.stopPropagation(); if(confirm('Delete this text?')){ texts.splice(i,1); if(texts.length===0){ texts=[]; currentText=-1; hideTextSettings(); } else { currentText = Math.min(currentText, texts.length-1); showTextSettings(currentText); } rebuildTextList(); draw(); pushHistory(); } };
      btns.appendChild(sel); btns.appendChild(del);
      h.appendChild(title); h.appendChild(btns);
      div.appendChild(h);
      textList.appendChild(div);
    });
  }

  function showTextSettings(i){
    if(i<0 || i>=texts.length){ hideTextSettings(); return; }
    const t = texts[i];
    textSettings.style.display='block';
    textContent.value = t.text || '';
    textSizeSetting.value = t.size || 24; textSizeLabel.textContent = (t.size || 24) + ' px';
    textColorSetting.value = t.color || '#f9f9f9';
    textIndex.textContent = (i+1);
  }
  function hideTextSettings(){ textSettings.style.display='none'; textIndex.textContent='—'; }

  textSizeSetting.addEventListener('input', ()=>{ if(currentText>=0){ texts[currentText].size = Number(textSizeSetting.value); textSizeLabel.textContent = textSizeSetting.value + ' px'; draw(); pushHistory(); } });
  textColorSetting.addEventListener('input', ()=>{ if(currentText>=0){ texts[currentText].color = textColorSetting.value; draw(); pushHistory(); } });
  textContent.addEventListener('input', ()=>{ if(currentText>=0){ texts[currentText].text = textContent.value; draw(); pushHistory(); } });
  deleteText.addEventListener('click', ()=>{ if(currentText>=0 && confirm('Delete this text?')){ texts.splice(currentText,1); if(texts.length===0){ texts=[]; currentText=-1; hideTextSettings(); } else { currentText = Math.min(currentText, texts.length-1); showTextSettings(currentText); } rebuildTextList(); draw(); pushHistory(); } });

  // Catmull-Rom conversion
  function catmullRom2bezier(pts){
    const beziers = [];
    if(pts.length < 2) return beziers;
    const p = pts.slice(); p.unshift(pts[0]); p.push(pts[pts.length-1]);
    for(let i=0;i<p.length-3;i++){
      const p0 = p[i], p1 = p[i+1], p2 = p[i+2], p3 = p[i+3];
      const bp1 = { x: p1.x + (p2.x - p0.x)/6, y: p1.y + (p2.y - p0.y)/6 };
      const bp2 = { x: p2.x - (p3.x - p1.x)/6, y: p2.y - (p3.y - p1.y)/6 };
      beziers.push([p1,bp1,bp2,p2]);
    }
    return beziers;
  }

  // helper to compute marker position coordinate on segment
  function computeMarkerPosition(seg){
    if(!seg.marker || seg.points.length===0) return null;
    const pos = seg.marker.pos || 'end';
    if(pos === 'start') return seg.points[0];
    if(pos === 'end') return seg.points[seg.points.length-1];
    // middle: choose middle point index or midpoint between middle two
    const midIndex = Math.floor((seg.points.length-1)/2);
    if(seg.points.length % 2 === 1) return seg.points[midIndex];
    // even -> midpoint
    const a = seg.points[midIndex], b = seg.points[midIndex+1];
    return { x: (a.x+b.x)/2, y: (a.y+b.y)/2 };
  }

  // compute tangent direction for marker orientation
  function computeTangent(seg){
    if(!seg || seg.points.length<2) return {x:1,y:0};
    const pos = seg.marker && seg.marker.pos ? seg.marker.pos : 'end';
    if(pos==='start'){
      const a = seg.points[0], b = seg.points[1]; return normalize({x: b.x-a.x, y: b.y-a.y});
    } else if(pos==='end'){
      const n = seg.points.length; const a = seg.points[n-2], b = seg.points[n-1]; return normalize({x: b.x-a.x, y: b.y-a.y});
    } else {
      const midIndex = Math.floor((seg.points.length-1)/2);
      if(seg.points.length %2 ===1){
        const a = seg.points[midIndex-1]||seg.points[midIndex]; const b = seg.points[midIndex+1]||seg.points[midIndex];
        return normalize({x: b.x-a.x, y: b.y-a.y});
      } else {
        const a = seg.points[midIndex], b = seg.points[midIndex+1]; return normalize({x: b.x-a.x, y: b.y-a.y});
      }
    }
  }

  function normalize(v){ const L = Math.hypot(v.x,v.y)||1; return {x: v.x/L, y: v.y/L}; }

  function drawTriangle(tctx, p, dir, color, segStroke){
    // ensure straight sides by using miter joins and saving / restoring context
    tctx.save();
    tctx.lineJoin = 'miter';
    tctx.miterLimit = 10;
    const size = Math.max(8, segStroke*3);
    const d = normalize(dir);
    const perp = { x: -d.y, y: d.x };
    const lenBack = size * 1.2; // distance from apex to base center
    const halfBase = size * 0.6; // half base width
    const apex = p;
    const baseCenter = { x: p.x - d.x * lenBack, y: p.y - d.y * lenBack };
    const b1 = { x: baseCenter.x + perp.x * halfBase, y: baseCenter.y + perp.y * halfBase };
    const b2 = { x: baseCenter.x - perp.x * halfBase, y: baseCenter.y - perp.y * halfBase };
    tctx.beginPath(); tctx.moveTo(apex.x, apex.y); tctx.lineTo(b1.x, b1.y); tctx.lineTo(b2.x, b2.y); tctx.closePath(); tctx.fillStyle = color; tctx.fill();
    tctx.restore();
  }

  function drawGrid(){
    const g = Number(gridSizeEl.value);
    const w = canvas.width / DPR; const h = canvas.height / DPR;
    ctx.clearRect(0,0,w,h);
    ctx.fillStyle = '#071827'; ctx.fillRect(0,0,w,h);
    ctx.beginPath(); ctx.lineWidth = 1; /* made grid brighter for visibility */ ctx.strokeStyle = 'rgba(255,255,255,0.12)';
    for(let x=0;x<=w;x+=g){ ctx.moveTo(x+0.5,0); ctx.lineTo(x+0.5,h); }
    for(let y=0;y<=h;y+=g){ ctx.moveTo(0,y+0.5); ctx.lineTo(w,y+0.5); }
    ctx.stroke();
  }

  function draw(){
    drawGrid();
    // draw each segment
    segments.forEach((seg,si)=>{
      if(!seg.points || seg.points.length===0) return;
      ctx.beginPath(); ctx.lineWidth = Math.max(1, seg.stroke); ctx.strokeStyle = seg.color;
      // choose join & cap based on segment shape to make corners sharp when requested
      const shape = (seg.shape && seg.shape!=='auto') ? seg.shape : modeEl.value;
      if(shape==='line') { ctx.lineJoin='miter'; ctx.lineCap='square'; } else { ctx.lineJoin='miter'; ctx.lineCap='round'; }

      if(shape==='line' || seg.points.length<3){
        ctx.moveTo(seg.points[0].x, seg.points[0].y);
        for(let i=1;i<seg.points.length;i++) ctx.lineTo(seg.points[i].x, seg.points[i].y);
      } else {
        const beziers = catmullRom2bezier(seg.points);
        ctx.moveTo(beziers[0][0].x, beziers[0][0].y);
        beziers.forEach(b=>{ const [p1,cp1,cp2,p2]=b; ctx.bezierCurveTo(cp1.x,cp1.y,cp2.x,cp2.y,p2.x,p2.y); });
      }
      ctx.stroke();

      // draw marker if any (filled, same color)
      if(seg.marker){
        const mpos = computeMarkerPosition(seg);
        if(mpos){
          const dir = computeTangent(seg);
          if(seg.marker.type==='circle'){
            ctx.beginPath(); ctx.fillStyle = seg.color; ctx.arc(mpos.x,mpos.y, Math.max(6, seg.stroke*1.5), 0, Math.PI*2); ctx.fill();
          } else if(seg.marker.type==='triangle'){
            drawTriangle(ctx, mpos, dir, seg.color, seg.stroke);
          }
        }
      }
    });

    // draw circles
    circles.forEach((c,ci)=>{
      ctx.beginPath(); ctx.fillStyle = c.color || '#06b6d4'; ctx.arc(c.x,c.y,c.r,0,Math.PI*2); ctx.fill();
      // optional outline
      ctx.beginPath(); ctx.lineWidth = 1; ctx.strokeStyle = 'rgba(0,0,0,0.25)'; ctx.arc(c.x,c.y,c.r,0,Math.PI*2); ctx.stroke();
    });

    // draw texts
    texts.forEach((t,ti)=>{
      const size = t.size || 24;
      ctx.save();
      ctx.font = `${size}px Cambria, 'Times New Roman', serif`;
      ctx.textBaseline = 'top';
      ctx.fillStyle = t.color || '#f9f9f9';
      ctx.fillText(t.text || 'Text', t.x, t.y);
      // show bounding box when selected
      if(ti === currentText){
        const metrics = ctx.measureText(t.text || '');
        const w = metrics.width; const h = size;
        ctx.beginPath(); ctx.lineWidth = 1; ctx.strokeStyle = 'rgba(6,182,212,0.6)'; ctx.setLineDash([4,3]); ctx.strokeRect(t.x-4, t.y-4, w+8, h+8);
        ctx.setLineDash([]);
      }
      ctx.restore();
    });
  }

  // Export PNG transparent
  exportPNG.addEventListener('click', ()=>{
    const w = canvas.width, h = canvas.height;
    const tmp = document.createElement('canvas'); tmp.width = w; tmp.height = h;
    const t = tmp.getContext('2d');
    // don't paint background -> transparent
    t.scale(DPR, DPR);

    // draw segments only
    segments.forEach(seg=>{
      if(!seg.points || seg.points.length===0) return;
      t.beginPath(); t.lineWidth = Math.max(1, seg.stroke); t.strokeStyle = seg.color;
      if(((seg.shape && seg.shape!=='auto') ? seg.shape : modeEl.value) === 'line' || seg.points.length<3){ t.moveTo(seg.points[0].x, seg.points[0].y); for(let i=1;i<seg.points.length;i++) t.lineTo(seg.points[i].x, seg.points[i].y); }
      else { const beziers = catmullRom2bezier(seg.points); t.moveTo(beziers[0][0].x, beziers[0][0].y); beziers.forEach(b=>{ const [p1,cp1,cp2,p2]=b; t.bezierCurveTo(cp1.x,cp1.y,cp2.x,cp2.y,p2.x,p2.y); }); }
      t.stroke();
      // markers
      if(seg.marker){
        const mpos = computeMarkerPosition(seg);
        const dir = computeTangent(seg);
        if(mpos){
          if(seg.marker.type==='circle'){ t.beginPath(); t.fillStyle = seg.color; t.arc(mpos.x,mpos.y, Math.max(6, seg.stroke*1.5), 0, Math.PI*2); t.fill(); }
          else if(seg.marker.type==='triangle'){ drawTriangle(t, mpos, dir, seg.color, seg.stroke); }
        }
      }
    });

    // draw circles
    circles.forEach(c=>{ t.beginPath(); t.fillStyle = c.color || '#06b6d4'; t.arc(c.x,c.y,c.r,0,Math.PI*2); t.fill(); });

    // draw texts
    texts.forEach(ti=>{
      const size = ti.size || 24;
      t.save();
      t.font = `${size}px Cambria, 'Times New Roman', serif`;
      t.textBaseline = 'top';
      t.fillStyle = ti.color || '#f9f9f9';
      t.fillText(ti.text || 'Text', ti.x, ti.y);
      t.restore();
    });

    const url = tmp.toDataURL('image/png');
    const a = document.createElement('a'); a.href = url; a.download = 'simple-graph-v3-restored.png'; a.click();
  });

  // Export SVG transparent
  exportSVG.addEventListener('click', ()=>{
    const w = canvas.width / DPR, h = canvas.height / DPR;
    const xmlns = 'http://www.w3.org/2000/svg'; let parts = [];
    parts.push(`<svg xmlns="${xmlns}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`);
    // segments
    segments.forEach(seg=>{
      if(!seg.points || seg.points.length===0) return;
      let path='';
      const shape = (seg.shape && seg.shape!=='auto') ? seg.shape : modeEl.value;
      if(shape==='line' || seg.points.length<3){ path += `M ${seg.points[0].x} ${seg.points[0].y}`; for(let i=1;i<seg.points.length;i++) path += ` L ${seg.points[i].x} ${seg.points[i].y}`; }
      else { const beziers = catmullRom2bezier(seg.points); path += `M ${beziers[0][0].x} ${beziers[0][0].y}`; beziers.forEach(b=>{ const [p1,cp1,cp2,p2]=b; path += ` C ${cp1.x} ${cp1.y}, ${cp2.x} ${cp2.y}, ${p2.x} ${p2.y}`; }); }
      const linejoin = (shape==='line') ? 'miter' : 'miter';
      const linecap = (shape==='line') ? 'square' : 'round';
      parts.push(`<path d="${path}" fill="none" stroke="${seg.color}" stroke-width="${seg.stroke}" stroke-linecap="${linecap}" stroke-linejoin="${linejoin}"/>`);
      // markers
      if(seg.marker){
        const mpos = computeMarkerPosition(seg);
        const dir = computeTangent(seg);
        if(mpos){
          if(seg.marker.type==='circle') parts.push(`<circle cx="${mpos.x}" cy="${mpos.y}" r="${Math.max(6, seg.stroke*1.5)}" fill="${seg.color}" />`);
          else if(seg.marker.type==='triangle'){
            const size = Math.max(8, seg.stroke*3);
            const d = normalize(dir);
            const perp = { x:-d.y, y:d.x };
            const lenBack = size * 1.2;
            const halfBase = size * 0.6;
            const apex = mpos;
            const baseCenter = { x: apex.x - d.x * lenBack, y: apex.y - d.y * lenBack };
            const b1 = { x: baseCenter.x + perp.x * halfBase, y: baseCenter.y + perp.y * halfBase };
            const b2 = { x: baseCenter.x - perp.x * halfBase, y: baseCenter.y - perp.y * halfBase };
            parts.push(`<path d="M ${apex.x} ${apex.y} L ${b1.x} ${b1.y} L ${b2.x} ${b2.y} Z" fill="${seg.color}" stroke-linejoin="miter"/>`);
          }
        }
      }
    });
    // circles
    circles.forEach(c=>{ parts.push(`<circle cx="${c.x}" cy="${c.y}" r="${c.r}" fill="${c.color}" />`); });
    // texts
    texts.forEach(t=>{
      const safeText = String(t.text || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
      const sz = t.size || 24;
      parts.push(`<text x="${t.x}" y="${t.y}" font-family="Cambria, 'Times New Roman', serif" font-size="${sz}px" fill="${t.color || '#f9f9f9'}">${safeText}</text>`);
    });
    parts.push('</svg>');
    const svgStr = parts.join('');
    const blob = new Blob([svgStr], {type:'image/svg+xml'});
    const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'simple-graph-v3-restored.svg'; a.click(); URL.revokeObjectURL(url);
  });

  // Save / Load JSON project
  saveJSON.addEventListener('click', ()=>{
    const data = {segments:segments, circles:circles, texts:texts};
    const blob = new Blob([JSON.stringify(data, null, 2)], {type:'application/json'});
    const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'simple-graph-project.json'; a.click(); URL.revokeObjectURL(url);
  });
  loadJSON.addEventListener('click', ()=> fileInput.click());
  fileInput.addEventListener('change', (ev)=>{
    const f = ev.target.files[0]; if(!f) return; const r = new FileReader(); r.onload = ()=>{ try{ const obj = JSON.parse(r.result); segments = obj.segments || []; circles = obj.circles || []; texts = obj.texts || []; currentSeg = -1; currentCircle = -1; currentText = -1; rebuildList(); rebuildCircleList(); rebuildTextList(); draw(); pushHistory(); }catch(e){ alert('Invalid JSON'); } }; r.readAsText(f);
  });

  // Export view-only HTML (embeds SVG)
  exportHTML.addEventListener('click', ()=>{
    // reuse exportSVG to build svg string
    const w = canvas.width / DPR, h = canvas.height / DPR;
    const xmlns = 'http://www.w3.org/2000/svg'; let parts = [];
    parts.push(`<svg xmlns="${xmlns}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`);
    segments.forEach(seg=>{
      if(!seg.points || seg.points.length===0) return;
      let path='';
      const shape = (seg.shape && seg.shape!=='auto') ? seg.shape : modeEl.value;
      if(shape==='line' || seg.points.length<3){ path += `M ${seg.points[0].x} ${seg.points[0].y}`; for(let i=1;i<seg.points.length;i++) path += ` L ${seg.points[i].x} ${seg.points[i].y}`; }
      else { const beziers = catmullRom2bezier(seg.points); path += `M ${beziers[0][0].x} ${beziers[0][0].y}`; beziers.forEach(b=>{ const [p1,cp1,cp2,p2]=b; path += ` C ${cp1.x} ${cp1.y}, ${cp2.x} ${cp2.y}, ${p2.x} ${p2.y}`; }); }
      parts.push(`<path d="${path}" fill="none" stroke="${seg.color}" stroke-width="${seg.stroke}" stroke-linecap="round" stroke-linejoin="miter"/>`);
    });
    circles.forEach(c=>{ parts.push(`<circle cx="${c.x}" cy="${c.y}" r="${c.r}" fill="${c.color}" />`); });
    texts.forEach(t=>{
      const safeText = String(t.text || '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
      const sz = t.size || 24;
      parts.push(`<text x="${t.x}" y="${t.y}" font-family="Cambria, 'Times New Roman', serif" font-size="${sz}px" fill="${t.color || '#f9f9f9'}">${safeText}</text>`);
    });
    parts.push('</svg>');
    const svgStr = parts.join('');
    const html = `<!doctype html><html><head><meta charset="utf-8"><title>Simple Graph (view)</title></head><body style="margin:0;background:#071827;display:flex;align-items:center;justify-content:center;height:100vh">${svgStr}</body></html>`;
    const blob = new Blob([html], {type:'text/html'});
    const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'simple-graph-view.html'; a.click(); URL.revokeObjectURL(url);
  });

  // inline text input overlay for adding text
  function openTextInputOverlay(pos){
    // create overlay
    const ov = document.createElement('div'); ov.className='text-input-overlay';
    ov.style.left = (pos.x + canvas.getBoundingClientRect().left + 6) + 'px';
    ov.style.top = (pos.y + canvas.getBoundingClientRect().top + 6) + 'px';
    const inp = document.createElement('input'); inp.type='text'; inp.placeholder='متن را وارد کنید...'; inp.style.fontFamily="Cambria, 'Times New Roman', serif"; inp.style.fontSize = defaultTextSize.value + 'px'; inp.style.width='220px';
    const ok = document.createElement('button'); ok.textContent='OK'; ok.className='small-btn'; ok.style.marginLeft='6px';
    const cancel = document.createElement('button'); cancel.textContent='Cancel'; cancel.className='small-btn'; cancel.style.marginLeft='6px';
    ov.appendChild(inp); ov.appendChild(ok); ov.appendChild(cancel);
    document.body.appendChild(ov);
    inp.focus();
    function finish(add){
      const rect = canvas.getBoundingClientRect();
      const p = { x: pos.x, y: pos.y };
      if(add && inp.value.trim()!==''){
        addTextAt(p, inp.value, Number(defaultTextSize.value), defaultTextColor.value);
      }
      ov.remove();
      addTextBtn.dataset.active='0'; addTextBtn.style.opacity=1; addTextBtn.textContent='Add Text';
    }
    ok.onclick = ()=> finish(true);
    cancel.onclick = ()=> finish(false);
    inp.addEventListener('keydown',(e)=>{ if(e.key==='Enter'){ finish(true); } if(e.key==='Escape'){ finish(false); } });
  }

  // initial demo
  segments = [
    { points: [ {x:80,y:200},{x:160,y:140},{x:240,y:220} ], stroke: 3, color: '#f9f9f9', marker: {type:'triangle', pos:'end'}, shape:'smooth' },
    { points: [ {x:320,y:120},{x:400,y:200} ], stroke: 2, color: '#f9f9f9', marker: null, shape:'line' }
  ];
  circles = [ {x:480,y:160,r:16,color:'#06b6d4'} ];
  texts = [ {x:120,y:50,text:'نمونه متن',size:24,color:'#f9f9f9'} ];
  currentSeg = 0; rebuildList(); rebuildCircleList(); rebuildTextList(); showSettingsFor(0); showCircleSettings(0); showTextSettings(0); draw();

  // push initial state to history
  pushHistory();

  // keyboard shortcuts
  window.addEventListener('keydown',(e)=>{
    if(e.key==='Delete'){
      if(currentSeg>=0){ segments.splice(currentSeg,1); if(segments.length===0){ currentSeg=-1; hideSettings(); } else { currentSeg = Math.min(currentSeg, segments.length-1); showSettingsFor(currentSeg); } rebuildList(); draw(); pushHistory(); }
      if(currentCircle>=0){ circles.splice(currentCircle,1); if(circles.length===0){ currentCircle=-1; hideCircleSettings(); } else { currentCircle = Math.min(currentCircle, circles.length-1); showCircleSettings(currentCircle); } rebuildCircleList(); draw(); pushHistory(); }
      if(currentText>=0){ texts.splice(currentText,1); if(texts.length===0){ currentText=-1; hideTextSettings(); } else { currentText = Math.min(currentText, texts.length-1); showTextSettings(currentText); } rebuildTextList(); draw(); pushHistory(); }
    }
    // Ctrl+Z and Ctrl+Y
    if((e.ctrlKey || e.metaKey) && e.key.toLowerCase()==='z'){ e.preventDefault(); undo(); }
    if((e.ctrlKey || e.metaKey) && (e.key.toLowerCase()==='y' || (e.shiftKey && e.key.toLowerCase()==='z'))){ e.preventDefault(); redo(); }
  });

})();
</script>
</body>
</html>