@littlepartytime/dev-kit 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -41,7 +41,7 @@ export default function PhoneFrame({ children }: { children: React.ReactNode })
|
|
|
41
41
|
<div
|
|
42
42
|
ref={containerRef}
|
|
43
43
|
className="flex items-center justify-center h-full overflow-hidden"
|
|
44
|
-
style={{ minWidth: 0, minHeight: 0 }}
|
|
44
|
+
style={{ flex: 1, minWidth: 0, minHeight: 0 }}
|
|
45
45
|
>
|
|
46
46
|
{/* Wrapper sized to the scaled phone for correct layout flow */}
|
|
47
47
|
<div style={{ width: BODY_W * scale, height: BODY_H * scale, flexShrink: 0 }}>
|
|
@@ -160,10 +160,28 @@ export default function Preview() {
|
|
|
160
160
|
}
|
|
161
161
|
}, [playerCount, playerIndex]);
|
|
162
162
|
|
|
163
|
+
// Resizable split panel
|
|
164
|
+
const [splitRatio, setSplitRatio] = useState(0.6); // left column gets 60%
|
|
165
|
+
const panelRef = useRef<HTMLDivElement>(null);
|
|
166
|
+
const dragging = useRef(false);
|
|
167
|
+
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
const onMove = (e: MouseEvent) => {
|
|
170
|
+
if (!dragging.current || !panelRef.current) return;
|
|
171
|
+
const rect = panelRef.current.getBoundingClientRect();
|
|
172
|
+
const ratio = (e.clientX - rect.left) / rect.width;
|
|
173
|
+
setSplitRatio(Math.min(0.8, Math.max(0.2, ratio)));
|
|
174
|
+
};
|
|
175
|
+
const onUp = () => { dragging.current = false; document.body.style.cursor = ''; document.body.style.userSelect = ''; };
|
|
176
|
+
window.addEventListener('mousemove', onMove);
|
|
177
|
+
window.addEventListener('mouseup', onUp);
|
|
178
|
+
return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
|
|
179
|
+
}, []);
|
|
180
|
+
|
|
163
181
|
return (
|
|
164
182
|
<div className="flex gap-4 h-[calc(100vh-80px)]">
|
|
165
|
-
{/* Renderer —
|
|
166
|
-
<div className="
|
|
183
|
+
{/* Renderer — half the screen width */}
|
|
184
|
+
<div className="h-full" style={{ width: '50%' }}>
|
|
167
185
|
<PhoneFrame>
|
|
168
186
|
{GameRenderer && platform && viewState ? (
|
|
169
187
|
<GameRenderer platform={platform} state={viewState} />
|
|
@@ -175,10 +193,10 @@ export default function Preview() {
|
|
|
175
193
|
</PhoneFrame>
|
|
176
194
|
</div>
|
|
177
195
|
|
|
178
|
-
{/* Control Panel — fills remaining width, two-column
|
|
179
|
-
<div className="flex-1 min-w-0
|
|
196
|
+
{/* Control Panel — fills remaining width, resizable two-column */}
|
|
197
|
+
<div ref={panelRef} className="flex-1 min-w-0 flex h-full">
|
|
180
198
|
{/* Left column: Players & Controls */}
|
|
181
|
-
<div className="flex flex-col gap-4">
|
|
199
|
+
<div className="flex flex-col gap-4 overflow-auto pr-1" style={{ width: `${splitRatio * 100}%` }}>
|
|
182
200
|
{/* Player Count */}
|
|
183
201
|
<div className="bg-zinc-900 rounded-lg p-3">
|
|
184
202
|
<h3 className="text-sm font-bold text-zinc-400 mb-2">Player Count</h3>
|
|
@@ -200,9 +218,25 @@ export default function Preview() {
|
|
|
200
218
|
const isActive = i === playerIndex;
|
|
201
219
|
const hue = (i * 137) % 360; // deterministic color per player
|
|
202
220
|
const playerState = fullState?.players?.find((ps: any) => ps.id === p.id);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
221
|
+
// Fallback chain: 1) PlayerState field 2) GameState.data mapping table
|
|
222
|
+
const ROLE_KEYS = ['role', 'character', 'team', 'class', 'job', 'faction', 'type'];
|
|
223
|
+
const DATA_MAP_KEYS = ['playerRoles', 'roles', 'playerCharacters', 'characters', 'playerTeams', 'teams'];
|
|
224
|
+
let roleLabel: string | undefined;
|
|
225
|
+
// Try PlayerState direct field
|
|
226
|
+
if (playerState) {
|
|
227
|
+
const entry = Object.entries(playerState).find(([k]) => ROLE_KEYS.includes(k.toLowerCase()));
|
|
228
|
+
if (entry) roleLabel = String(entry[1]);
|
|
229
|
+
}
|
|
230
|
+
// Try GameState.data lookup table
|
|
231
|
+
if (!roleLabel && fullState?.data) {
|
|
232
|
+
for (const mapKey of DATA_MAP_KEYS) {
|
|
233
|
+
const map = fullState.data[mapKey];
|
|
234
|
+
if (map && typeof map === 'object' && !Array.isArray(map)) {
|
|
235
|
+
const val = (map as Record<string, unknown>)[p.id];
|
|
236
|
+
if (val != null) { roleLabel = String(val); break; }
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
206
240
|
return (
|
|
207
241
|
<button
|
|
208
242
|
key={p.id}
|
|
@@ -227,9 +261,9 @@ export default function Preview() {
|
|
|
227
261
|
<span className="text-[10px] text-amber-400 shrink-0">HOST</span>
|
|
228
262
|
)}
|
|
229
263
|
</div>
|
|
230
|
-
{
|
|
264
|
+
{roleLabel && (
|
|
231
265
|
<div className="text-[10px] text-zinc-500 truncate">
|
|
232
|
-
{
|
|
266
|
+
{roleLabel}
|
|
233
267
|
</div>
|
|
234
268
|
)}
|
|
235
269
|
</div>
|
|
@@ -268,8 +302,16 @@ export default function Preview() {
|
|
|
268
302
|
)}
|
|
269
303
|
</div>
|
|
270
304
|
|
|
305
|
+
{/* Drag handle */}
|
|
306
|
+
<div
|
|
307
|
+
className="shrink-0 w-2 cursor-col-resize flex items-center justify-center group"
|
|
308
|
+
onMouseDown={() => { dragging.current = true; document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; }}
|
|
309
|
+
>
|
|
310
|
+
<div className="w-0.5 h-8 bg-zinc-700 rounded group-hover:bg-zinc-500 transition-colors" />
|
|
311
|
+
</div>
|
|
312
|
+
|
|
271
313
|
{/* Right column: State & Logs */}
|
|
272
|
-
<div className="flex flex-col gap-4">
|
|
314
|
+
<div className="flex flex-col gap-4 overflow-auto pl-1" style={{ width: `${(1 - splitRatio) * 100}%` }}>
|
|
273
315
|
{/* State Editor */}
|
|
274
316
|
<div className="bg-zinc-900 rounded-lg p-3 flex-1 flex flex-col min-h-0">
|
|
275
317
|
<h3 className="text-sm font-bold text-zinc-400 mb-2">Game State (Full)</h3>
|