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:
@@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user