feat(ui): 이름 줄바꿈 토글 및 구분자 옵션을 노드 설정으로 이동

- 노드에 nameNewline(bool, default:true) 속성 추가
  - true: 이름 다음 줄바꿈 후 값 출력 (기존 동작)
  - false: 이름 바로 뒤에 값 연결 (인라인)
  - 첫 번째 list/scalar에만 적용, 이후는 항상 '\n' 사용
- 구분자 설정 바(sep-bar)를 노드 설정 섹션 내부로 이동
  - 다음 노드 / 하위 노드 / 이름 뒤 3개 행 항상 표시
  - 선택적 설정(header 등)은 드롭다운으로 하단에 추가
- renderSepBar → renderStructSettings 리네임

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
gm
2026-06-03 00:54:12 +09:00
parent df011a5fcc
commit 63cb612a6e

View File

@@ -105,8 +105,8 @@ input,textarea,button,select{font-family:inherit}
.npl-inp:disabled{background:var(--surface-alt);color:var(--muted)}
.opt-hint{font-size:10px;color:var(--muted)}
/* ── 구분자 설정 바 ───────────────────────── */
.sep-bar{background:var(--surface-alt);border:1px solid var(--border);border-radius:5px;padding:5px 8px;display:flex;flex-direction:column;gap:4px}
/* ── 구분자/이름줄바꿈 설정 (노드 설정 내부) ─ */
.struct-sep{display:flex;flex-direction:column;gap:4px;padding-bottom:6px;margin-bottom:6px;border-bottom:1px dashed var(--border)}
.sep-bar-row{display:flex;align-items:center;gap:5px}
.sep-bar-lbl{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);width:52px;flex-shrink:0}
@@ -283,7 +283,7 @@ function uid(){ return Math.random().toString(36).slice(2,9) }
function mkNode(mark='', content=''){
return { id:uid(), mark, content, hidden:false, values:[], settings:{},
nextSep:'blank', childSep:'blank' };
nextSep:'blank', childSep:'blank', nameNewline:true };
}
function mkListVal(names='', category=''){
return { id:uid(), type:'list', names, sep:' ', namesPerLine:0, category };
@@ -332,8 +332,9 @@ function load(){
}
function normalizeNode(n){
if(n.nextSep===undefined) n.nextSep='blank';
if(n.childSep===undefined) n.childSep='blank';
if(n.nextSep===undefined) n.nextSep='blank';
if(n.childSep===undefined) n.childSep='blank';
if(n.nameNewline===undefined) n.nameNewline=true;
for(const v of n.values||[]) if(v.type==='node') normalizeNode(v.node);
}
function normalizeState(s){
@@ -523,7 +524,6 @@ function renderNode(node, nested){
</div>
${!node.hidden?`
<div class="node-body">
${renderSepBar(node)}
<!-- 값 -->
<div class="body-sec">
<div class="body-sec-hdr">
@@ -547,12 +547,12 @@ function renderNode(node, nested){
onchange="addSetting('${node.id}',this.value);this.selectedIndex=0">
<option value=""> 옵션 추가</option>
${setOpts}
</select>`
:`<span class="all-set">모두 설정됨</span>`}
</select>`:''}
</div>
<div class="body-sec-body">
${renderStructSettings(node)}
${settingItems}
</div>
${settingItems
?`<div class="body-sec-body">${settingItems}</div>`
:'<div class="empty-hint">기본값 사용</div>'}
</div>
</div>`:''}
</div>`;
@@ -653,16 +653,28 @@ function renderSepChips(nid, field, current){
style="width:56px">`;
}
function renderSepBar(node){
function setNameNL(nid, val){
const n=nodeMap[nid]; if(!n) return;
n.nameNewline=val; refresh();
}
function renderStructSettings(node){
const nid=node.id;
const nl=node.nameNewline??true;
return `
<div class="sep-bar">
<div class="struct-sep">
<div class="sep-bar-row">
<span class="sep-bar-lbl">다음 노드</span>
${renderSepChips(node.id,'nextSep', node.nextSep??'blank')}
${renderSepChips(nid,'nextSep', node.nextSep??'blank')}
</div>
<div class="sep-bar-row">
<span class="sep-bar-lbl">하위 노드</span>
${renderSepChips(node.id,'childSep', node.childSep??'blank')}
${renderSepChips(nid,'childSep', node.childSep??'blank')}
</div>
<div class="sep-bar-row">
<span class="sep-bar-lbl">이름 뒤</span>
<span class="sep-chip${nl?' on':''}" onclick="setNameNL('${nid}',true)">줄바꿈</span>
<span class="sep-chip${!nl?' on':''}" onclick="setNameNL('${nid}',false)">없음</span>
</div>
</div>`;
}
@@ -724,8 +736,10 @@ function toText(){
// 노드 하나를 문자열로 렌더링
function rNode(node){
let t=`${node.mark??''}${node.content||''}`;
let pendingSep=null; // 다음 child node 앞에 붙일 구분자
const nl=(node.nameNewline??true)?'\n':''; // 이름 뒤 줄바꿈 여부
let pendingSep=null;
let prevWasList=false;
let firstContent=true; // 첫 번째 list/scalar 여부 (nameNewline 적용 대상)
for(let i=0;i<node.values.length;i++){
const v=node.values[i];
@@ -739,24 +753,25 @@ function toText(){
if(lsep==='\n') lt=ns.map((n,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;
const pre2=firstContent?nl:'\n'; // list만 nameNewline 적용
t+=pre2+lt+'\n';
prevWasList=true; pendingSep=null; firstContent=false;
} else if(v.type==='node'){
// 이 child node 앞에 붙일 구분자 결정
if(!prevWasList){
t+=(pendingSep!==null) ? pendingSep : ss(node.childSep??'blank');
}
t+=rNode(v.node);
prevWasList=false;
// 다음 형제가 노드면 nextSep을 예약
prevWasList=false; firstContent=false;
pendingSep=(nextV&&nextV.type==='node') ? ss(v.node.nextSep??'blank') : null;
} else {
if(v.data) t+='\n'+String(v.data);
prevWasList=false;
pendingSep=null;
if(v.data){
const pre2=firstContent?nl:'\n';
t+=pre2+String(v.data);
firstContent=false;
}
prevWasList=false; pendingSep=null;
}
}
return t;