chanmin0723 commited on
Commit
ae559f4
·
verified ·
1 Parent(s): 31a80a9

Upload index.html

Browse files

[feat] bigger pannel

Files changed (1) hide show
  1. index.html +211 -99
index.html CHANGED
@@ -196,23 +196,81 @@
196
  padding:16px;
197
  }
198
  .piece-head{
199
- display:flex; align-items:center; justify-content:space-between;
200
- margin-bottom:10px;
 
201
  }
202
  .piece-label{
203
  font-family:'Space Grotesk',sans-serif;
204
  font-weight:600;
205
  font-size:13px;
206
- display:flex; align-items:center; gap:8px;
 
 
 
 
 
 
207
  }
208
- .piece-dot{width:9px;height:9px;border-radius:50%;}
209
  .piece-clear{
210
  background:none; border:none; color:var(--muted-2);
211
  font-size:11px; cursor:pointer; font-family:'Inter',sans-serif;
212
  padding:2px 4px;
 
213
  }
214
  .piece-clear:hover{color:var(--ink);}
215
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
  /* ---- mode tabs (preset vs draw) ---- */
217
  .mode-tabs{
218
  display:flex;
@@ -244,8 +302,12 @@
244
  .mode-tab:focus-visible{outline:2px solid var(--clear); outline-offset:1px;}
245
 
246
  /* ---- preset picker ---- */
 
 
 
247
  .preset-panel{
248
- max-height:280px;
 
249
  overflow-y:auto;
250
  padding-right:4px;
251
  }
@@ -255,21 +317,23 @@
255
  letter-spacing:0.03em;
256
  text-transform:uppercase;
257
  color:var(--muted-2);
258
- margin:12px 0 6px;
259
  }
260
  .preset-group-label:first-child{margin-top:0;}
261
  .preset-group-grid{
262
- display:grid;
263
- grid-template-columns:repeat(4, 1fr);
264
- gap:6px;
 
265
  }
266
  .preset-thumb-btn{
267
- aspect-ratio:1/1;
 
268
  background:var(--panel-2);
269
  border:1.5px solid var(--grid-line);
270
  border-radius:8px;
271
  cursor:pointer;
272
- display:flex;
273
  align-items:center;
274
  justify-content:center;
275
  padding:6px;
@@ -283,12 +347,12 @@
283
  box-shadow:0 0 0 1px var(--piece-color, var(--clear)) inset;
284
  }
285
  .thumb-grid{
286
- width:100%;
287
- height:100%;
288
  display:grid;
289
- gap:1.5px;
290
  }
291
  .thumb-cell{
 
 
292
  background:transparent;
293
  border-radius:1.5px;
294
  }
@@ -481,39 +545,32 @@
481
  <aside class="pieces">
482
  <div class="piece-card">
483
  <div class="piece-head">
484
- <div class="piece-label"><span class="piece-dot" style="background:var(--piece-a)"></span>조각 A</div>
485
- <button class="piece-clear" data-clear="a">지우기</button>
486
- </div>
487
- <div class="mode-tabs" data-piece="a">
488
- <button class="mode-tab active" data-mode="preset" type="button">프리셋에서 선택</button>
489
- <button class="mode-tab" data-mode="draw" type="button">직접 그리기</button>
490
- </div>
491
- <div class="preset-panel" id="preset-a"></div>
492
- <div class="piece-grid" id="piece-a" style="--piece-color:var(--piece-a); display:none;"></div>
493
- </div>
494
- <div class="piece-card">
495
- <div class="piece-head">
496
- <div class="piece-label"><span class="piece-dot" style="background:var(--piece-b)"></span>조각 B</div>
497
- <button class="piece-clear" data-clear="b">지우기</button>
498
  </div>
499
- <div class="mode-tabs" data-piece="b">
500
- <button class="mode-tab active" data-mode="preset" type="button">프리셋에서 선택</button>
501
- <button class="mode-tab" data-mode="draw" type="button">직접 그리기</button>
502
- </div>
503
- <div class="preset-panel" id="preset-b"></div>
504
- <div class="piece-grid" id="piece-b" style="--piece-color:var(--piece-b); display:none;"></div>
505
- </div>
506
- <div class="piece-card">
507
- <div class="piece-head">
508
- <div class="piece-label"><span class="piece-dot" style="background:var(--piece-c)"></span>조각 C</div>
509
- <button class="piece-clear" data-clear="c">지우기</button>
 
 
 
