Skip to content

Commit a86460c

Browse files
authored
Add files via upload
1 parent 7626504 commit a86460c

1 file changed

Lines changed: 129 additions & 13 deletions

File tree

docs/kide_wasm.html

Lines changed: 129 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
.mbtn:hover{border-color:var(--cyan);color:var(--txt);}
3939
.mbtn.active{background:#0a2030;border-color:var(--cyan);color:var(--cyan);}
4040
#run-btn{
41-
margin-left:auto;padding:5px 16px;
41+
padding:5px 16px;
4242
background:linear-gradient(135deg,#003828,#002a20);
4343
border:1px solid #00e89055;color:var(--green);border-radius:4px;
4444
font-size:11.5px;font-weight:700;transition:.15s;
@@ -47,6 +47,10 @@
4747
#run-btn:hover{background:linear-gradient(135deg,#004838,#003428);box-shadow:0 0 14px #00e89035;}
4848
#run-btn:disabled{opacity:.5;cursor:default;}
4949
#run-btn.running{color:var(--orange);border-color:#ff6b3555;text-shadow:0 0 8px var(--orange);}
50+
#file-btns{display:flex;gap:4px;margin-left:auto;}
51+
.fbtn{padding:4px 10px;background:var(--bg3);border:1px solid var(--b2);color:var(--mid);border-radius:3px;font-size:11px;transition:.15s;white-space:nowrap;}
52+
.fbtn:hover{border-color:var(--cyan);color:var(--cyan);}
53+
5054
#wasm-badge{
5155
font-size:10px;color:var(--dim);padding:2px 9px;
5256
border:1px solid var(--b1);border-radius:10px;display:flex;align-items:center;gap:5px;
@@ -117,8 +121,6 @@
117121
/* ── 리사이즈 핸들 ── */
118122
.rv{width:4px;background:var(--b1);cursor:col-resize;flex-shrink:0;transition:.15s;}
119123
.rv:hover,.rv.drag{background:var(--cyan);}
120-
#bot-resize{height:4px;background:var(--b1);cursor:row-resize;flex-shrink:0;transition:.15s;}
121-
#bot-resize:hover,#bot-resize.drag{background:var(--cyan);}
122124

123125
/* ── 아웃라인 ── */
124126
#outline{
@@ -154,7 +156,8 @@
154156
border-radius:3px;padding:4px 7px;font-size:11px;color:var(--txt);
155157
outline:none;font-family:monospace;transition:.15s;
156158
}
157-
.prp-div{border-top:1px solid var(--b1);margin:8px 0;}
159+
.prp-del{width:100%;padding:5px;background:#1a0808;border:1px solid var(--red)44;color:var(--red);border-radius:3px;font-size:11px;margin-top:6px;transition:.15s;}
160+
.prp-del:hover{background:var(--red);color:#fff;border-color:var(--red);}
158161
.prp-uid{display:flex;justify-content:space-between;font-size:9px;color:var(--dim);padding:2px 0;}
159162

160163
/* ── AI 패널 ── */
@@ -293,9 +296,16 @@
293296

294297
/* ── 하단 ── */
295298
#bottom{
296-
height:var(--botH);min-height:60px;background:var(--panel);
299+
height:var(--botH);min-height:60px;max-height:70vh;background:var(--panel);
297300
border-top:1px solid var(--b1);display:flex;flex-direction:column;flex-shrink:0;
301+
overflow:hidden;
302+
}
303+
#bot-resize{
304+
height:5px;background:var(--b1);cursor:row-resize;flex-shrink:0;
305+
transition:.15s;border-top:1px solid var(--b1);
298306
}
307+
#bot-resize:hover,#bot-resize.drag{background:var(--cyan);box-shadow:0 0 6px var(--cyan)44;}
308+
299309
#tabs{
300310
display:flex;align-items:center;background:#060a14;
301311
border-bottom:1px solid var(--b1);padding:0 8px;gap:2px;flex-shrink:0;
@@ -361,6 +371,12 @@
361371
<button id="mt" class="mbtn" onclick="setMode('text')">≡ 텍스트</button>
362372
</div>
363373
<button id="run-btn" onclick="handleRun()">▶ 실행</button>
374+
<div id="file-btns">
375+
<button class="fbtn" onclick="newFile()" title="새 파일 (Ctrl+N)">□ 새 파일</button>
376+
<button class="fbtn" onclick="saveFile()" title="저장 (Ctrl+S)">↓ 저장</button>
377+
<button class="fbtn" onclick="document.getElementById('file-open-inp').click()" title="열기 (Ctrl+O)">↑ 열기</button>
378+
</div>
379+
<input id="file-open-inp" type="file" accept=".han,.txt,.kcode" style="display:none" onchange="openFile(event)">
364380
<div id="wasm-badge">
365381
<span id="wasm-dot"></span>
366382
<span id="wasm-txt">WASM 로딩...</span>
@@ -979,8 +995,85 @@ <h3>🤖 AI 어시스턴트 선택</h3>
979995
}
980996

