Skip to content

Commit a42c698

Browse files
tmck-codeclaude
andcommitted
pikachu companion: move mark-all to toolbar, make order settings visible, fix back-view block framing
- replace #completeBox overlay with compact #markAllBtn toolbar toggle - block-order change re-indexes and re-frames the current block - draw inter-cluster hop lines in front view so route order is visible - gotoBlock mirrors columns in back view; flip keeps the same cells in view Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent c9baba4 commit a42c698

6 files changed

Lines changed: 64 additions & 37 deletions

File tree

pages/pikachu-stitch-companion/js/blocks.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ export function blockIsCompleteForColour(br, bc, v){
5757
* left on odd block-rows (boustrophedon), so consecutive blocks are
5858
* always adjacent.
5959
*/
60-
export function blockOrderList(v){
60+
export function blockOrderList(v, order = state.settings.blockOrder){
6161
const blocks = blocksForColour(v); // already row-major by br,bc
62-
if(state.settings.blockOrder === 'row-major') return blocks;
62+
if(order === 'row-major') return blocks;
6363
const byRow = new Map();
6464
for(const b of blocks){
6565
if(!byRow.has(b.br)) byRow.set(b.br, []);
@@ -84,7 +84,10 @@ export function gotoBlock(br, bc){
8484
const fit = Math.min(w,h) * 0.96 / blockPx;
8585
view.scale = fit;
8686
const s = view.base * view.scale; // effective cell size at the new scale
87-
const cx = (bc*BLOCK_SIZE + BLOCK_SIZE/2) * s;
87+
// back view mirrors columns (screenX = tx + N*s - c*s), so centre on the
88+
// mirrored column position — same block, seen from behind
89+
const colMid = bc*BLOCK_SIZE + BLOCK_SIZE/2;
90+
const cx = (view.backView ? N - colMid : colMid) * s;
8891
const cy = (br*BLOCK_SIZE + BLOCK_SIZE/2) * s;
8992
view.tx = w/2 - cx;
9093
view.ty = h/2 - cy;

pages/pikachu-stitch-companion/js/input.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// legend drag-to-scroll.
33

44
import { stage, view, applyZoom, clampView, draw, fit, cellAtClient, setSymbolsOn, symbolsOn } from './render.js';
5+
import { N } from './pattern.js';
56
import { state, colourAt, isStitched, setStitched, hasTieOff, setTieOff, logEvent } from './state.js';
67
import { markDirty } from './persistence.js';
78
import { refreshUI, getBlockIdx, setBlockIdx, updatePosReadout } from './ui.js';
@@ -39,6 +40,10 @@ const flipBtn = document.getElementById('flipBtn');
3940
const viewLabel = document.getElementById('viewLabel');
4041
flipBtn.addEventListener('click', ()=>{
4142
view.backView = !view.backView;
43+
// keep the same cells in view: the grid is mirrored about its own centre,
44+
// so the pan offset mirrors about the stage width
45+
view.tx = stage.clientWidth - view.tx - N*view.base*view.scale;
46+
clampView();
4247
flipBtn.classList.toggle('on', view.backView);
4348
flipBtn.setAttribute('aria-pressed', String(view.backView));
4449
viewLabel.textContent = view.backView ? 'Back' : 'Front';
@@ -147,7 +152,7 @@ const pts=new Map();
147152
let lastDist=0,lastMid=null,lastTap=0;
148153
stage.addEventListener('pointerdown',e=>{
149154
// only the canvas takes pan/zoom/marking gestures — other stage children
150-
// (#completeBox, #miniMap, …) keep their own pointer/click behaviour
155+
// (#miniMap, #blockCelebrate, …) keep their own pointer/click behaviour
151156
if (e.target && e.target.id !== 'cv') return;
152157
try { stage.setPointerCapture(e.pointerId); } catch(_) {}
153158
pts.set(e.pointerId,{x:e.clientX,y:e.clientY});

pages/pikachu-stitch-companion/js/render.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,17 @@ export function draw(){
181181
});
182182
ctx.stroke();
183183
}
184+
// hop lines: exit of one cluster -> entry of the next, so the visit
185+
// order (which settings like confetti-first reorder) is visible
186+
ctx.lineWidth=Math.max(0.8,s*0.04);
187+
ctx.setLineDash([s*0.2, s*0.15]);
188+
for (const hop of route.hops){
189+
const fx=colOf(hop.from)*s+s/2, fy=rowOf(hop.from)*s+s/2;
190+
const tx=colOf(hop.to)*s+s/2, ty=rowOf(hop.to)*s+s/2;
191+
ctx.strokeStyle = hop.kind==='carry' ? 'rgba(244,241,255,0.35)' : 'rgba(253,233,73,0.3)';
192+
ctx.beginPath(); ctx.moveTo(fx,fy); ctx.lineTo(tx,ty); ctx.stroke();
193+
}
194+
ctx.setLineDash([]);
184195
// hop markers: carry vs tie-off, distinct colour/shape
185196
for (const hop of route.hops){
186197
const tx=colOf(hop.to)*s+s/2, ty=rowOf(hop.to)*s+s/2;

pages/pikachu-stitch-companion/js/ui.js

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// ui.js - legend chip construction, refreshUI(), #doneCheck handler,
1+
// ui.js - legend chip construction, refreshUI(), #markAllBtn handler,
22
// #resetBtn wiring.
33

44
import { COLORS, TOTAL, CELL_COUNT, N } from './pattern.js';
@@ -30,19 +30,20 @@ COLORS.forEach((c,i)=>{
3030
});
3131

3232
/* ---------- complete toggle + progress ---------- */
33-
const box = document.getElementById('completeBox');
34-
const check = document.getElementById('doneCheck');
35-
const clabel = document.getElementById('completeLabel');
36-
check.addEventListener('change', ()=>{
33+
// toolbar "mark all" toggle: marks every cell of the selected colour as
34+
// stitched (or, when the colour is already complete, unmarks them all).
35+
const markAllBtn = document.getElementById('markAllBtn');
36+
markAllBtn.addEventListener('click', ()=>{
3737
if (state.selected==null) return;
3838
const v = state.selected;
39+
const on = !isColourComplete(v);
3940
const cellsToToggle = [];
4041
for (let i=0;i<CELL_COUNT;i++){
41-
if (colourAt(i)===v && isStitched(i)!==check.checked) cellsToToggle.push(i);
42+
if (colourAt(i)===v && isStitched(i)!==on) cellsToToggle.push(i);
4243
}
43-
cellsToToggle.forEach(i=>setStitched(i, check.checked));
44-
logEvent({kind:'bulk', c:v, cells:cellsToToggle, unmark: !check.checked});
45-
if (check.checked && isColourComplete(v)) celebrateBulk('Colour finished!');
44+
cellsToToggle.forEach(i=>setStitched(i, on));
45+
logEvent({kind:'bulk', c:v, cells:cellsToToggle, unmark: !on});
46+
if (on && isColourComplete(v)) celebrateBulk('Colour finished!');
4647
refreshUI(); draw();
4748
});
4849
// cheap reuse of input.js's celebration banner for the bulk "mark all" path (4.6)
@@ -68,12 +69,22 @@ export function refreshUI(){
6869
});
6970
if (state.selected!=null){
7071
const c = COLORS[state.selected-1];
71-
clabel.textContent = `Mark all of DMC ${c[0]} · ${c[1]} as stitched`;
72-
check.checked = isColourComplete(state.selected);
73-
box.classList.add('show');
72+
const complete = isColourComplete(state.selected);
73+
const lbl = complete
74+
? `Unmark all of DMC ${c[0]} · ${c[1]}`
75+
: `Mark all of DMC ${c[0]} · ${c[1]} as stitched`;
76+
markAllBtn.disabled = false;
77+
markAllBtn.classList.toggle('on', complete);
78+
markAllBtn.setAttribute('aria-pressed', String(complete));
79+
markAllBtn.setAttribute('aria-label', lbl);
80+
markAllBtn.title = lbl;
7481
document.getElementById('chip'+state.selected)
7582
.scrollIntoView({behavior:'smooth',inline:'center',block:'nearest'});
76-
} else box.classList.remove('show');
83+
} else {
84+
markAllBtn.disabled = true;
85+
markAllBtn.classList.remove('on');
86+
markAllBtn.setAttribute('aria-pressed','false');
87+
}
7788
const doneCount = state.stitchedCount.reduce((s,n)=>s+n,0);
7889
const pct = Math.round(doneCount/TOTAL*100);
7990
document.getElementById('pctNum').textContent = pct;
@@ -265,13 +276,25 @@ heatmapBtn.addEventListener('click', ()=>{
265276
// fresh one (6.3). The remaining settings (railroading, topLegDirection,
266277
// blockOrder) only affect drawing/labels or blockOrderList, so they just
267278
// redraw + refresh.
279+
let prevBlockOrder = state.settings.blockOrder;
268280
const ROUTE_AFFECTING_KEYS = new Set([
269281
'maxCarry', 'threadLength', 'fabricCount', 'strands',
270282
'origin', 'darkCarryGuard', 'confettiFirst',
271283
]);
272284

273285
function applySettingChange(key){
274286
markDirty();
287+
if (key==='blockOrder' && state.selected!=null){
288+
// keep the block currently in view, but re-index it into the new order so
289+
// prev/next walk the newly chosen sequence from here
290+
const oldList = blockOrderList(state.selected, prevBlockOrder);
291+
const cur = oldList[blockIdx];
292+
const newList = blockOrderList(state.selected);
293+
const ni = cur ? newList.findIndex(b=>b.br===cur.br && b.bc===cur.bc) : -1;
294+
blockIdx = ni>=0 ? ni : 0;
295+
const b = newList[blockIdx]; if (b) gotoBlock(b.br, b.bc);
296+
}
297+
prevBlockOrder = state.settings.blockOrder;
275298
if (ROUTE_AFFECTING_KEYS.has(key)){
276299
invalidateAllRoutes();
277300
if (state.selected!=null) requestRoute(state.selected);
@@ -297,6 +320,7 @@ const conservativeCarryBtn = document.getElementById('conservativeCarryBtn');
297320

298321
function fillSettingsForm(){
299322
const s = state.settings;
323+
prevBlockOrder = s.blockOrder; // settings may have been loaded from storage after module init
300324
setFabricCount.value = s.fabricCount;
301325
setStrands.value = s.strands;
302326
setMaxCarry.value = s.maxCarry;

pages/pikachu-stitch-companion/pikachu-stitch-companion.css

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,6 @@
101101
pointer-events:none;transition:opacity .6s;
102102
}
103103

104-
/* complete toggle */
105-
#completeBox{
106-
position:absolute;left:50%;bottom:14px;transform:translateX(-50%);
107-
display:none;align-items:center;gap:10px;
108-
background:var(--panel-2);
109-
border:1.5px solid var(--glow);
110-
border-radius:14px;
111-
padding:10px 16px;
112-
font-size:13px;font-weight:700;
113-
cursor:pointer;
114-
box-shadow:0 4px 24px rgba(0,0,0,.5), 0 0 18px rgba(253,233,73,.12);
115-
}
116-
#completeBox.show{display:flex}
117-
#completeBox input{
118-
width:20px;height:20px;accent-color:var(--done);cursor:pointer;
119-
}
120104

121105
/* mini-map (4.5) - overlays the stage, top-right, touch target only on itself */
122106
#miniMap{

pages/pikachu-stitch-companion/pikachu-stitch-companion.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ <h1><span class="spark">⚡</span> Glowing Pikachu</h1>
1717
<div id="stage">
1818
<canvas id="cv"></canvas>
1919
<div id="zoomHint">drag to pan · double-tap to zoom</div>
20-
<label id="completeBox">
21-
<input type="checkbox" id="doneCheck">
22-
<span id="completeLabel"></span>
23-
</label>
2420
<canvas id="miniMap" width="150" height="150"></canvas>
2521
<div id="blockCelebrate"></div>
2622
</div>
@@ -52,6 +48,10 @@ <h3>Cluster anchors</h3>
5248
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-15-6.7L3 13"/></svg>
5349
<span class="lbl">Undo</span>
5450
</button>
51+
<button id="markAllBtn" class="tbtn" type="button" disabled aria-label="Mark all of this colour as stitched" title="Mark all of this colour as stitched" aria-pressed="false">
52+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="3"/><polyline points="7,12.5 10.5,16 17,8.5"/></svg>
53+
<span class="lbl">Mark all</span>
54+
</button>
5555
<button id="setStartBtn" class="tbtn" type="button" disabled aria-label="Set start point" title="Set start point" aria-pressed="false">
5656
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="7"/><line x1="12" y1="1" x2="12" y2="5"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="1" y1="12" x2="5" y2="12"/><line x1="19" y1="12" x2="23" y2="12"/><circle cx="12" cy="12" r="1.5" fill="currentColor"/></svg>
5757
<span class="lbl">Set start</span>

0 commit comments

Comments
 (0)