510
  </div>
511
- <div class="mode-tabs" data-piece="c">
 
512
  <button class="mode-tab active" data-mode="preset" type="button">프리셋에서 선택</button>
513
  <button class="mode-tab" data-mode="draw" type="button">직접 그리기</button>
514
  </div>
515
- <div class="preset-panel" id="preset-c"></div>
516
- <div class="piece-grid" id="piece-c" style="--piece-color:var(--piece-c); display:none;"></div>
 
517
  </div>
518
 
519
  <div class="solve-row">
@@ -602,6 +659,7 @@
602
  };
603
  let pieceMode = {a:'preset', b:'preset', c:'preset'}; // 'preset' | 'draw'
604
  let selectedPreset = {a:null, b:null, c:null};
 
605
  let lastSolution = null; // {steps:[{piece,r,c,offsets,boardBeforeClear,clearRows,clearCols,boardAfterClear}], totalLines, finalEmpty}
606
 
607
  // ---- DOM refs ----
@@ -633,27 +691,26 @@
633
  boardCells.push(row);
634
  }
635
 
636
- // ---- build piece editors ----
637
- const pieceCellEls = {a:[], b:[], c:[]};
638
- pieceKeys.forEach(k=>{
639
- const el = document.getElementById('piece-'+k);
640
  for(let r=0;r<PIECE_GRID;r++){
641
  const rowEls=[];
642
  for(let c=0;c<PIECE_GRID;c++){
643
  const b = document.createElement('button');
644
  b.type='button';
645
  b.className='pcell';
646
- b.setAttribute('aria-label', `${pieceNameKo[k]} 조각 ${r+1},${c+1}`);
647
  b.dataset.role='piece';
648
- b.dataset.key=k;
649
  b.dataset.r=r;
650
  b.dataset.c=c;
651
  el.appendChild(b);
652
  rowEls.push(b);
653
  }
654
- pieceCellEls[k].push(rowEls);
655
  }
656
- });
657
 
658
  // ---- shared cell get/set (used by both click and drag-paint) ----
659
  function getCellValue(role, key, r, c){
@@ -668,7 +725,7 @@
668
  } else {
669
  selectedPreset[key] = null; // manual edit no longer matches any preset
670
  renderPiece(key);
671
- renderPresetPanel(key);
672
  }
673
  return true;
674
  }
@@ -748,13 +805,15 @@
748
  return {h:maxR+1, w:maxC+1};
749
  }
750
 
 
 
751
  function buildThumb(cells){
752
  const {h,w} = shapeBBox(cells);
753
  const filled = new Set(cells.map(([r,c])=>r+','+c));
754
  const grid = document.createElement('div');
755
  grid.className = 'thumb-grid';
756
- grid.style.gridTemplateColumns = `repeat(${w}, 1fr)`;
757
- grid.style.gridTemplateRows = `repeat(${h}, 1fr)`;
758
  for(let r=0;r<h;r++){
759
  for(let c=0;c<w;c++){
760
  const cell = document.createElement('div');
@@ -765,24 +824,33 @@
765
  return grid;
766
  }
767
 
768
- function applyPresetToGrid(k, shape){
769
- pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
770
- shape.cells.forEach(([r,c])=>{ pieceGrids[k][r][c] = 1; });
771
- selectedPreset[k] = shape.id;
 
 
 
 
 
 
772
  clearSolution();
773
- renderPiece(k);
774
- renderPresetPanel(k);
 
 
 
 
775
  }
776
 
777
- function renderPresetPanel(k){
778
- const container = document.getElementById('preset-'+k);
779
- container.querySelectorAll('.preset-thumb-btn').forEach(btn=>{
780
- btn.classList.toggle('selected', btn.dataset.presetId === selectedPreset[k]);
781
  });
782
  }
783
 
784
- function buildPresetPanel(k){
785
- const container = document.getElementById('preset-'+k);
786
  container.innerHTML = '';
787
  const groups = {};
788
  const groupOrder = [];
@@ -803,55 +871,88 @@
803
  btn.type = 'button';
804
  btn.className = 'preset-thumb-btn';
805
  btn.dataset.presetId = shape.id;
806
- btn.style.setProperty('--piece-color', pieceColorVar[k]);
807
- btn.setAttribute('aria-label', `${pieceNameKo[k]} 조각: ${groupName} 모양 선택`);
808
  btn.appendChild(buildThumb(shape.cells));
809
- btn.addEventListener('click', ()=> applyPresetToGrid(k, shape));
810
  row.appendChild(btn);
811
  });
812
  container.appendChild(row);
813
  });