981997
/* ================================================================
982-
렌더 함수들
998+
파일 관리 — 새 파일 / 저장 / 열기
999+
================================================================ */
1000+
function newFile(){
1001+
if(ws.length>0){
1002+
if(!confirm('현재 작업을 모두 지우고 새 파일을 만드시겠습니까?'))return;
1003+
}
1004+
ws=[];selUid=null;consoleLog=[];errLog=[];
1005+
const ta=document.getElementById('text-area');
1006+
if(ta)ta.value='';
1007+
renderAll();
1008+
consoleLog.push({t:'info',s:'□ 새 파일'});
1009+
renderTabContent();
1010+
}
1011+
1012+
function saveFile(){
1013+
let content='';
1014+
let filename='main.han';
1015+
if(editorMode==='text'){
1016+
content=document.getElementById('text-area').value||'';
1017+
}else{
1018+
content=ws.map(b=>blockToHan(b,0)).join('');
1019+
}
1020+
const blob=new Blob([content],{type:'text/plain;charset=utf-8'});
1021+
const a=document.createElement('a');
1022+
a.href=URL.createObjectURL(blob);
1023+
a.download=filename;
1024+
a.click();
1025+
URL.revokeObjectURL(a.href);
1026+
consoleLog.push({t:'info',s:`↓ 저장: ${filename}`});
1027+
renderTabContent();
1028+
}
1029+
1030+
function openFile(e){
1031+
const file=e.target.files?.[0];if(!file)return;
1032+
const reader=new FileReader();
1033+
reader.onload=ev=>{
1034+
const content=ev.target.result||'';
1035+
/* 텍스트 모드로 전환하여 내용 표시 */
1036+
setMode('text');
1037+
document.getElementById('text-area').value=content;
1038+
consoleLog.push({t:'info',s:`↑ 열기: ${file.name}`});
1039+
renderTabContent();
1040+
};
1041+
reader.readAsText(file,'UTF-8');
1042+
e.target.value=''; /* 같은 파일 재선택 가능하도록 초기화 */
1043+
}
1044+
1045+
/* ================================================================
1046+
블록 삭제
1047+
================================================================ */
1048+
function deleteBlock(uid){
1049+
function remove(bls){return bls.filter(b=>b.uid!==uid).map(b=>({...b,children:remove(b.children||[])}));}
1050+
ws=remove(ws);
1051+
selUid=null;
1052+
renderAll();
1053+
}
1054+
1055+
/* ================================================================
1056+
키보드 단축키
9831057
================================================================ */
1058+
document.addEventListener('keydown',e=>{
1059+
const tag=document.activeElement?.tagName;
1060+
const isInput=tag==='INPUT'||tag==='TEXTAREA';
1061+
1062+
/* 블록 삭제: Delete / Backspace (입력창 제외) */
1063+
if(!isInput&&(e.key==='Delete'||e.key==='Backspace')&&selUid!=null){
1064+
e.preventDefault();
1065+
deleteBlock(selUid);
1066+
return;
1067+
}
1068+
/* Ctrl+N: 새 파일 */
1069+
if(e.ctrlKey&&e.key==='n'){e.preventDefault();newFile();return;}
1070+
/* Ctrl+S: 저장 */
1071+
if(e.ctrlKey&&e.key==='s'){e.preventDefault();saveFile();return;}
1072+
/* Ctrl+O: 열기 */
1073+
if(e.ctrlKey&&e.key==='o'){e.preventDefault();document.getElementById('file-open-inp').click();return;}
1074+
/* ESC: 선택 해제 */
1075+
if(!isInput&&e.key==='Escape'){selUid=null;renderAll();return;}
1076+
});
9841077
function renderMenus(){
9851078
const el=document.getElementById('menus');
9861079
el.style.cssText='display:flex;align-items:center;';el.innerHTML='';
@@ -1103,6 +1196,11 @@ <h3>🤖 AI 어시스턴트 선택</h3>
11031196
const dv=document.createElement('div');dv.className='prp-div';el.appendChild(dv);
11041197
const uid=document.createElement('div');uid.className='prp-uid';
11051198
uid.innerHTML=`<span>uid</span><span style="color:var(--dim);">#${sb.uid}</span>`;el.appendChild(uid);
1199+
/* 삭제 버튼 */
1200+
const del=document.createElement('button');del.className='prp-del';
1201+
del.textContent='✕ 블록 삭제';
1202+
del.onclick=()=>deleteBlock(sb.uid);
1203+
el.appendChild(del);
11061204
}
11071205

