From df011a5fcce3d49376abb4b554ddf1153da4ae74 Mon Sep 17 00:00:00 2001 From: gm Date: Wed, 3 Jun 2026 00:28:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=EC=9D=B8=EB=9D=BC=EC=9D=B8=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=EC=9E=90=20=EC=A7=80=EC=9B=90=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=9E=90=EB=A3=8C=ED=98=95=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VALUE_TYPES에서 '리스트 (List)' 제거 - SEP_CFG에 '줄바꿈' 프리셋 추가 (없음/줄바꿈/빈줄) - toText() 재작성: lines 배열 → string 직접 결합 방식으로 변경 - 인라인 구분자(예: '/') 사용 시 a/b/c 형태로 한 줄 출력 - ss() 함수: none='' / newline='\n' / blank='\n\n' / 직접입력=그대로 - pendingSep 패턴으로 연속 child node 간 구분자 처리 Co-Authored-By: Claude Sonnet 4.6 --- ui/builder.html | 62 ++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/ui/builder.html b/ui/builder.html index ed8f1cb..b3f3275 100644 --- a/ui/builder.html +++ b/ui/builder.html @@ -230,7 +230,6 @@ input,textarea,button,select{font-family:inherit} // 상수 // ════════════════════════════════════════════════════════ const VALUE_TYPES = [ - {v:'list', lbl:'리스트 (List)'}, {v:'node', lbl:'노드 (Node)'}, {v:'string', lbl:'문자열 (String)'}, {v:'integer', lbl:'정수 (Integer)'}, @@ -631,8 +630,9 @@ function renderValue(nid, val){ } const SEP_CFG = [ - {v:'none', lbl:'없음'}, - {v:'blank', lbl:'빈 줄'}, + {v:'none', lbl:'없음'}, + {v:'newline', lbl:'줄바꿈'}, + {v:'blank', lbl:'빈 줄'}, ]; function setSep(nid, field, val){ @@ -712,49 +712,63 @@ function renderSetting(nid, key, val){ function parseNames(raw){ return (raw??'').trim().split(/[\s,\n]+/).filter(Boolean) } function toText(){ - const lines=[]; - const title=S.title.trim(); - if(title){ lines.push(`${S.titleMark}${title}`); lines.push(''); } - - function applySep(sep){ + // 구분자 값 → 실제 문자열 + function ss(sep){ const s=sep??'blank'; - if(s==='none') return; - if(s==='blank'){ lines.push(''); return; } - for(const l of String(s).replace(/\\n/g,'\n').split('\n')) lines.push(l); + if(s==='none') return ''; + if(s==='newline') return '\n'; + if(s==='blank') return '\n\n'; + return String(s); // 직접 입력값 (예: '/', ', ') } + // 노드 하나를 문자열로 렌더링 function rNode(node){ - lines.push(`${node.mark??''}${node.content||''}`); + let t=`${node.mark??''}${node.content||''}`; + let pendingSep=null; // 다음 child node 앞에 붙일 구분자 let prevWasList=false; + for(let i=0;ilines.push((j===0?pre:'')+n)); - else if(npl>0) for(let j=0;j(j===0?pre:'')+n).join('\n'); + else if(npl>0) lt=Array.from({length:Math.ceil(ns.length/npl)},(_,j)=>(j===0?pre:'')+ns.slice(j*npl,(j+1)*npl).join(lsep)).join('\n'); + else lt=pre+ns.join(lsep); + t+='\n'+lt+'\n'; // list는 항상 줄 단위 prevWasList=true; + pendingSep=null; + } else if(v.type==='node'){ - if(!prevWasList) applySep(node.childSep); // list가 없을 때만 childSep 적용 - rNode(v.node); - if(nextV&&nextV.type==='node') applySep(v.node.nextSep); // 다음도 노드면 nextSep + // 이 child node 앞에 붙일 구분자 결정 + if(!prevWasList){ + t+=(pendingSep!==null) ? pendingSep : ss(node.childSep??'blank'); + } + t+=rNode(v.node); prevWasList=false; + // 다음 형제가 노드면 nextSep을 예약 + pendingSep=(nextV&&nextV.type==='node') ? ss(v.node.nextSep??'blank') : null; + } else { - if(v.data) lines.push(String(v.data)); + if(v.data) t+='\n'+String(v.data); prevWasList=false; + pendingSep=null; } } + return t; } + const title=S.title.trim(); + let out=title?`${S.titleMark}${title}\n\n`:''; for(let i=0;i