814
  }
 
815
 
816
- pieceKeys.forEach(buildPresetPanel);
817
-
818
- // ---- mode tabs: 프리셋에서 선택 (default) vs 직접 그리기 ----
819
- document.querySelectorAll('.mode-tabs').forEach(tabsEl=>{
820
- const k = tabsEl.dataset.piece;
821
- const tabButtons = tabsEl.querySelectorAll('.mode-tab');
822
- tabButtons.forEach(tabBtn=>{
823
- tabBtn.addEventListener('click', ()=>{
824
- const mode = tabBtn.dataset.mode;
825
- if(pieceMode[k] === mode) return;
826
- pieceMode[k] = mode;
827
- tabButtons.forEach(b=> b.classList.toggle('active', b===tabBtn));
828
- document.getElementById('preset-'+k).style.display = mode==='preset' ? 'block' : 'none';
829
- document.getElementById('piece-'+k).style.display = mode==='draw' ? 'grid' : 'none';
830
- });
 
 
 
 
 
 
 
 
831
  });
 
 
 
 
 
 
 
 
 
 
 
832
  });
833
 
834
- document.querySelectorAll('.piece-clear').forEach(btn=>{
835
- btn.addEventListener('click', ()=>{
836
- const k = btn.dataset.clear;
837
- pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
838
- selectedPreset[k] = null;
839
- clearSolution();
840
- renderPiece(k);
841
- renderPresetPanel(k);
 
 
 
842
  });
843
  });
844
 
 
 
 
 
 
 
 
 
 
 
845
  document.getElementById('resetAllBtn').addEventListener('click', ()=>{
846
  board = Array.from({length:N}, ()=>Array(N).fill(0));
847
  pieceKeys.forEach(k=>{
848
  pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
849
  selectedPreset[k] = null;
 
850
  });
851
  clearSolution();
852
  renderBoard();
853
- pieceKeys.forEach(renderPiece);
854
- pieceKeys.forEach(renderPresetPanel);
855
  statusLine.textContent='';
856
  });
857
 
@@ -876,15 +977,24 @@
876
  }
877
  }
878
 
879
- function renderPiece(k){
 
880
  for(let r=0;r<PIECE_GRID;r++){
881
  for(let c=0;c<PIECE_GRID;c++){
882
- const el = pieceCellEls[k][r][c];
883
- el.classList.toggle('on', !!pieceGrids[k][r][c]);
 
 
884
  }
885
  }
886
  }
887
 
 
 
 
 
 
 
888
  function clearSolution(){
889
  lastSolution = null;
890
  resultsBlock.style.display='none';
@@ -893,7 +1003,8 @@
893
  }
894
 
895
  renderBoard();
896
- pieceKeys.forEach(renderPiece);
 
897
 
898
  // ---- shape trimming ----
899
  function trimShape(grid){
@@ -1153,11 +1264,12 @@
1153
  pieceKeys.forEach(k=>{
1154
  pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
1155
  selectedPreset[k] = null;
 
1156
  });
1157
  clearSolution();
1158
  renderBoard();
1159
- pieceKeys.forEach(renderPiece);
1160
- pieceKeys.forEach(renderPresetPanel);
1161
  statusLine.textContent = '이어서 다음 조각 3개를 선택해주세요.';
1162
  emptyNote.textContent = '아래에서 다음 세 조각을 고르고 해답 찾기를 누르면 여기 결과가 표시됩니다.';
1163
  }
 
196
  padding:16px;
197
  }
