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