11081206
function renderAI(){
@@ -1448,13 +1546,31 @@ <h3>🤖 AI 어시스턴트 선택</h3>
14481546
const bh=document.getElementById('bot-resize');
14491547
if(bh){
14501548
let startY,startH;
1451-
bh.addEventListener('mousedown',e=>{
1452-
startY=e.clientY;startH=document.getElementById('bottom').getBoundingClientRect().height;
1453-
bh.classList.add('drag');document.body.style.cursor='row-resize';document.body.style.userSelect='none';
1454-
const onMove=e=>{const delta=startY-e.clientY;const newH=Math.max(60,Math.min(window.innerHeight*0.65,startH+delta));document.getElementById('bottom').style.height=newH+'px';};
1455-
const onUp=()=>{bh.classList.remove('drag');document.body.style.cursor='';document.body.style.userSelect='';document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);};
1456-
document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);
1457-
});
1549+
const startDrag=e=>{
1550+
e.preventDefault();
1551+
startY=e.clientY??(e.touches?.[0]?.clientY);
1552+
startH=document.getElementById('bottom').getBoundingClientRect().height;
1553+
bh.classList.add('drag');
1554+
document.body.style.cursor='row-resize';
1555+
document.body.style.userSelect='none';
1556+
};
1557+
const onMove=e=>{
1558+
const y=e.clientY??(e.touches?.[0]?.clientY);
1559+
const delta=startY-y;
1560+
const newH=Math.max(60,Math.min(window.innerHeight*0.70,startH+delta));
1561+
document.getElementById('bottom').style.height=newH+'px';
1562+
};
1563+
const onUp=()=>{
1564+
bh.classList.remove('drag');
1565+
document.body.style.cursor='';
1566+
document.body.style.userSelect='';
1567+
document.removeEventListener('mousemove',onMove);
1568+
document.removeEventListener('mouseup',onUp);
1569+
document.removeEventListener('touchmove',onMove);
1570+
document.removeEventListener('touchend',onUp);
1571+
};
1572+
bh.addEventListener('mousedown',e=>{startDrag(e);document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);});
1573+
bh.addEventListener('touchstart',e=>{startDrag(e);document.addEventListener('touchmove',onMove,{passive:false});document.addEventListener('touchend',onUp);},{passive:false});
14581574
}
14591575
}
14601576

0 commit comments

Comments
 (0)