198
  .piece-head{
199
+ display:flex; align-items:flex-start; justify-content:space-between;
200
+ gap:10px;
201
+ margin-bottom:14px;
202
  }
203
  .piece-label{
204
  font-family:'Space Grotesk',sans-serif;
205
  font-weight:600;
206
  font-size:13px;
207
+ display:flex; flex-direction:column; gap:3px;
208
+ }
209
+ .hint-inline{
210
+ font-family:'Inter',sans-serif;
211
+ font-weight:400;
212
+ font-size:11px;
213
+ color:var(--muted-2);
214
  }
 
215
  .piece-clear{
216
  background:none; border:none; color:var(--muted-2);
217
  font-size:11px; cursor:pointer; font-family:'Inter',sans-serif;
218
  padding:2px 4px;
219
+ white-space:nowrap;
220
  }
221
  .piece-clear:hover{color:var(--ink);}
222
 
223
+ /* ---- slot row: pick which of A/B/C you're filling ---- */
224
+ .slot-row{
225
+ display:grid;
226
+ grid-template-columns:repeat(3, 1fr);
227
+ gap:8px;
228
+ margin-bottom:14px;
229
+ }
230
+ .slot-chip{
231
+ background:var(--panel-2);
232
+ border:1.5px solid var(--grid-line);
233
+ border-radius:10px;
234
+ padding:8px 6px;
235
+ cursor:pointer;
236
+ display:flex;
237
+ flex-direction:column;
238
+ align-items:center;
239
+ gap:6px;
240
+ transition:border-color .12s, box-shadow .12s, opacity .12s;
241
+ opacity:0.72;
242
+ }
243
+ .slot-chip:hover{border-color:var(--muted-2);}
244
+ .slot-chip:focus-visible{outline:2px solid var(--clear); outline-offset:2px;}
245
+ .slot-chip.active{
246
+ opacity:1;
247
+ border-color:var(--piece-color);
248
+ box-shadow:0 0 0 1px var(--piece-color) inset, 0 0 14px -4px var(--piece-color);
249
+ }
250
+ .slot-thumb-wrap{
251
+ width:100%;
252
+ height:34px;
253
+ display:flex;
254
+ align-items:center;
255
+ justify-content:center;
256
+ }
257
+ .slot-empty{
258
+ font-family:'JetBrains Mono',monospace;
259
+ font-size:11px;
260
+ color:var(--muted-2);
261
+ border:1.5px dashed var(--grid-line);
262
+ border-radius:6px;
263
+ width:26px; height:26px;
264
+ display:flex; align-items:center; justify-content:center;
265
+ }
266
+ .slot-chip.active .slot-empty{border-color:var(--piece-color); color:var(--piece-color);}
267
+ .slot-label{
268
+ font-family:'Space Grotesk',sans-serif;
269
+ font-weight:700;
270
+ font-size:12px;
271
+ color:var(--piece-color);
272
+ }
273
+
274
  /* ---- mode tabs (preset vs draw) ---- */
275
  .mode-tabs{
276
  display:flex;
 
302
  .mode-tab:focus-visible{outline:2px solid var(--clear); outline-offset:1px;}
303
 
304
  /* ---- preset picker ---- */
305
+ /* Cell size is FIXED (--cell-px) across every shape, so a thumbnail's overall
306
+ footprint scales with the piece's real size instead of every thumbnail
307
+ being squeezed into an identical box. */
308
  .preset-panel{
309
+ --cell-px:9px;
310
+ max-height:340px;
311
  overflow-y:auto;
312
  padding-right:4px;
313
  }
 
317
  letter-spacing:0.03em;
318
  text-transform:uppercase;
319
  color:var(--muted-2);
320
+ margin:14px 0 7px;
321
  }
322
  .preset-group-label:first-child{margin-top:0;}
323
  .preset-group-grid{
324
+ display:flex;
325
+ flex-wrap:wrap;
326
+ align-items:center;
327
+ gap:8px;
328
  }
