치지직 채널 ID로 입장하게 변경 및 일부 판 커스텀 설정 추가.
This commit is contained in:
@@ -83,6 +83,7 @@ function boardForExport(board: BoardConfig): BoardConfig {
|
|||||||
type: cell.type,
|
type: cell.type,
|
||||||
eventId: cell.eventId ?? null,
|
eventId: cell.eventId ?? null,
|
||||||
imageSrc: null,
|
imageSrc: null,
|
||||||
|
contentVisible: cell.contentVisible !== false,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export type BoardCellConfig = {
|
|||||||
eventId?: string | null;
|
eventId?: string | null;
|
||||||
/** 칸 전용 이미지 (data URL 또는 상대 경로) */
|
/** 칸 전용 이미지 (data URL 또는 상대 경로) */
|
||||||
imageSrc?: string | null;
|
imageSrc?: string | null;
|
||||||
|
/** false면 게임에서 이름·이미지를 숨김 (기본 true) */
|
||||||
|
contentVisible?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BoardConfig = {
|
export type BoardConfig = {
|
||||||
@@ -117,9 +119,18 @@ export function createRandomBoardConfig(
|
|||||||
const geo = buildPerimeterGeometry(w, h);
|
const geo = buildPerimeterGeometry(w, h);
|
||||||
const specials = defaultSpecials(w, h);
|
const specials = defaultSpecials(w, h);
|
||||||
const cells: BoardCellConfig[] = geo.map((_, index) => {
|
const cells: BoardCellConfig[] = geo.map((_, index) => {
|
||||||
if (index === 0) return { type: '시작', eventId: null, imageSrc: null };
|
if (index === 0) {
|
||||||
if (specials[index]) return { ...specials[index], imageSrc: null };
|
return { type: '시작', eventId: null, imageSrc: null, contentVisible: true };
|
||||||
return { type: randomPresetType(), eventId: null, imageSrc: null };
|
}
|
||||||
|
if (specials[index]) {
|
||||||
|
return { ...specials[index], imageSrc: null, contentVisible: true };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: randomPresetType(),
|
||||||
|
eventId: null,
|
||||||
|
imageSrc: null,
|
||||||
|
contentVisible: true,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
return { version: BOARD_VERSION, width: w, height: h, cells };
|
return { version: BOARD_VERSION, width: w, height: h, cells };
|
||||||
}
|
}
|
||||||
@@ -136,7 +147,8 @@ function sanitizeCell(src: BoardCellConfig | undefined, fallback: BoardCellConfi
|
|||||||
: String(src.eventId);
|
: String(src.eventId);
|
||||||
const imageSrc =
|
const imageSrc =
|
||||||
typeof src.imageSrc === 'string' && src.imageSrc.length > 0 ? src.imageSrc : null;
|
typeof src.imageSrc === 'string' && src.imageSrc.length > 0 ? src.imageSrc : null;
|
||||||
return { type: src.type.trim(), eventId, imageSrc };
|
const contentVisible = src.contentVisible !== false;
|
||||||
|
return { type: src.type.trim(), eventId, imageSrc, contentVisible };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeBoardConfig(input: BoardConfig | null | undefined): BoardConfig {
|
export function normalizeBoardConfig(input: BoardConfig | null | undefined): BoardConfig {
|
||||||
@@ -154,7 +166,12 @@ export function normalizeBoardConfig(input: BoardConfig | null | undefined): Boa
|
|||||||
cells.push(sanitizeCell(input.cells[i], base.cells[i]));
|
cells.push(sanitizeCell(input.cells[i], base.cells[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
cells[0] = { type: '시작', eventId: null, imageSrc: cells[0]?.imageSrc ?? null };
|
cells[0] = {
|
||||||
|
type: '시작',
|
||||||
|
eventId: null,
|
||||||
|
imageSrc: cells[0]?.imageSrc ?? null,
|
||||||
|
contentVisible: cells[0]?.contentVisible !== false,
|
||||||
|
};
|
||||||
return { version: BOARD_VERSION, width, height, cells };
|
return { version: BOARD_VERSION, width, height, cells };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +244,7 @@ export function resizeBoardConfig(
|
|||||||
type: '시작',
|
type: '시작',
|
||||||
eventId: null,
|
eventId: null,
|
||||||
imageSrc: prev.cells[0]?.imageSrc ?? null,
|
imageSrc: prev.cells[0]?.imageSrc ?? null,
|
||||||
|
contentVisible: prev.cells[0]?.contentVisible !== false,
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -242,6 +260,7 @@ export function resizeBoardConfig(
|
|||||||
type: src?.type && src.type !== '시작' ? src.type : randomPresetType(),
|
type: src?.type && src.type !== '시작' ? src.type : randomPresetType(),
|
||||||
eventId: src?.eventId ?? null,
|
eventId: src?.eventId ?? null,
|
||||||
imageSrc: src?.imageSrc ?? null,
|
imageSrc: src?.imageSrc ?? null,
|
||||||
|
contentVisible: src?.contentVisible !== false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +277,10 @@ export function updateBoardCell(
|
|||||||
if (index < 0 || index >= current.cells.length) return current;
|
if (index < 0 || index >= current.cells.length) return current;
|
||||||
const cells = current.cells.map((cell, i) => {
|
const cells = current.cells.map((cell, i) => {
|
||||||
if (i !== index) return cell;
|
if (i !== index) return cell;
|
||||||
|
const contentVisible =
|
||||||
|
patch.contentVisible === undefined
|
||||||
|
? cell.contentVisible !== false
|
||||||
|
: Boolean(patch.contentVisible);
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
return {
|
return {
|
||||||
type: '시작',
|
type: '시작',
|
||||||
@@ -268,6 +291,7 @@ export function updateBoardCell(
|
|||||||
: patch.imageSrc === ''
|
: patch.imageSrc === ''
|
||||||
? null
|
? null
|
||||||
: patch.imageSrc,
|
: patch.imageSrc,
|
||||||
|
contentVisible,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -284,6 +308,7 @@ export function updateBoardCell(
|
|||||||
: patch.imageSrc === ''
|
: patch.imageSrc === ''
|
||||||
? null
|
? null
|
||||||
: patch.imageSrc,
|
: patch.imageSrc,
|
||||||
|
contentVisible,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const next = {
|
const next = {
|
||||||
@@ -296,6 +321,25 @@ export function updateBoardCell(
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 모든 칸의 내용 표시 여부를 한 번에 설정 */
|
||||||
|
export function setAllCellsContentVisible(
|
||||||
|
visible: boolean,
|
||||||
|
current = getOrCreateBoardConfig(),
|
||||||
|
): BoardConfig {
|
||||||
|
const cells = current.cells.map((cell) => ({
|
||||||
|
...cell,
|
||||||
|
contentVisible: visible,
|
||||||
|
}));
|
||||||
|
const next = {
|
||||||
|
version: BOARD_VERSION,
|
||||||
|
width: current.width,
|
||||||
|
height: current.height,
|
||||||
|
cells,
|
||||||
|
};
|
||||||
|
writeStoredBoard(next);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
export function buildPlaneCells(config: BoardConfig = getOrCreateBoardConfig()): PlaneCell[] {
|
export function buildPlaneCells(config: BoardConfig = getOrCreateBoardConfig()): PlaneCell[] {
|
||||||
const normalized = normalizeBoardConfig(config);
|
const normalized = normalizeBoardConfig(config);
|
||||||
const geo = buildPerimeterGeometry(normalized.width, normalized.height);
|
const geo = buildPerimeterGeometry(normalized.width, normalized.height);
|
||||||
@@ -306,10 +350,19 @@ export function buildPlaneCells(config: BoardConfig = getOrCreateBoardConfig()):
|
|||||||
type: normalized.cells[index]?.type ?? (index === 0 ? '시작' : '한잔'),
|
type: normalized.cells[index]?.type ?? (index === 0 ? '시작' : '한잔'),
|
||||||
eventId: index === 0 ? null : (normalized.cells[index]?.eventId ?? null),
|
eventId: index === 0 ? null : (normalized.cells[index]?.eventId ?? null),
|
||||||
imageSrc: normalized.cells[index]?.imageSrc ?? null,
|
imageSrc: normalized.cells[index]?.imageSrc ?? null,
|
||||||
|
contentVisible: normalized.cells[index]?.contentVisible !== false,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cellColorFor(eventId: string | null | undefined, index: number) {
|
export function cellColorFor(
|
||||||
|
eventId: string | null | undefined,
|
||||||
|
index: number,
|
||||||
|
options?: { contentVisible?: boolean },
|
||||||
|
) {
|
||||||
|
// 내용 숨김이면 종류가 색으로도 드러나지 않게 통일
|
||||||
|
if (options?.contentVisible === false && index !== 0) {
|
||||||
|
return '#be123c';
|
||||||
|
}
|
||||||
if (index === 0) return '#059669';
|
if (index === 0) return '#059669';
|
||||||
switch (eventId) {
|
switch (eventId) {
|
||||||
case 'island':
|
case 'island':
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export type PlaneCell = {
|
|||||||
eventId?: string | null;
|
eventId?: string | null;
|
||||||
/** 칸 이미지 (없으면 텍스트만) */
|
/** 칸 이미지 (없으면 텍스트만) */
|
||||||
imageSrc?: string | null;
|
imageSrc?: string | null;
|
||||||
|
/** false면 게임 판에서 이름·이미지를 숨김 */
|
||||||
|
contentVisible?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TurnContext = {
|
export type TurnContext = {
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import { getAssets } from './asset-storage';
|
|||||||
import {
|
import {
|
||||||
buildPlaneCells,
|
buildPlaneCells,
|
||||||
cellColorFor,
|
cellColorFor,
|
||||||
|
DEFAULT_PLANE_SIZE,
|
||||||
diceAreaSpan,
|
diceAreaSpan,
|
||||||
getOrCreateBoardConfig,
|
getOrCreateBoardConfig,
|
||||||
|
perimeterLength,
|
||||||
} from './board-config';
|
} from './board-config';
|
||||||
|
|
||||||
const STEP_MS = 280;
|
const STEP_MS = 280;
|
||||||
@@ -34,15 +36,13 @@ export default function DicePlane({
|
|||||||
chatFeed?: ChatFeedItem[];
|
chatFeed?: ChatFeedItem[];
|
||||||
showChatFeed?: boolean;
|
showChatFeed?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const initial = getOrCreateBoardConfig();
|
const [boardReady, setBoardReady] = useState(false);
|
||||||
const [planeSize, setPlaneSize] = useState({
|
const [planeSize, setPlaneSize] = useState<{ width: number; height: number }>({
|
||||||
width: initial.width,
|
width: DEFAULT_PLANE_SIZE.width,
|
||||||
height: initial.height,
|
height: DEFAULT_PLANE_SIZE.height,
|
||||||
});
|
});
|
||||||
const [dicePlaneArray, setDicePlaneArray] = useState<PlaneCell[]>(() =>
|
const [dicePlaneArray, setDicePlaneArray] = useState<PlaneCell[]>([]);
|
||||||
buildPlaneCells(initial),
|
const chessmanRef = useRef(new Chessman(perimeterLength(DEFAULT_PLANE_SIZE.width, DEFAULT_PLANE_SIZE.height)));
|
||||||
);
|
|
||||||
const chessmanRef = useRef(new Chessman(initial.cells.length));
|
|
||||||
const boardRef = useRef(dicePlaneArray);
|
const boardRef = useRef(dicePlaneArray);
|
||||||
const [chessmanIndex, setChessmanIndex] = useState(0);
|
const [chessmanIndex, setChessmanIndex] = useState(0);
|
||||||
const [chessmanSrc, setChessmanSrc] = useState('/chessman/default.png');
|
const [chessmanSrc, setChessmanSrc] = useState('/chessman/default.png');
|
||||||
@@ -59,6 +59,7 @@ export default function DicePlane({
|
|||||||
const [islandTurnsLeft, setIslandTurnsLeft] = useState(0);
|
const [islandTurnsLeft, setIslandTurnsLeft] = useState(0);
|
||||||
const moveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const moveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
|
// SSR/초기 렌더에서 랜덤 판을 쓰지 않고, 브라우저에 저장된 설정을 로드
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const board = getOrCreateBoardConfig();
|
const board = getOrCreateBoardConfig();
|
||||||
const cells = buildPlaneCells(board);
|
const cells = buildPlaneCells(board);
|
||||||
@@ -72,6 +73,7 @@ export default function DicePlane({
|
|||||||
setStatusText('시계방향');
|
setStatusText('시계방향');
|
||||||
setIslandTurnsLeft(0);
|
setIslandTurnsLeft(0);
|
||||||
setDicePlaneArray(cells);
|
setDicePlaneArray(cells);
|
||||||
|
setBoardReady(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -175,6 +177,25 @@ export default function DicePlane({
|
|||||||
const chessmanCell = dicePlaneArray[chessmanIndex] ?? dicePlaneArray[0];
|
const chessmanCell = dicePlaneArray[chessmanIndex] ?? dicePlaneArray[0];
|
||||||
const diceSpan = diceAreaSpan(planeSize.width, planeSize.height);
|
const diceSpan = diceAreaSpan(planeSize.width, planeSize.height);
|
||||||
|
|
||||||
|
if (!boardReady || dicePlaneArray.length === 0) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: 'rgba(255,255,255,0.7)',
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
판 불러오는 중…
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
|
||||||
<div
|
<div
|
||||||
@@ -192,6 +213,7 @@ export default function DicePlane({
|
|||||||
{dicePlaneArray.map((plane, index) => {
|
{dicePlaneArray.map((plane, index) => {
|
||||||
const isStart = index === 0;
|
const isStart = index === 0;
|
||||||
const isLanded = !moving && index === chessmanIndex && landedLabel != null;
|
const isLanded = !moving && index === chessmanIndex && landedLabel != null;
|
||||||
|
const showContent = plane.contentVisible !== false;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={plane.id}
|
key={plane.id}
|
||||||
@@ -201,8 +223,11 @@ export default function DicePlane({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
backgroundColor: cellColorFor(plane.eventId, index),
|
backgroundColor: cellColorFor(plane.eventId, index, {
|
||||||
backgroundImage: plane.imageSrc ? `url(${plane.imageSrc})` : undefined,
|
contentVisible: showContent,
|
||||||
|
}),
|
||||||
|
backgroundImage:
|
||||||
|
showContent && plane.imageSrc ? `url(${plane.imageSrc})` : undefined,
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
@@ -219,10 +244,13 @@ export default function DicePlane({
|
|||||||
transform: isLanded ? 'scale(1.04)' : undefined,
|
transform: isLanded ? 'scale(1.04)' : undefined,
|
||||||
transition: 'box-shadow 180ms ease, transform 180ms ease',
|
transition: 'box-shadow 180ms ease, transform 180ms ease',
|
||||||
zIndex: isLanded ? 4 : undefined,
|
zIndex: isLanded ? 4 : undefined,
|
||||||
textShadow: plane.imageSrc ? '0 1px 3px rgba(0,0,0,0.85)' : undefined,
|
textShadow:
|
||||||
|
showContent && plane.imageSrc
|
||||||
|
? '0 1px 3px rgba(0,0,0,0.85)'
|
||||||
|
: undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isStart ? '시작' : plane.type}
|
{showContent ? (isStart ? '시작' : plane.type) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
67
app/page.tsx
67
app/page.tsx
@@ -40,6 +40,8 @@ import {
|
|||||||
perimeterLength,
|
perimeterLength,
|
||||||
randomizeAndSaveBoard,
|
randomizeAndSaveBoard,
|
||||||
resizeBoardConfig,
|
resizeBoardConfig,
|
||||||
|
setAllCellsContentVisible,
|
||||||
|
setBoardConfig,
|
||||||
updateBoardCell,
|
updateBoardCell,
|
||||||
} from './game/board-config';
|
} from './game/board-config';
|
||||||
|
|
||||||
@@ -145,9 +147,25 @@ function BoardEditor({
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="text-sm font-semibold text-white">주사위판 칸 (board)</h3>
|
<h3 className="text-sm font-semibold text-white">주사위판 칸 (board)</h3>
|
||||||
<p className="text-[11px] text-white/50">
|
<p className="text-[11px] text-white/50">
|
||||||
가로·세로로 판 크기를 바꾸고, 칸마다 이름·효과·이미지를 설정합니다. 0번(시작) 이름은 고정입니다.
|
가로·세로로 판 크기를 바꾸고, 칸마다 이름·효과·이미지·내용 표시를 설정합니다. 0번(시작) 이름은
|
||||||
|
고정입니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-wrap gap-1.5">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="rounded bg-white/15 px-3 py-1.5 text-xs font-medium text-white hover:bg-white/25"
|
||||||
|
onClick={() => onChange(setAllCellsContentVisible(true, board))}
|
||||||
|
>
|
||||||
|
전체 내용 표시
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="rounded bg-white/15 px-3 py-1.5 text-xs font-medium text-white hover:bg-white/25"
|
||||||
|
onClick={() => onChange(setAllCellsContentVisible(false, board))}
|
||||||
|
>
|
||||||
|
전체 내용 숨김
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded bg-violet-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-violet-600"
|
className="rounded bg-violet-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-violet-600"
|
||||||
@@ -156,6 +174,7 @@ function BoardEditor({
|
|||||||
랜덤 다시 배치
|
랜덤 다시 배치
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap items-end gap-3 rounded-lg bg-black/25 p-3">
|
<div className="flex flex-wrap items-end gap-3 rounded-lg bg-black/25 p-3">
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
@@ -195,26 +214,35 @@ function BoardEditor({
|
|||||||
{geo.map((g, index) => {
|
{geo.map((g, index) => {
|
||||||
const cell = board.cells[index];
|
const cell = board.cells[index];
|
||||||
const active = selected === index;
|
const active = selected === index;
|
||||||
|
const showContent = cell?.contentVisible !== false;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={g.id}
|
key={g.id}
|
||||||
type="button"
|
type="button"
|
||||||
title={`#${index} ${cell?.type ?? ''}`}
|
title={`#${index} ${cell?.type ?? ''}${showContent ? '' : ' (내용 숨김)'}`}
|
||||||
onClick={() => setSelected(index)}
|
onClick={() => setSelected(index)}
|
||||||
className="min-h-10 rounded px-0.5 text-[10px] font-semibold text-white sm:text-[11px]"
|
className="relative min-h-10 rounded px-0.5 text-[10px] font-semibold text-white sm:text-[11px]"
|
||||||
style={{
|
style={{
|
||||||
gridColumn: g.x + 1,
|
gridColumn: g.x + 1,
|
||||||
gridRow: g.y + 1,
|
gridRow: g.y + 1,
|
||||||
backgroundColor: cellColorFor(cell?.eventId, index),
|
backgroundColor: cellColorFor(cell?.eventId, index),
|
||||||
backgroundImage: cell?.imageSrc ? `url(${cell.imageSrc})` : undefined,
|
backgroundImage:
|
||||||
|
showContent && cell?.imageSrc ? `url(${cell.imageSrc})` : undefined,
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
backgroundPosition: 'center',
|
backgroundPosition: 'center',
|
||||||
textShadow: cell?.imageSrc ? '0 1px 2px rgba(0,0,0,0.9)' : undefined,
|
textShadow:
|
||||||
|
showContent && cell?.imageSrc ? '0 1px 2px rgba(0,0,0,0.9)' : undefined,
|
||||||
outline: active ? '2px solid #fde68a' : undefined,
|
outline: active ? '2px solid #fde68a' : undefined,
|
||||||
outlineOffset: 1,
|
outlineOffset: 1,
|
||||||
|
opacity: showContent ? 1 : 0.7,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{index === 0 ? '시작' : cell?.type}
|
{index === 0 ? '시작' : cell?.type}
|
||||||
|
{!showContent && (
|
||||||
|
<span className="absolute top-0.5 right-0.5 rounded bg-black/55 px-0.5 text-[8px] font-normal text-white/70">
|
||||||
|
숨김
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -262,6 +290,27 @@ function BoardEditor({
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col gap-1 sm:col-span-2">
|
||||||
|
<label className="inline-flex items-center gap-2 text-sm text-white/85">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectedCell.contentVisible !== false}
|
||||||
|
onChange={(e) =>
|
||||||
|
onChange(
|
||||||
|
updateBoardCell(
|
||||||
|
selected,
|
||||||
|
{ contentVisible: e.target.checked },
|
||||||
|
board,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
게임에서 이 칸 내용(이름·이미지) 표시
|
||||||
|
</label>
|
||||||
|
<p className="text-[11px] text-white/45">
|
||||||
|
끄면 판에는 색만 보이고, 착지 시 효과·안내는 그대로 동작합니다.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col gap-2 sm:col-span-2">
|
<div className="flex flex-col gap-2 sm:col-span-2">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<label className="text-xs text-white/60">
|
<label className="text-xs text-white/60">
|
||||||
@@ -621,7 +670,13 @@ export default function Home() {
|
|||||||
<p className="rounded bg-red-900/70 px-3 py-2 text-sm text-white">{error}</p>
|
<p className="rounded bg-red-900/70 px-3 py-2 text-sm text-white">{error}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<BoardEditor board={board} onChange={setBoard} />
|
<BoardEditor
|
||||||
|
board={board}
|
||||||
|
onChange={(next) => {
|
||||||
|
setBoardConfig(next);
|
||||||
|
setBoard(next);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
<AssetThumb
|
<AssetThumb
|
||||||
|
|||||||
Reference in New Issue
Block a user