feat(ui): 인라인 구분자 지원 및 리스트 자료형 제거

- 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 <noreply@anthropic.com>
This commit is contained in:
gm
2026-06-03 00:28:03 +09:00
parent 881b3d1f10
commit df011a5fcc

View File

@@ -230,7 +230,6 @@ input,textarea,button,select{font-family:inherit}
// 상수 // 상수
// ════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════
const VALUE_TYPES = [ const VALUE_TYPES = [
{v:'list', lbl:'리스트 (List)'},
{v:'node', lbl:'노드 (Node)'}, {v:'node', lbl:'노드 (Node)'},
{v:'string', lbl:'문자열 (String)'}, {v:'string', lbl:'문자열 (String)'},
{v:'integer', lbl:'정수 (Integer)'}, {v:'integer', lbl:'정수 (Integer)'},
@@ -632,6 +631,7 @@ function renderValue(nid, val){
const SEP_CFG = [ const SEP_CFG = [
{v:'none', lbl:'없음'}, {v:'none', lbl:'없음'},
{v:'newline', lbl:'줄바꿈'},
{v:'blank', lbl:'빈 줄'}, {v:'blank', lbl:'빈 줄'},
]; ];
@@ -712,49 +712,63 @@ function renderSetting(nid, key, val){
function parseNames(raw){ return (raw??'').trim().split(/[\s,\n]+/).filter(Boolean) } function parseNames(raw){ return (raw??'').trim().split(/[\s,\n]+/).filter(Boolean) }
function toText(){ function toText(){
const lines=[]; // 구분자 값 → 실제 문자열
const title=S.title.trim(); function ss(sep){
if(title){ lines.push(`${S.titleMark}${title}`); lines.push(''); }
function applySep(sep){
const s=sep??'blank'; const s=sep??'blank';
if(s==='none') return; if(s==='none') return '';
if(s==='blank'){ lines.push(''); return; } if(s==='newline') return '\n';
for(const l of String(s).replace(/\\n/g,'\n').split('\n')) lines.push(l); if(s==='blank') return '\n\n';
return String(s); // 직접 입력값 (예: '/', ', ')
} }
// 노드 하나를 문자열로 렌더링
function rNode(node){ function rNode(node){
lines.push(`${node.mark??''}${node.content||''}`); let t=`${node.mark??''}${node.content||''}`;
let pendingSep=null; // 다음 child node 앞에 붙일 구분자
let prevWasList=false; let prevWasList=false;
for(let i=0;i<node.values.length;i++){ for(let i=0;i<node.values.length;i++){
const v=node.values[i]; const v=node.values[i];
const nextV=node.values[i+1]??null; const nextV=node.values[i+1]??null;
if(v.type==='list'){ if(v.type==='list'){
const ns=parseNames(v.names); if(!ns.length) continue; const ns=parseNames(v.names); if(!ns.length) continue;
const cat=(v.category??'').trim(), pre=cat?`(${cat}) `:''; const cat=(v.category??'').trim(), pre=cat?`(${cat}) `:'';
const sep=v.sep??' ', npl=v.namesPerLine??0; const lsep=v.sep??' ', npl=v.namesPerLine??0;
if(sep==='\n') ns.forEach((n,j)=>lines.push((j===0?pre:'')+n)); let lt;
else if(npl>0) for(let j=0;j<ns.length;j+=npl) lines.push((j===0?pre:'')+ns.slice(j,j+npl).join(sep)); if(lsep==='\n') lt=ns.map((n,j)=>(j===0?pre:'')+n).join('\n');
else lines.push(pre+ns.join(sep)); 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');
lines.push(''); // list 뒤 빈 줄 (항상) else lt=pre+ns.join(lsep);
t+='\n'+lt+'\n'; // list는 항상 줄 단위
prevWasList=true; prevWasList=true;
pendingSep=null;
} else if(v.type==='node'){ } else if(v.type==='node'){
if(!prevWasList) applySep(node.childSep); // list가 없을 때만 childSep 적용 // 이 child node 앞에 붙일 구분자 결정
rNode(v.node); if(!prevWasList){
if(nextV&&nextV.type==='node') applySep(v.node.nextSep); // 다음도 노드면 nextSep t+=(pendingSep!==null) ? pendingSep : ss(node.childSep??'blank');
}
t+=rNode(v.node);
prevWasList=false; prevWasList=false;
// 다음 형제가 노드면 nextSep을 예약
pendingSep=(nextV&&nextV.type==='node') ? ss(v.node.nextSep??'blank') : null;
} else { } else {
if(v.data) lines.push(String(v.data)); if(v.data) t+='\n'+String(v.data);
prevWasList=false; prevWasList=false;
pendingSep=null;
} }
} }
return t;
} }
const title=S.title.trim();
let out=title?`${S.titleMark}${title}\n\n`:'';
for(let i=0;i<S.nodes.length;i++){ for(let i=0;i<S.nodes.length;i++){
rNode(S.nodes[i]); out+=rNode(S.nodes[i]);
if(i<S.nodes.length-1) applySep(S.nodes[i].nextSep); if(i<S.nodes.length-1) out+=ss(S.nodes[i].nextSep??'blank');
} }
return lines.join('\n').replace(/\n{3,}/g,'\n\n').trimEnd()+'\n'; return out.replace(/\n{3,}/g,'\n\n').trimEnd()+'\n';
} }
// ════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════