329
  .preset-thumb-btn{
330
+ min-width:38px;
331
+ min-height:38px;
332
  background:var(--panel-2);
333
  border:1.5px solid var(--grid-line);
334
  border-radius:8px;
335
  cursor:pointer;
336
+ display:inline-flex;
337
  align-items:center;
338
  justify-content:center;
339
  padding:6px;
 
347
  box-shadow:0 0 0 1px var(--piece-color, var(--clear)) inset;
348
  }
349
  .thumb-grid{
 
 
350
  display:grid;
351
+ gap:2px;
352
  }
353
  .thumb-cell{
354
+ width:var(--cell-px);
355
+ height:var(--cell-px);
356
  background:transparent;
357
  border-radius:1.5px;
358
  }
 
545
  <aside class="pieces">
546
  <div class="piece-card">
547
  <div class="piece-head">
548
+ <div class="piece-label">조각 선택 <span class="hint-inline">같은 판에서 3번 클릭 → A, B, C 순서로 채워��요</span></div>
549
+ <button class="piece-clear" id="clearActiveBtn">지금 조각 지우기</button>
 
 
 
 
 
 
 
 
 
 
 
 
550
  </div>
551
+
552
+ <div class="slot-row" id="slotRow">
553
+ <button class="slot-chip active" data-slot="a" style="--piece-color:var(--piece-a)" type="button">
554
+ <span class="slot-thumb-wrap" id="slotThumb-a"><span class="slot-empty">A</span></span>
555
+ <span class="slot-label">A</span>
556
+ </button>
557
+ <button class="slot-chip" data-slot="b" style="--piece-color:var(--piece-b)" type="button">
558
+ <span class="slot-thumb-wrap" id="slotThumb-b"><span class="slot-empty">B</span></span>
559
+ <span class="slot-label">B</span>
560
+ </button>
561
+ <button class="slot-chip" data-slot="c" style="--piece-color:var(--piece-c)" type="button">
562
+ <span class="slot-thumb-wrap" id="slotThumb-c"><span class="slot-empty">C</span></span>
563
+ <span class="slot-label">C</span>
564
+ </button>
565
  </div>
566
+
567
+ <div class="mode-tabs" id="modeTabs">
568
  <button class="mode-tab active" data-mode="preset" type="button">프리셋에서 선택</button>
569
  <button class="mode-tab" data-mode="draw" type="button">직접 그리기</button>
570
  </div>
571
+
572
+ <div class="preset-panel" id="presetPanel"></div>
573
+ <div class="piece-grid" id="pieceGridEditor" style="display:none;"></div>
574
  </div>
575
 
576
  <div class="solve-row">
 
659
  };
660
  let pieceMode = {a:'preset', b:'preset', c:'preset'}; // 'preset' | 'draw'
661
  let selectedPreset = {a:null, b:null, c:null};
662
+ let activeSlot = 'a'; // which piece (a/b/c) the shared picker is currently filling
663
  let lastSolution = null; // {steps:[{piece,r,c,offsets,boardBeforeClear,clearRows,clearCols,boardAfterClear}], totalLines, finalEmpty}
664
 
665
  // ---- DOM refs ----
 
691
  boardCells.push(row);
692
  }
693
 
694
+ // ---- build the single shared 5x5 piece draw grid (used by whichever slot is active) ----
695
+ const pieceCellEls = [];
696
+ {
697
+ const el = document.getElementById('pieceGridEditor');
698
  for(let r=0;r<PIECE_GRID;r++){
699
  const rowEls=[];
700
  for(let c=0;c<PIECE_GRID;c++){
701
  const b = document.createElement('button');
702
  b.type='button';
703
  b.className='pcell';
 
704
  b.dataset.role='piece';
705
+ b.dataset.key='a'; // updated live to whichever slot is active
706
  b.dataset.r=r;
707
  b.dataset.c=c;
708
  el.appendChild(b);
709
  rowEls.push(b);
710
  }
711
+ pieceCellEls.push(rowEls);
712
  }
713
+ }
714
 
715
  // ---- shared cell get/set (used by both click and drag-paint) ----
716
  function getCellValue(role, key, r, c){
 
725
  } else {
726
  selectedPreset[key] = null; // manual edit no longer matches any preset
727
  renderPiece(key);
728
+ renderPresetSelection();
729
  }
730
  return true;
731
  }
 
805
  return {h:maxR+1, w:maxC+1};
806
  }
807
 
808
+ // Cell size is fixed (CSS var --cell-px on .preset-panel), so a shape's thumbnail
809
+ // grows/shrinks with its real footprint instead of every shape sharing one box size.
810
  function buildThumb(cells){
811
  const {h,w} = shapeBBox(cells);
812
  const filled = new Set(cells.map(([r,c])=>r+','+c));
813
  const grid = document.createElement('div');
814
  grid.className = 'thumb-grid';
815
+ grid.style.gridTemplateColumns = `repeat(${w}, var(--cell-px))`;
816
+ grid.style.gridTemplateRows = `repeat(${h}, var(--cell-px))`;
817
  for(let r=0;r<h;r++){
818
  for(let c=0;c<w;c++){
819
  const cell = document.createElement('div');
 
824
  return grid;
825
  }
826
 
827
+ const SLOT_ORDER = ['a','b','c'];
828
+
829
+ // apply a chosen preset shape to whichever slot is currently active, then
830
+ // auto-advance to the next slot (a -> b -> c) so 3 clicks in this one panel
831
+ // fill all three pieces in order.
832
+ function applyPresetToActiveSlot(shape){
833
+ const key = activeSlot;
834
+ pieceGrids[key] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
835
+ shape.cells.forEach(([r,c])=>{ pieceGrids[key][r][c] = 1; });
836
+ selectedPreset[key] = shape.id;
837
  clearSolution();
838
+ renderSlotChip(key);
839
+ renderPresetSelection();
840
+ const idx = SLOT_ORDER.indexOf(key);
841
+ if(idx < SLOT_ORDER.length - 1){
842
+ setActiveSlot(SLOT_ORDER[idx+1]);
843
+ }
844
  }
845
 
846
+ function renderPresetSelection(){
847
+ document.querySelectorAll('#presetPanel .preset-thumb-btn').forEach(btn=>{
848
+ btn.classList.toggle('selected', btn.dataset.presetId === selectedPreset[activeSlot]);
 
849
  });
850
  }
851
 
852
+ function buildPresetPanel(){
853
+ const container = document.getElementById('presetPanel');
854
  container.innerHTML = '';
855
  const groups = {};
856
  const groupOrder = [];
 
871
  btn.type = 'button';
872
  btn.className = 'preset-thumb-btn';
873
  btn.dataset.presetId = shape.id;
874
+ btn.setAttribute('aria-label', `${groupName} 모양 선택`);
 
875
  btn.appendChild(buildThumb(shape.cells));
876
+ btn.addEventListener('click', ()=> applyPresetToActiveSlot(shape));
877
  row.appendChild(btn);
878
  });
879
  container.appendChild(row);
880
  });
881
  }
882
+ buildPresetPanel();
883
 
884
+ // ---- slot chip preview (small thumbnail of whatever's currently in that slot) ----
885
+ function renderSlotChip(key){
886
+ const wrap = document.getElementById('slotThumb-'+key);
887
+ wrap.innerHTML = '';
888
+ const trimmed = trimShape(pieceGrids[key]);
889
+ if(trimmed){
890
+ wrap.appendChild(buildThumb(trimmed.offsets));
891
+ } else {
892
+ const span = document.createElement('span');
893
+ span.className = 'slot-empty';
894
+ span.textContent = pieceNameKo[key];
895
+ wrap.appendChild(span);
896
+ }
897
+ }
898
+
899
+ // ---- switch which slot (A/B/C) the shared picker + draw grid apply to ----
900
+ function setActiveSlot(key){
901
+ activeSlot = key;
902
+ document.querySelectorAll('.slot-chip').forEach(chip=>{
903
+ chip.classList.toggle('active', chip.dataset.slot===key);
904
+ });
905
+ document.querySelectorAll('#modeTabs .mode-tab').forEach(tabBtn=>{
906
+ tabBtn.classList.toggle('active', tabBtn.dataset.mode===pieceMode[key]);
907
  });
908
+ document.getElementById('presetPanel').style.display = pieceMode[key]==='preset' ? 'block' : 'none';
909
+ const editor = document.getElementById('pieceGridEditor');
910
+ editor.style.display = pieceMode[key]==='draw' ? 'grid' : 'none';
911
+ editor.style.setProperty('--piece-color', pieceColorVar[key]);
912
+ document.getElementById('presetPanel').style.setProperty('--piece-color', pieceColorVar[key]);
913
+ renderPresetSelection();
914
+ renderDrawGrid();
915
+ }
916
+
917
+ document.querySelectorAll('.slot-chip').forEach(chip=>{
918
+ chip.addEventListener('click', ()=> setActiveSlot(chip.dataset.slot));
919
  });
920
 
921
+ // ---- mode tabs: 프리셋에서 선택 (default) vs 직접 그리기, applies to the active slot ----
922
+ document.querySelectorAll('#modeTabs .mode-tab').forEach(tabBtn=>{
923
+ tabBtn.addEventListener('click', ()=>{
924
+ const mode = tabBtn.dataset.mode;
925
+ if(pieceMode[activeSlot] === mode) return;
926
+ pieceMode[activeSlot] = mode;
927
+ document.querySelectorAll('#modeTabs .mode-tab').forEach(b=> b.classList.toggle('active', b===tabBtn));
928
+ document.getElementById('presetPanel').style.display = mode==='preset' ? 'block' : 'none';
929
+ const editor = document.getElementById('pieceGridEditor');
930
+ editor.style.display = mode==='draw' ? 'grid' : 'none';
931
+ if(mode==='draw') renderDrawGrid();
932
  });
933
  });
934
 
935
+ document.getElementById('clearActiveBtn').addEventListener('click', ()=>{
936
+ const key = activeSlot;
937
+ pieceGrids[key] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
938
+ selectedPreset[key] = null;
939
+ clearSolution();
940
+ renderSlotChip(key);
941
+ renderPresetSelection();
942
+ renderDrawGrid();
943
+ });
944
+
945
  document.getElementById('resetAllBtn').addEventListener('click', ()=>{
946
  board = Array.from({length:N}, ()=>Array(N).fill(0));
947
  pieceKeys.forEach(k=>{
948
  pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
949
  selectedPreset[k] = null;
950
+ pieceMode[k] = 'preset';
951
  });
952
  clearSolution();
953
  renderBoard();
954
+ pieceKeys.forEach(renderSlotChip);
955
+ setActiveSlot('a');
956
  statusLine.textContent='';
957
  });
958
 
 
977
  }
978
  }
979
 
980
+ // refresh the single shared draw grid to show the active slot's shape
981
+ function renderDrawGrid(){
982
  for(let r=0;r<PIECE_GRID;r++){
983
  for(let c=0;c<PIECE_GRID;c++){
984
+ const el = pieceCellEls[r][c];
985
+ el.dataset.key = activeSlot;
986
+ el.setAttribute('aria-label', `${pieceNameKo[activeSlot]} 조각 ${r+1},${c+1}`);
987
+ el.classList.toggle('on', !!pieceGrids[activeSlot][r][c]);
988
  }
989
  }
990
  }
991
 
992
+ // called after a manual (draw-mode) edit changes pieceGrids[key]
993
+ function renderPiece(key){
994
+ renderDrawGrid();
995
+ renderSlotChip(key);
996
+ }
997
+
998
  function clearSolution(){
999
  lastSolution = null;
1000
  resultsBlock.style.display='none';
 
1003
  }
1004
 
1005
  renderBoard();
1006
+ pieceKeys.forEach(renderSlotChip);
1007
+ setActiveSlot('a');
1008
 
1009
  // ---- shape trimming ----
1010
  function trimShape(grid){
 
1264
  pieceKeys.forEach(k=>{
1265
  pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
1266
  selectedPreset[k] = null;
1267
+ pieceMode[k] = 'preset';
1268
  });
1269
  clearSolution();
1270
  renderBoard();
1271
+ pieceKeys.forEach(renderSlotChip);
1272
+ setActiveSlot('a');
1273
  statusLine.textContent = '이어서 다음 조각 3개를 선택해주세요.';
1274
  emptyNote.textContent = '아래에서 다음 세 조각을 고르고 해답 찾기를 누르면 여기 결과가 표시됩니다.';
1275
  }