@downcity/ui 0.1.0 → 0.1.1

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.
package/dist/index.js CHANGED
@@ -194,18 +194,183 @@ function CardFooter({ className, ...props }) {
194
194
  );
195
195
  }
196
196
 
197
+ // src/components/checkbox.tsx
198
+ import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox";
199
+ import { CheckIcon } from "lucide-react";
200
+ import { jsx as jsx3 } from "react/jsx-runtime";
201
+ function Checkbox({ className, ...props }) {
202
+ return /* @__PURE__ */ jsx3(
203
+ CheckboxPrimitive.Root,
204
+ {
205
+ "data-slot": "checkbox",
206
+ className: cn(
207
+ "peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
208
+ className
209
+ ),
210
+ ...props,
211
+ children: /* @__PURE__ */ jsx3(
212
+ CheckboxPrimitive.Indicator,
213
+ {
214
+ "data-slot": "checkbox-indicator",
215
+ className: "grid place-content-center text-current transition-none [&>svg]:size-3.5",
216
+ children: /* @__PURE__ */ jsx3(CheckIcon, {})
217
+ }
218
+ )
219
+ }
220
+ );
221
+ }
222
+
223
+ // src/components/dialog.tsx
224
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
225
+ import { XIcon } from "lucide-react";
226
+ import { jsx as jsx4, jsxs } from "react/jsx-runtime";
227
+ function Dialog(props) {
228
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
229
+ }
230
+ function DialogTrigger(props) {
231
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
232
+ }
233
+ function DialogPortal(props) {
234
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
235
+ }
236
+ function DialogClose(props) {
237
+ return /* @__PURE__ */ jsx4(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
238
+ }
239
+ function DialogOverlay({ className, ...props }) {
240
+ return /* @__PURE__ */ jsx4(
241
+ DialogPrimitive.Backdrop,
242
+ {
243
+ "data-slot": "dialog-overlay",
244
+ className: cn(
245
+ "fixed inset-0 z-50 bg-black/20 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
246
+ className
247
+ ),
248
+ ...props
249
+ }
250
+ );
251
+ }
252
+ function DialogContent({
253
+ className,
254
+ children,
255
+ showCloseButton = true,
256
+ ...props
257
+ }) {
258
+ return /* @__PURE__ */ jsxs(DialogPortal, { children: [
259
+ /* @__PURE__ */ jsx4(DialogOverlay, {}),
260
+ /* @__PURE__ */ jsxs(
261
+ DialogPrimitive.Popup,
262
+ {
263
+ "data-slot": "dialog-content",
264
+ className: cn(
265
+ "fixed top-1/2 left-1/2 z-50 w-[min(92vw,760px)] max-h-[86vh] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-[24px] bg-card text-sm shadow-[0_22px_70px_rgba(15,23,42,0.12)] transition duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0 data-ending-style:scale-[0.985] data-starting-style:scale-[0.985]",
266
+ className
267
+ ),
268
+ ...props,
269
+ children: [
270
+ children,
271
+ showCloseButton ? /* @__PURE__ */ jsxs(
272
+ DialogPrimitive.Close,
273
+ {
274
+ "data-slot": "dialog-close",
275
+ render: /* @__PURE__ */ jsx4(Button, { size: "icon-sm", variant: "ghost", className: "absolute top-3 right-3" }),
276
+ children: [
277
+ /* @__PURE__ */ jsx4(XIcon, {}),
278
+ /* @__PURE__ */ jsx4("span", { className: "sr-only", children: "Close" })
279
+ ]
280
+ }
281
+ ) : null
282
+ ]
283
+ }
284
+ )
285
+ ] });
286
+ }
287
+ function DialogHeader({ className, ...props }) {
288
+ return /* @__PURE__ */ jsx4(
289
+ "div",
290
+ {
291
+ "data-slot": "dialog-header",
292
+ className: cn("flex flex-col gap-0.5 p-4 pr-12", className),
293
+ ...props
294
+ }
295
+ );
296
+ }
297
+ function DialogFooter({ className, ...props }) {
298
+ return /* @__PURE__ */ jsx4(
299
+ "div",
300
+ {
301
+ "data-slot": "dialog-footer",
302
+ className: cn("flex items-center justify-end gap-2 p-4", className),
303
+ ...props
304
+ }
305
+ );
306
+ }
307
+ function DialogTitle({ className, ...props }) {
308
+ return /* @__PURE__ */ jsx4(
309
+ DialogPrimitive.Title,
310
+ {
311
+ "data-slot": "dialog-title",
312
+ className: cn("text-base font-medium text-foreground", className),
313
+ ...props
314
+ }
315
+ );
316
+ }
317
+ function DialogDescription({ className, ...props }) {
318
+ return /* @__PURE__ */ jsx4(
319
+ DialogPrimitive.Description,
320
+ {
321
+ "data-slot": "dialog-description",
322
+ className: cn("text-sm text-muted-foreground", className),
323
+ ...props
324
+ }
325
+ );
326
+ }
327
+
328
+ // src/components/input.tsx
329
+ import { Input as InputPrimitive } from "@base-ui/react/input";
330
+ import { jsx as jsx5 } from "react/jsx-runtime";
331
+ function Input({ className, type, ...props }) {
332
+ return /* @__PURE__ */ jsx5(
333
+ InputPrimitive,
334
+ {
335
+ type,
336
+ "data-slot": "input",
337
+ className: cn(
338
+ "h-9 w-full min-w-0 rounded-[12px] border border-transparent bg-secondary/85 px-3 py-2 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:bg-secondary focus-visible:ring-3 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/45 disabled:opacity-50 aria-invalid:bg-destructive/5 aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
339
+ className
340
+ ),
341
+ ...props
342
+ }
343
+ );
344
+ }
345
+
346
+ // src/components/label.tsx
347
+ import { jsx as jsx6 } from "react/jsx-runtime";
348
+ function Label({ className, ...props }) {
349
+ return /* @__PURE__ */ jsx6(
350
+ "label",
351
+ {
352
+ "data-slot": "label",
353
+ className: cn(
354
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
355
+ className
356
+ ),
357
+ ...props
358
+ }
359
+ );
360
+ }
361
+
197
362
  // src/components/dropdown-menu.tsx
198
363
  import { Menu as MenuPrimitive } from "@base-ui/react/menu";
199
- import { CheckIcon, ChevronRightIcon } from "lucide-react";
200
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
364
+ import { CheckIcon as CheckIcon2, ChevronRightIcon } from "lucide-react";
365
+ import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
201
366
  function DropdownMenu({ ...props }) {
202
- return /* @__PURE__ */ jsx3(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
367
+ return /* @__PURE__ */ jsx7(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
203
368
  }
204
369
  function DropdownMenuPortal({ ...props }) {
205
- return /* @__PURE__ */ jsx3(MenuPrimitive.Portal, { "data-slot": "dropdown-menu-portal", ...props });
370
+ return /* @__PURE__ */ jsx7(MenuPrimitive.Portal, { "data-slot": "dropdown-menu-portal", ...props });
206
371
  }
207
372
  function DropdownMenuTrigger({ ...props }) {
208
- return /* @__PURE__ */ jsx3(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
373
+ return /* @__PURE__ */ jsx7(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
209
374
  }
210
375
  function DropdownMenuContent({
211
376
  align = "start",
@@ -215,7 +380,7 @@ function DropdownMenuContent({
215
380
  className,
216
381
  ...props
217
382
  }) {
218
- return /* @__PURE__ */ jsx3(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx3(
383
+ return /* @__PURE__ */ jsx7(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx7(
219
384
  MenuPrimitive.Positioner,
220
385
  {
221
386
  className: "isolate z-50 outline-none",
@@ -223,7 +388,7 @@ function DropdownMenuContent({
223
388
  alignOffset,
224
389
  side,
225
390
  sideOffset,
226
- children: /* @__PURE__ */ jsx3(
391
+ children: /* @__PURE__ */ jsx7(
227
392
  MenuPrimitive.Popup,
228
393
  {
229
394
  "data-slot": "dropdown-menu-content",
@@ -238,14 +403,14 @@ function DropdownMenuContent({
238
403
  ) });
239
404
  }
240
405
  function DropdownMenuGroup({ ...props }) {
241
- return /* @__PURE__ */ jsx3(MenuPrimitive.Group, { "data-slot": "dropdown-menu-group", ...props });
406
+ return /* @__PURE__ */ jsx7(MenuPrimitive.Group, { "data-slot": "dropdown-menu-group", ...props });
242
407
  }
243
408
  function DropdownMenuLabel({
244
409
  className,
245
410
  inset,
246
411
  ...props
247
412
  }) {
248
- return /* @__PURE__ */ jsx3(
413
+ return /* @__PURE__ */ jsx7(
249
414
  MenuPrimitive.GroupLabel,
250
415
  {
251
416
  "data-slot": "dropdown-menu-label",
@@ -264,7 +429,7 @@ function DropdownMenuItem({
264
429
  variant = "default",
265
430
  ...props
266
431
  }) {
267
- return /* @__PURE__ */ jsx3(
432
+ return /* @__PURE__ */ jsx7(
268
433
  MenuPrimitive.Item,
269
434
  {
270
435
  "data-slot": "dropdown-menu-item",
@@ -279,7 +444,7 @@ function DropdownMenuItem({
279
444
  );
280
445
  }
281
446
  function DropdownMenuSub({ ...props }) {
282
- return /* @__PURE__ */ jsx3(MenuPrimitive.SubmenuRoot, { "data-slot": "dropdown-menu-sub", ...props });
447
+ return /* @__PURE__ */ jsx7(MenuPrimitive.SubmenuRoot, { "data-slot": "dropdown-menu-sub", ...props });
283
448
  }
284
449
  function DropdownMenuSubTrigger({
285
450
  className,
@@ -287,7 +452,7 @@ function DropdownMenuSubTrigger({
287
452
  children,
288
453
  ...props
289
454
  }) {
290
- return /* @__PURE__ */ jsxs(
455
+ return /* @__PURE__ */ jsxs2(
291
456
  MenuPrimitive.SubmenuTrigger,
292
457
  {
293
458
  "data-slot": "dropdown-menu-sub-trigger",
@@ -299,7 +464,7 @@ function DropdownMenuSubTrigger({
299
464
  ...props,
300
465
  children: [
301
466
  children,
302
- /* @__PURE__ */ jsx3(ChevronRightIcon, { className: "ml-auto" })
467
+ /* @__PURE__ */ jsx7(ChevronRightIcon, { className: "ml-auto" })
303
468
  ]
304
469
  }
305
470
  );
@@ -312,7 +477,7 @@ function DropdownMenuSubContent({
312
477
  className,
313
478
  ...props
314
479
  }) {
315
- return /* @__PURE__ */ jsx3(
480
+ return /* @__PURE__ */ jsx7(
316
481
  DropdownMenuContent,
317
482
  {
318
483
  "data-slot": "dropdown-menu-sub-content",
@@ -335,7 +500,7 @@ function DropdownMenuCheckboxItem({
335
500
  inset,
336
501
  ...props
337
502
  }) {
338
- return /* @__PURE__ */ jsxs(
503
+ return /* @__PURE__ */ jsxs2(
339
504
  MenuPrimitive.CheckboxItem,
340
505
  {
341
506
  "data-slot": "dropdown-menu-checkbox-item",
@@ -347,12 +512,12 @@ function DropdownMenuCheckboxItem({
347
512
  checked,
348
513
  ...props,
349
514
  children: [
350
- /* @__PURE__ */ jsx3(
515
+ /* @__PURE__ */ jsx7(
351
516
  "span",
352
517
  {
353
518
  className: "pointer-events-none absolute right-2 flex items-center justify-center",
354
519
  "data-slot": "dropdown-menu-checkbox-item-indicator",
355
- children: /* @__PURE__ */ jsx3(MenuPrimitive.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx3(CheckIcon, {}) })
520
+ children: /* @__PURE__ */ jsx7(MenuPrimitive.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx7(CheckIcon2, {}) })
356
521
  }
357
522
  ),
358
523
  children
@@ -361,7 +526,7 @@ function DropdownMenuCheckboxItem({
361
526
  );
362
527
  }
363
528
  function DropdownMenuRadioGroup({ ...props }) {
364
- return /* @__PURE__ */ jsx3(MenuPrimitive.RadioGroup, { "data-slot": "dropdown-menu-radio-group", ...props });
529
+ return /* @__PURE__ */ jsx7(MenuPrimitive.RadioGroup, { "data-slot": "dropdown-menu-radio-group", ...props });
365
530
  }
366
531
  function DropdownMenuRadioItem({
367
532
  className,
@@ -369,7 +534,7 @@ function DropdownMenuRadioItem({
369
534
  inset,
370
535
  ...props
371
536
  }) {
372
- return /* @__PURE__ */ jsxs(
537
+ return /* @__PURE__ */ jsxs2(
373
538
  MenuPrimitive.RadioItem,
374
539
  {
375
540
  "data-slot": "dropdown-menu-radio-item",
@@ -380,12 +545,12 @@ function DropdownMenuRadioItem({
380
545
  ),
381
546
  ...props,
382
547
  children: [
383
- /* @__PURE__ */ jsx3(
548
+ /* @__PURE__ */ jsx7(
384
549
  "span",
385
550
  {
386
551
  className: "pointer-events-none absolute right-2 flex items-center justify-center",
387
552
  "data-slot": "dropdown-menu-radio-item-indicator",
388
- children: /* @__PURE__ */ jsx3(MenuPrimitive.RadioItemIndicator, { children: /* @__PURE__ */ jsx3(CheckIcon, {}) })
553
+ children: /* @__PURE__ */ jsx7(MenuPrimitive.RadioItemIndicator, { children: /* @__PURE__ */ jsx7(CheckIcon2, {}) })
389
554
  }
390
555
  ),
391
556
  children
@@ -397,7 +562,7 @@ function DropdownMenuSeparator({
397
562
  className,
398
563
  ...props
399
564
  }) {
400
- return /* @__PURE__ */ jsx3(
565
+ return /* @__PURE__ */ jsx7(
401
566
  MenuPrimitive.Separator,
402
567
  {
403
568
  "data-slot": "dropdown-menu-separator",
@@ -410,7 +575,7 @@ function DropdownMenuShortcut({
410
575
  className,
411
576
  ...props
412
577
  }) {
413
- return /* @__PURE__ */ jsx3(
578
+ return /* @__PURE__ */ jsx7(
414
579
  "span",
415
580
  {
416
581
  "data-slot": "dropdown-menu-shortcut",
@@ -425,12 +590,12 @@ function DropdownMenuShortcut({
425
590
 
426
591
  // src/components/popover.tsx
427
592
  import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
428
- import { jsx as jsx4 } from "react/jsx-runtime";
593
+ import { jsx as jsx8 } from "react/jsx-runtime";
429
594
  function Popover({ ...props }) {
430
- return /* @__PURE__ */ jsx4(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
595
+ return /* @__PURE__ */ jsx8(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
431
596
  }
432
597
  function PopoverTrigger({ ...props }) {
433
- return /* @__PURE__ */ jsx4(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
598
+ return /* @__PURE__ */ jsx8(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
434
599
  }
435
600
  function PopoverContent({
436
601
  className,
@@ -440,7 +605,7 @@ function PopoverContent({
440
605
  alignOffset = 0,
441
606
  ...props
442
607
  }) {
443
- return /* @__PURE__ */ jsx4(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx4(
608
+ return /* @__PURE__ */ jsx8(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx8(
444
609
  PopoverPrimitive.Positioner,
445
610
  {
446
611
  side,
@@ -448,7 +613,7 @@ function PopoverContent({
448
613
  align,
449
614
  alignOffset,
450
615
  className: "isolate z-50",
451
- children: /* @__PURE__ */ jsx4(
616
+ children: /* @__PURE__ */ jsx8(
452
617
  PopoverPrimitive.Popup,
453
618
  {
454
619
  "data-slot": "popover-content",
@@ -463,21 +628,259 @@ function PopoverContent({
463
628
  ) });
464
629
  }
465
630
 
631
+ // src/components/separator.tsx
632
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
633
+ import { jsx as jsx9 } from "react/jsx-runtime";
634
+ function Separator({
635
+ className,
636
+ orientation = "horizontal",
637
+ ...props
638
+ }) {
639
+ return /* @__PURE__ */ jsx9(
640
+ SeparatorPrimitive,
641
+ {
642
+ "data-slot": "separator",
643
+ orientation,
644
+ className: cn(
645
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
646
+ className
647
+ ),
648
+ ...props
649
+ }
650
+ );
651
+ }
652
+
653
+ // src/components/sheet.tsx
654
+ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
655
+ import { XIcon as XIcon2 } from "lucide-react";
656
+ import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
657
+ function Sheet({ ...props }) {
658
+ return /* @__PURE__ */ jsx10(SheetPrimitive.Root, { "data-slot": "sheet", ...props });
659
+ }
660
+ function SheetTrigger({ ...props }) {
661
+ return /* @__PURE__ */ jsx10(SheetPrimitive.Trigger, { "data-slot": "sheet-trigger", ...props });
662
+ }
663
+ function SheetClose({ ...props }) {
664
+ return /* @__PURE__ */ jsx10(SheetPrimitive.Close, { "data-slot": "sheet-close", ...props });
665
+ }
666
+ function SheetPortal({ ...props }) {
667
+ return /* @__PURE__ */ jsx10(SheetPrimitive.Portal, { "data-slot": "sheet-portal", ...props });
668
+ }
669
+ function SheetOverlay({ className, ...props }) {
670
+ return /* @__PURE__ */ jsx10(
671
+ SheetPrimitive.Backdrop,
672
+ {
673
+ "data-slot": "sheet-overlay",
674
+ className: cn(
675
+ "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
676
+ className
677
+ ),
678
+ ...props
679
+ }
680
+ );
681
+ }
682
+ function SheetContent({
683
+ className,
684
+ children,
685
+ side = "right",
686
+ showCloseButton = true,
687
+ ...props
688
+ }) {
689
+ return /* @__PURE__ */ jsxs3(SheetPortal, { children: [
690
+ /* @__PURE__ */ jsx10(SheetOverlay, {}),
691
+ /* @__PURE__ */ jsxs3(
692
+ SheetPrimitive.Popup,
693
+ {
694
+ "data-slot": "sheet-content",
695
+ "data-side": side,
696
+ className: cn(
697
+ "fixed z-50 flex flex-col gap-4 bg-background bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
698
+ className
699
+ ),
700
+ ...props,
701
+ children: [
702
+ children,
703
+ showCloseButton ? /* @__PURE__ */ jsxs3(
704
+ SheetPrimitive.Close,
705
+ {
706
+ "data-slot": "sheet-close",
707
+ render: /* @__PURE__ */ jsx10(Button, { variant: "ghost", className: "absolute top-3 right-3", size: "icon-sm" }),
708
+ children: [
709
+ /* @__PURE__ */ jsx10(XIcon2, {}),
710
+ /* @__PURE__ */ jsx10("span", { className: "sr-only", children: "Close" })
711
+ ]
712
+ }
713
+ ) : null
714
+ ]
715
+ }
716
+ )
717
+ ] });
718
+ }
719
+ function SheetHeader({ className, ...props }) {
720
+ return /* @__PURE__ */ jsx10(
721
+ "div",
722
+ {
723
+ "data-slot": "sheet-header",
724
+ className: cn("flex flex-col gap-0.5 p-4", className),
725
+ ...props
726
+ }
727
+ );
728
+ }
729
+ function SheetFooter({ className, ...props }) {
730
+ return /* @__PURE__ */ jsx10(
731
+ "div",
732
+ {
733
+ "data-slot": "sheet-footer",
734
+ className: cn("mt-auto flex flex-col gap-2 p-4", className),
735
+ ...props
736
+ }
737
+ );
738
+ }
739
+ function SheetTitle({ className, ...props }) {
740
+ return /* @__PURE__ */ jsx10(
741
+ SheetPrimitive.Title,
742
+ {
743
+ "data-slot": "sheet-title",
744
+ className: cn("text-base font-medium text-foreground", className),
745
+ ...props
746
+ }
747
+ );
748
+ }
749
+ function SheetDescription({ className, ...props }) {
750
+ return /* @__PURE__ */ jsx10(
751
+ SheetPrimitive.Description,
752
+ {
753
+ "data-slot": "sheet-description",
754
+ className: cn("text-sm text-muted-foreground", className),
755
+ ...props
756
+ }
757
+ );
758
+ }
759
+
760
+ // src/components/skeleton.tsx
761
+ import { jsx as jsx11 } from "react/jsx-runtime";
762
+ function Skeleton({ className, ...props }) {
763
+ return /* @__PURE__ */ jsx11(
764
+ "div",
765
+ {
766
+ "data-slot": "skeleton",
767
+ className: cn("animate-pulse rounded-md bg-muted", className),
768
+ ...props
769
+ }
770
+ );
771
+ }
772
+
773
+ // src/components/tabs.tsx
774
+ import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
775
+ import { cva as cva3 } from "class-variance-authority";
776
+ import { jsx as jsx12 } from "react/jsx-runtime";
777
+ function Tabs({
778
+ className,
779
+ orientation = "horizontal",
780
+ ...props
781
+ }) {
782
+ return /* @__PURE__ */ jsx12(
783
+ TabsPrimitive.Root,
784
+ {
785
+ "data-slot": "tabs",
786
+ "data-orientation": orientation,
787
+ className: cn("group/tabs flex gap-2 data-horizontal:flex-col", className),
788
+ ...props
789
+ }
790
+ );
791
+ }
792
+ var tabsListVariants = cva3(
793
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
794
+ {
795
+ variants: {
796
+ variant: {
797
+ default: "bg-muted",
798
+ line: "gap-1 bg-transparent"
799
+ }
800
+ },
801
+ defaultVariants: {
802
+ variant: "default"
803
+ }
804
+ }
805
+ );
806
+ function TabsList({
807
+ className,
808
+ variant = "default",
809
+ ...props
810
+ }) {
811
+ return /* @__PURE__ */ jsx12(
812
+ TabsPrimitive.List,
813
+ {
814
+ "data-slot": "tabs-list",
815
+ "data-variant": variant,
816
+ className: cn(tabsListVariants({ variant }), className),
817
+ ...props
818
+ }
819
+ );
820
+ }
821
+ function TabsTrigger({ className, ...props }) {
822
+ return /* @__PURE__ */ jsx12(
823
+ TabsPrimitive.Tab,
824
+ {
825
+ "data-slot": "tabs-trigger",
826
+ className: cn(
827
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
828
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
829
+ "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
830
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
831
+ className
832
+ ),
833
+ ...props
834
+ }
835
+ );
836
+ }
837
+ function TabsContent({ className, ...props }) {
838
+ return /* @__PURE__ */ jsx12(
839
+ TabsPrimitive.Panel,
840
+ {
841
+ "data-slot": "tabs-content",
842
+ className: cn("flex-1 text-sm outline-none", className),
843
+ ...props
844
+ }
845
+ );
846
+ }
847
+
848
+ // src/components/textarea.tsx
849
+ import * as React from "react";
850
+ import { jsx as jsx13 } from "react/jsx-runtime";
851
+ var Textarea = React.forwardRef(
852
+ ({ className, ...props }, ref) => {
853
+ return /* @__PURE__ */ jsx13(
854
+ "textarea",
855
+ {
856
+ ref,
857
+ "data-slot": "textarea",
858
+ className: cn(
859
+ "flex min-h-[96px] w-full rounded-[16px] border border-transparent bg-secondary/85 px-3 py-2.5 text-sm outline-none transition placeholder:text-muted-foreground focus-visible:bg-secondary focus-visible:ring-3 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-55",
860
+ className
861
+ ),
862
+ ...props
863
+ }
864
+ );
865
+ }
866
+ );
867
+ Textarea.displayName = "Textarea";
868
+
466
869
  // src/components/sonner.tsx
467
870
  import { CircleCheckIcon, InfoIcon, Loader2Icon, OctagonXIcon, TriangleAlertIcon } from "lucide-react";
468
871
  import { Toaster as Sonner } from "sonner";
469
- import { jsx as jsx5 } from "react/jsx-runtime";
872
+ import { jsx as jsx14 } from "react/jsx-runtime";
470
873
  function Toaster({ ...props }) {
471
- return /* @__PURE__ */ jsx5(
874
+ return /* @__PURE__ */ jsx14(
472
875
  Sonner,
473
876
  {
474
877
  className: "toaster group",
475
878
  icons: {
476
- success: /* @__PURE__ */ jsx5(CircleCheckIcon, { className: "size-4" }),
477
- info: /* @__PURE__ */ jsx5(InfoIcon, { className: "size-4" }),
478
- warning: /* @__PURE__ */ jsx5(TriangleAlertIcon, { className: "size-4" }),
479
- error: /* @__PURE__ */ jsx5(OctagonXIcon, { className: "size-4" }),
480
- loading: /* @__PURE__ */ jsx5(Loader2Icon, { className: "size-4 animate-spin" })
879
+ success: /* @__PURE__ */ jsx14(CircleCheckIcon, { className: "size-4" }),
880
+ info: /* @__PURE__ */ jsx14(InfoIcon, { className: "size-4" }),
881
+ warning: /* @__PURE__ */ jsx14(TriangleAlertIcon, { className: "size-4" }),
882
+ error: /* @__PURE__ */ jsx14(OctagonXIcon, { className: "size-4" }),
883
+ loading: /* @__PURE__ */ jsx14(Loader2Icon, { className: "size-4 animate-spin" })
481
884
  },
482
885
  style: {
483
886
  "--normal-bg": "var(--popover)",
@@ -494,6 +897,3390 @@ function Toaster({ ...props }) {
494
897
  }
495
898
  );
496
899
  }
900
+
901
+ // src/components/toggle.tsx
902
+ import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
903
+ import { cva as cva4 } from "class-variance-authority";
904
+ import { jsx as jsx15 } from "react/jsx-runtime";
905
+ var toggleVariants = cva4(
906
+ "group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
907
+ {
908
+ variants: {
909
+ variant: {
910
+ default: "bg-transparent",
911
+ outline: "border border-input bg-transparent hover:bg-muted"
912
+ },
913
+ size: {
914
+ default: "h-8 min-w-8 px-2",
915
+ sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]",
916
+ lg: "h-9 min-w-9 px-2.5"
917
+ }
918
+ },
919
+ defaultVariants: {
920
+ variant: "default",
921
+ size: "default"
922
+ }
923
+ }
924
+ );
925
+ function Toggle({
926
+ className,
927
+ variant = "default",
928
+ size = "default",
929
+ ...props
930
+ }) {
931
+ return /* @__PURE__ */ jsx15(
932
+ TogglePrimitive,
933
+ {
934
+ "data-slot": "toggle",
935
+ className: cn(toggleVariants({ variant, size, className })),
936
+ ...props
937
+ }
938
+ );
939
+ }
940
+
941
+ // src/components/toggle-group.tsx
942
+ import * as React2 from "react";
943
+ import { Toggle as TogglePrimitive2 } from "@base-ui/react/toggle";
944
+ import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
945
+ import { jsx as jsx16 } from "react/jsx-runtime";
946
+ var ToggleGroupContext = React2.createContext({
947
+ size: "default",
948
+ variant: "default",
949
+ spacing: 0,
950
+ orientation: "horizontal"
951
+ });
952
+ function ToggleGroup({
953
+ className,
954
+ variant,
955
+ size,
956
+ spacing = 0,
957
+ orientation = "horizontal",
958
+ children,
959
+ ...props
960
+ }) {
961
+ return /* @__PURE__ */ jsx16(
962
+ ToggleGroupPrimitive,
963
+ {
964
+ "data-slot": "toggle-group",
965
+ "data-variant": variant,
966
+ "data-size": size,
967
+ "data-spacing": spacing,
968
+ "data-orientation": orientation,
969
+ style: { "--gap": spacing },
970
+ className: cn(
971
+ "group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch",
972
+ className
973
+ ),
974
+ ...props,
975
+ children: /* @__PURE__ */ jsx16(ToggleGroupContext.Provider, { value: { variant, size, spacing, orientation }, children })
976
+ }
977
+ );
978
+ }
979
+ function ToggleGroupItem({
980
+ className,
981
+ children,
982
+ variant = "default",
983
+ size = "default",
984
+ ...props
985
+ }) {
986
+ const context = React2.useContext(ToggleGroupContext);
987
+ return /* @__PURE__ */ jsx16(
988
+ TogglePrimitive2,
989
+ {
990
+ "data-slot": "toggle-group-item",
991
+ "data-variant": context.variant || variant,
992
+ "data-size": context.size || size,
993
+ "data-spacing": context.spacing,
994
+ className: cn(
995
+ "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
996
+ toggleVariants({
997
+ variant: context.variant || variant,
998
+ size: context.size || size
999
+ }),
1000
+ className
1001
+ ),
1002
+ ...props,
1003
+ children
1004
+ }
1005
+ );
1006
+ }
1007
+
1008
+ // src/components/tooltip.tsx
1009
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
1010
+ import { jsx as jsx17, jsxs as jsxs4 } from "react/jsx-runtime";
1011
+ function TooltipProvider({
1012
+ delay = 0,
1013
+ ...props
1014
+ }) {
1015
+ return /* @__PURE__ */ jsx17(
1016
+ TooltipPrimitive.Provider,
1017
+ {
1018
+ "data-slot": "tooltip-provider",
1019
+ delay,
1020
+ ...props
1021
+ }
1022
+ );
1023
+ }
1024
+ function Tooltip({ ...props }) {
1025
+ return /* @__PURE__ */ jsx17(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props });
1026
+ }
1027
+ function TooltipTrigger({ ...props }) {
1028
+ return /* @__PURE__ */ jsx17(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
1029
+ }
1030
+ function TooltipContent({
1031
+ className,
1032
+ side = "top",
1033
+ sideOffset = 4,
1034
+ align = "center",
1035
+ alignOffset = 0,
1036
+ children,
1037
+ ...props
1038
+ }) {
1039
+ return /* @__PURE__ */ jsx17(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx17(
1040
+ TooltipPrimitive.Positioner,
1041
+ {
1042
+ align,
1043
+ alignOffset,
1044
+ side,
1045
+ sideOffset,
1046
+ className: "isolate z-50",
1047
+ children: /* @__PURE__ */ jsxs4(
1048
+ TooltipPrimitive.Popup,
1049
+ {
1050
+ "data-slot": "tooltip-content",
1051
+ className: cn(
1052
+ "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
1053
+ className
1054
+ ),
1055
+ ...props,
1056
+ children: [
1057
+ children,
1058
+ /* @__PURE__ */ jsx17(TooltipPrimitive.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })
1059
+ ]
1060
+ }
1061
+ )
1062
+ }
1063
+ ) });
1064
+ }
1065
+
1066
+ // src/components/workboard.tsx
1067
+ import * as React7 from "react";
1068
+ import {
1069
+ Maximize2Icon,
1070
+ Minimize2Icon,
1071
+ RefreshCwIcon
1072
+ } from "lucide-react";
1073
+
1074
+ // src/components/workboard-game-atlas.tsx
1075
+ import * as React4 from "react";
1076
+
1077
+ // src/components/workboard-stage-map.tsx
1078
+ import { jsx as jsx18, jsxs as jsxs5 } from "react/jsx-runtime";
1079
+ var TILE_SIZE = 40;
1080
+ var GRID_COLS = 40;
1081
+ var GRID_ROWS = 24;
1082
+ var STAGE_WIDTH = TILE_SIZE * GRID_COLS;
1083
+ var STAGE_HEIGHT = TILE_SIZE * GRID_ROWS;
1084
+ var WORKBOARD_TOWN_PLAZA_POINT = { x: 800, y: 480 };
1085
+ var WORKBOARD_ZONE_GATE_POINTS = {
1086
+ engaged: { x: 560, y: 400 },
1087
+ steady: { x: 1040, y: 400 },
1088
+ quiet: { x: 560, y: 560 },
1089
+ drift: { x: 1040, y: 560 }
1090
+ };
1091
+ var WORKBOARD_ZONE_LAYOUT = {
1092
+ engaged: { x: 2.5, y: 12.5, w: 35, h: 29.167, hubX: 20, hubY: 27.083 },
1093
+ steady: { x: 62.5, y: 12.5, w: 35, h: 29.167, hubX: 80, hubY: 27.083 },
1094
+ quiet: { x: 2.5, y: 58.333, w: 35, h: 33.333, hubX: 20, hubY: 75 },
1095
+ drift: { x: 62.5, y: 58.333, w: 35, h: 33.333, hubX: 80, hubY: 75 }
1096
+ };
1097
+ var ZONE_PIXEL_PALETTE = {
1098
+ engaged: {
1099
+ fill: "rgba(195,230,214,0.9)",
1100
+ fillStrong: "rgba(126,190,162,0.96)",
1101
+ stroke: "rgba(39,110,80,0.95)",
1102
+ line: "rgba(52,144,111,0.78)",
1103
+ shadow: "rgba(37,86,66,0.24)"
1104
+ },
1105
+ steady: {
1106
+ fill: "rgba(225,235,184,0.92)",
1107
+ fillStrong: "rgba(186,207,103,0.96)",
1108
+ stroke: "rgba(112,132,38,0.92)",
1109
+ line: "rgba(145,161,63,0.72)",
1110
+ shadow: "rgba(98,112,35,0.2)"
1111
+ },
1112
+ quiet: {
1113
+ fill: "rgba(225,221,211,0.94)",
1114
+ fillStrong: "rgba(190,183,169,0.98)",
1115
+ stroke: "rgba(103,96,87,0.9)",
1116
+ line: "rgba(130,124,112,0.7)",
1117
+ shadow: "rgba(84,78,71,0.18)"
1118
+ },
1119
+ drift: {
1120
+ fill: "rgba(245,214,178,0.94)",
1121
+ fillStrong: "rgba(231,162,92,0.96)",
1122
+ stroke: "rgba(161,91,32,0.92)",
1123
+ line: "rgba(194,121,55,0.78)",
1124
+ shadow: "rgba(146,78,28,0.2)"
1125
+ }
1126
+ };
1127
+ var TOWN_PATH_TILES = [
1128
+ { col: 0, row: 10, cols: 40, rows: 4 },
1129
+ { col: 19, row: 0, cols: 2, rows: 24 },
1130
+ { col: 12, row: 7, cols: 7, rows: 2 },
1131
+ { col: 21, row: 7, cols: 7, rows: 2 },
1132
+ { col: 12, row: 15, cols: 7, rows: 2 },
1133
+ { col: 21, row: 15, cols: 7, rows: 2 }
1134
+ ];
1135
+ var TOWN_WATER_TILES = [
1136
+ { col: 0, row: 0, cols: 10, rows: 2 },
1137
+ { col: 30, row: 0, cols: 10, rows: 2 },
1138
+ { col: 0, row: 22, cols: 11, rows: 2 },
1139
+ { col: 29, row: 22, cols: 11, rows: 2 },
1140
+ { col: 0, row: 2, cols: 2, rows: 5 },
1141
+ { col: 38, row: 17, cols: 2, rows: 5 }
1142
+ ];
1143
+ var TOWN_FENCE_TILES = [
1144
+ { col: 2, row: 2, cols: 15, rows: 1 },
1145
+ { col: 23, row: 2, cols: 15, rows: 1 },
1146
+ { col: 2, row: 22, cols: 15, rows: 1 },
1147
+ { col: 23, row: 22, cols: 15, rows: 1 }
1148
+ ];
1149
+ var TOWN_BUILDINGS = [
1150
+ {
1151
+ col: 1,
1152
+ row: 3,
1153
+ cols: 14,
1154
+ rows: 7,
1155
+ zoneId: "engaged",
1156
+ floor: "rgba(230,199,170,0.98)",
1157
+ wall: "rgba(135,73,56,0.96)",
1158
+ entrance: "bottom",
1159
+ walls: [
1160
+ { col: 5, row: 3, cols: 1, rows: 7 },
1161
+ { col: 10, row: 3, cols: 1, rows: 7 },
1162
+ { col: 1, row: 6, cols: 14, rows: 1 }
1163
+ ],
1164
+ props: [
1165
+ { col: 2, row: 4, cols: 1, rows: 1, kind: "desk" },
1166
+ { col: 7, row: 4, cols: 2, rows: 1, kind: "shelf" },
1167
+ { col: 12, row: 4, cols: 1, rows: 1, kind: "desk" },
1168
+ { col: 2, row: 8, cols: 2, rows: 1, kind: "table" },
1169
+ { col: 7, row: 8, cols: 1, rows: 1, kind: "sofa" },
1170
+ { col: 12, row: 8, cols: 1, rows: 1, kind: "sofa" }
1171
+ ]
1172
+ },
1173
+ {
1174
+ col: 25,
1175
+ row: 3,
1176
+ cols: 14,
1177
+ rows: 7,
1178
+ zoneId: "steady",
1179
+ floor: "rgba(244,236,174,0.98)",
1180
+ wall: "rgba(118,95,68,0.96)",
1181
+ entrance: "bottom",
1182
+ walls: [
1183
+ { col: 29, row: 3, cols: 1, rows: 7 },
1184
+ { col: 34, row: 3, cols: 1, rows: 7 },
1185
+ { col: 25, row: 6, cols: 14, rows: 1 }
1186
+ ],
1187
+ props: [
1188
+ { col: 26, row: 4, cols: 1, rows: 1, kind: "desk" },
1189
+ { col: 31, row: 4, cols: 2, rows: 1, kind: "shelf" },
1190
+ { col: 36, row: 4, cols: 1, rows: 1, kind: "desk" },
1191
+ { col: 26, row: 8, cols: 2, rows: 1, kind: "table" },
1192
+ { col: 31, row: 8, cols: 1, rows: 1, kind: "bed" },
1193
+ { col: 36, row: 8, cols: 1, rows: 1, kind: "sofa" }
1194
+ ]
1195
+ },
1196
+ {
1197
+ col: 1,
1198
+ row: 14,
1199
+ cols: 14,
1200
+ rows: 8,
1201
+ zoneId: "quiet",
1202
+ floor: "rgba(231,226,207,0.98)",
1203
+ wall: "rgba(116,107,97,0.96)",
1204
+ entrance: "top",
1205
+ walls: [
1206
+ { col: 5, row: 14, cols: 1, rows: 8 },
1207
+ { col: 10, row: 14, cols: 1, rows: 8 },
1208
+ { col: 1, row: 18, cols: 14, rows: 1 }
1209
+ ],
1210
+ props: [
1211
+ { col: 2, row: 15, cols: 1, rows: 1, kind: "bed" },
1212
+ { col: 7, row: 15, cols: 2, rows: 1, kind: "shelf" },
1213
+ { col: 12, row: 15, cols: 1, rows: 1, kind: "bed" },
1214
+ { col: 2, row: 20, cols: 2, rows: 1, kind: "table" },
1215
+ { col: 7, row: 20, cols: 1, rows: 1, kind: "sofa" },
1216
+ { col: 12, row: 20, cols: 1, rows: 1, kind: "sofa" }
1217
+ ]
1218
+ },
1219
+ {
1220
+ col: 25,
1221
+ row: 14,
1222
+ cols: 14,
1223
+ rows: 8,
1224
+ zoneId: "drift",
1225
+ floor: "rgba(244,211,166,0.98)",
1226
+ wall: "rgba(155,85,43,0.96)",
1227
+ entrance: "top",
1228
+ walls: [
1229
+ { col: 29, row: 14, cols: 1, rows: 8 },
1230
+ { col: 34, row: 14, cols: 1, rows: 8 },
1231
+ { col: 25, row: 18, cols: 14, rows: 1 }
1232
+ ],
1233
+ props: [
1234
+ { col: 26, row: 15, cols: 1, rows: 1, kind: "desk" },
1235
+ { col: 31, row: 15, cols: 2, rows: 1, kind: "shelf" },
1236
+ { col: 36, row: 15, cols: 1, rows: 1, kind: "desk" },
1237
+ { col: 26, row: 20, cols: 2, rows: 1, kind: "table" },
1238
+ { col: 31, row: 20, cols: 1, rows: 1, kind: "sofa" },
1239
+ { col: 36, row: 20, cols: 1, rows: 1, kind: "sofa" }
1240
+ ]
1241
+ }
1242
+ ];
1243
+ var TOWN_TREE_POINTS = [
1244
+ { col: 19, row: 2 },
1245
+ { col: 20, row: 21 },
1246
+ { col: 7, row: 2 },
1247
+ { col: 32, row: 2 },
1248
+ { col: 17, row: 4 },
1249
+ { col: 22, row: 4 },
1250
+ { col: 17, row: 19 },
1251
+ { col: 22, row: 19 }
1252
+ ];
1253
+ var TOWN_SHRUB_POINTS = [
1254
+ { col: 16, row: 8 },
1255
+ { col: 23, row: 8 },
1256
+ { col: 16, row: 15 },
1257
+ { col: 23, row: 15 },
1258
+ { col: 4, row: 2 },
1259
+ { col: 35, row: 2 },
1260
+ { col: 4, row: 22 },
1261
+ { col: 35, row: 22 }
1262
+ ];
1263
+ var TOWN_FLOWER_POINTS = [
1264
+ { col: 6, row: 11 },
1265
+ { col: 11, row: 12 },
1266
+ { col: 16, row: 9 },
1267
+ { col: 23, row: 14 },
1268
+ { col: 29, row: 11 },
1269
+ { col: 34, row: 12 },
1270
+ { col: 18, row: 5 },
1271
+ { col: 21, row: 18 },
1272
+ { col: 3, row: 21 },
1273
+ { col: 36, row: 2 }
1274
+ ];
1275
+ function tileToRect(tile) {
1276
+ return {
1277
+ x: tile.col * TILE_SIZE,
1278
+ y: tile.row * TILE_SIZE,
1279
+ width: tile.cols * TILE_SIZE,
1280
+ height: tile.rows * TILE_SIZE
1281
+ };
1282
+ }
1283
+ function estimateTextWidth(text) {
1284
+ return Array.from(text).reduce((acc, char) => acc + (char.charCodeAt(0) > 255 ? 9 : 6), 0);
1285
+ }
1286
+ function fitPixelLabel(text, maxWidth) {
1287
+ if (estimateTextWidth(text) <= maxWidth) return text;
1288
+ let current = "";
1289
+ for (const char of Array.from(text)) {
1290
+ const next = `${current}${char}`;
1291
+ if (estimateTextWidth(`${next}...`) > maxWidth) {
1292
+ return current.length > 0 ? `${current}...` : text;
1293
+ }
1294
+ current = next;
1295
+ }
1296
+ return current;
1297
+ }
1298
+ function TileRectSvg(props) {
1299
+ const rect2 = tileToRect(props.tile);
1300
+ return /* @__PURE__ */ jsx18(
1301
+ "rect",
1302
+ {
1303
+ x: rect2.x,
1304
+ y: rect2.y,
1305
+ width: rect2.width,
1306
+ height: rect2.height,
1307
+ fill: props.fill,
1308
+ stroke: props.stroke,
1309
+ strokeWidth: props.stroke ? 2 : void 0,
1310
+ opacity: props.opacity
1311
+ }
1312
+ );
1313
+ }
1314
+ function renderGrassTiles() {
1315
+ return Array.from({ length: GRID_COLS * GRID_ROWS }, (_, index) => {
1316
+ const col = index % GRID_COLS;
1317
+ const row = Math.floor(index / GRID_COLS);
1318
+ const fill = (col + row) % 2 === 0 ? "rgba(104,136,76,0.98)" : "rgba(91,124,70,0.98)";
1319
+ const rect2 = tileToRect({ col, row, cols: 1, rows: 1 });
1320
+ return /* @__PURE__ */ jsxs5("g", { children: [
1321
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x, y: rect2.y, width: TILE_SIZE, height: TILE_SIZE, fill }),
1322
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 6, y: rect2.y + 8, width: "4", height: "4", fill: "rgba(40,78,45,0.26)" }),
1323
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 27, y: rect2.y + 25, width: "4", height: "4", fill: "rgba(142,161,89,0.18)" })
1324
+ ] }, `grass-${col}-${row}`);
1325
+ });
1326
+ }
1327
+ function renderTownPath(tile, index) {
1328
+ const rect2 = tileToRect(tile);
1329
+ const cobbles = Array.from({ length: tile.cols * tile.rows }, (_, cobbleIndex) => {
1330
+ const col = tile.col + cobbleIndex % tile.cols;
1331
+ const row = tile.row + Math.floor(cobbleIndex / tile.cols);
1332
+ const x = col * TILE_SIZE + ((col + row) % 2 === 0 ? 8 : 22);
1333
+ const y = row * TILE_SIZE + ((col * 3 + row) % 2 === 0 ? 10 : 24);
1334
+ return /* @__PURE__ */ jsx18("rect", { x, y, width: "8", height: "5", fill: "rgba(134,105,67,0.24)" }, `town-path-cobble-${index}-${cobbleIndex}`);
1335
+ });
1336
+ return /* @__PURE__ */ jsxs5("g", { children: [
1337
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, fill: "rgba(193,165,104,0.98)" }),
1338
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: "4", fill: "rgba(116,92,62,0.34)" }),
1339
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x, y: rect2.y + rect2.height - 4, width: rect2.width, height: "4", fill: "rgba(116,92,62,0.28)" }),
1340
+ cobbles
1341
+ ] }, `town-path-${index}`);
1342
+ }
1343
+ function renderTownWater(tile, index) {
1344
+ const rect2 = tileToRect(tile);
1345
+ return /* @__PURE__ */ jsxs5("g", { children: [
1346
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, fill: "rgba(82,139,149,0.9)" }),
1347
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 8, y: rect2.y + 10, width: Math.max(12, rect2.width - 16), height: "4", fill: "rgba(160,205,199,0.34)" }),
1348
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 18, y: rect2.y + rect2.height - 14, width: Math.max(18, rect2.width - 48), height: "4", fill: "rgba(53,103,112,0.24)" })
1349
+ ] }, `town-water-${index}`);
1350
+ }
1351
+ function renderTownFence(tile, index) {
1352
+ const rect2 = tileToRect(tile);
1353
+ const posts = Array.from({ length: tile.cols }, (_, postIndex) => {
1354
+ const x = rect2.x + postIndex * TILE_SIZE + 16;
1355
+ return /* @__PURE__ */ jsx18("rect", { x, y: rect2.y + 8, width: "8", height: "24", fill: "rgba(111,78,47,0.88)" }, `town-fence-post-${index}-${postIndex}`);
1356
+ });
1357
+ return /* @__PURE__ */ jsxs5("g", { children: [
1358
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 8, y: rect2.y + 15, width: rect2.width - 16, height: "6", fill: "rgba(151,104,61,0.82)" }),
1359
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 8, y: rect2.y + 25, width: rect2.width - 16, height: "5", fill: "rgba(126,88,54,0.7)" }),
1360
+ posts
1361
+ ] }, `town-fence-${index}`);
1362
+ }
1363
+ function renderTownPlaza() {
1364
+ return /* @__PURE__ */ jsxs5("g", { children: [
1365
+ /* @__PURE__ */ jsx18(TileRectSvg, { tile: { col: 17, row: 9, cols: 6, rows: 6 }, fill: "rgba(211,189,128,0.96)", stroke: "rgba(126,97,58,0.36)" }),
1366
+ /* @__PURE__ */ jsx18(TileRectSvg, { tile: { col: 18, row: 10, cols: 4, rows: 4 }, fill: "rgba(232,210,148,0.98)", stroke: "rgba(126,97,58,0.32)" }),
1367
+ /* @__PURE__ */ jsx18("rect", { x: "776", y: "456", width: "48", height: "48", fill: "rgba(116,160,178,0.92)", stroke: "rgba(64,91,105,0.76)", strokeWidth: "4" }),
1368
+ /* @__PURE__ */ jsx18("rect", { x: "788", y: "468", width: "24", height: "24", fill: "rgba(178,226,228,0.88)" }),
1369
+ /* @__PURE__ */ jsx18("rect", { x: "796", y: "444", width: "8", height: "24", fill: "rgba(101,112,109,0.9)" }),
1370
+ /* @__PURE__ */ jsx18("rect", { x: "784", y: "516", width: "32", height: "12", fill: "rgba(113,79,48,0.86)" }),
1371
+ /* @__PURE__ */ jsx18("rect", { x: "736", y: "436", width: "8", height: "8", fill: "rgba(238,228,183,0.9)" }),
1372
+ /* @__PURE__ */ jsx18("rect", { x: "856", y: "436", width: "8", height: "8", fill: "rgba(238,228,183,0.9)" }),
1373
+ /* @__PURE__ */ jsx18("rect", { x: "736", y: "556", width: "8", height: "8", fill: "rgba(238,228,183,0.9)" }),
1374
+ /* @__PURE__ */ jsx18("rect", { x: "856", y: "556", width: "8", height: "8", fill: "rgba(238,228,183,0.9)" })
1375
+ ] }, "town-plaza");
1376
+ }
1377
+ function renderTree(point, index) {
1378
+ const x = point.col * TILE_SIZE;
1379
+ const y = point.row * TILE_SIZE;
1380
+ return /* @__PURE__ */ jsxs5("g", { children: [
1381
+ /* @__PURE__ */ jsx18("rect", { x: x + 17, y: y + 22, width: "8", height: "12", fill: "rgba(103,73,42,0.92)" }),
1382
+ /* @__PURE__ */ jsx18("rect", { x: x + 10, y: y + 10, width: "22", height: "18", fill: "rgba(58,145,77,0.95)" }),
1383
+ /* @__PURE__ */ jsx18("rect", { x: x + 14, y: y + 4, width: "14", height: "12", fill: "rgba(80,174,86,0.96)" }),
1384
+ /* @__PURE__ */ jsx18("rect", { x: x + 8, y: y + 18, width: "26", height: "8", fill: "rgba(38,122,67,0.84)" })
1385
+ ] }, `tree-${index}`);
1386
+ }
1387
+ function renderShrub(point, index) {
1388
+ const x = point.col * TILE_SIZE;
1389
+ const y = point.row * TILE_SIZE;
1390
+ return /* @__PURE__ */ jsxs5("g", { children: [
1391
+ /* @__PURE__ */ jsx18("rect", { x: x + 8, y: y + 18, width: "24", height: "12", fill: "rgba(53,111,61,0.86)" }),
1392
+ /* @__PURE__ */ jsx18("rect", { x: x + 14, y: y + 12, width: "14", height: "10", fill: "rgba(72,132,67,0.9)" }),
1393
+ /* @__PURE__ */ jsx18("rect", { x: x + 5, y: y + 27, width: "30", height: "5", fill: "rgba(33,76,43,0.32)" })
1394
+ ] }, `shrub-${index}`);
1395
+ }
1396
+ function renderFlower(point, index) {
1397
+ const x = point.col * TILE_SIZE;
1398
+ const y = point.row * TILE_SIZE;
1399
+ return /* @__PURE__ */ jsxs5("g", { children: [
1400
+ /* @__PURE__ */ jsx18("rect", { x: x + 11, y: y + 13, width: "4", height: "4", fill: "rgba(240,97,135,0.86)" }),
1401
+ /* @__PURE__ */ jsx18("rect", { x: x + 24, y: y + 20, width: "4", height: "4", fill: "rgba(248,219,82,0.9)" }),
1402
+ /* @__PURE__ */ jsx18("rect", { x: x + 17, y: y + 26, width: "4", height: "4", fill: "rgba(134,104,218,0.76)" })
1403
+ ] }, `flower-${index}`);
1404
+ }
1405
+ function renderTownProp(prop, index) {
1406
+ const rect2 = tileToRect(prop);
1407
+ const x = rect2.x + 6;
1408
+ const y = rect2.y + 7;
1409
+ if (prop.kind === "bed") {
1410
+ return /* @__PURE__ */ jsxs5("g", { children: [
1411
+ /* @__PURE__ */ jsx18("rect", { x, y, width: "28", height: "24", fill: "rgba(178,112,68,0.88)" }),
1412
+ /* @__PURE__ */ jsx18("rect", { x: x + 4, y: y + 4, width: "20", height: "8", fill: "rgba(249,209,122,0.9)" }),
1413
+ /* @__PURE__ */ jsx18("rect", { x: x + 4, y: y + 14, width: "20", height: "7", fill: "rgba(208,94,67,0.78)" })
1414
+ ] }, `town-prop-${index}`);
1415
+ }
1416
+ if (prop.kind === "shelf") {
1417
+ return /* @__PURE__ */ jsxs5("g", { children: [
1418
+ /* @__PURE__ */ jsx18("rect", { x, y, width: Math.max(24, rect2.width - 12), height: "9", fill: "rgba(124,83,56,0.92)" }),
1419
+ /* @__PURE__ */ jsx18("rect", { x, y: y + 12, width: Math.max(24, rect2.width - 12), height: "9", fill: "rgba(124,83,56,0.84)" }),
1420
+ /* @__PURE__ */ jsx18("rect", { x: x + 5, y: y + 3, width: "5", height: "4", fill: "rgba(229,195,96,0.9)" })
1421
+ ] }, `town-prop-${index}`);
1422
+ }
1423
+ if (prop.kind === "table") {
1424
+ return /* @__PURE__ */ jsxs5("g", { children: [
1425
+ /* @__PURE__ */ jsx18("rect", { x: x + 3, y: y + 5, width: Math.max(28, rect2.width - 18), height: "18", fill: "rgba(156,103,64,0.92)" }),
1426
+ /* @__PURE__ */ jsx18("rect", { x: x + 8, y: y + 9, width: "6", height: "6", fill: "rgba(236,226,188,0.9)" })
1427
+ ] }, `town-prop-${index}`);
1428
+ }
1429
+ if (prop.kind === "sofa") {
1430
+ return /* @__PURE__ */ jsxs5("g", { children: [
1431
+ /* @__PURE__ */ jsx18("rect", { x, y: y + 4, width: "26", height: "19", fill: "rgba(99,143,178,0.88)" }),
1432
+ /* @__PURE__ */ jsx18("rect", { x: x + 4, y: y + 8, width: "18", height: "7", fill: "rgba(145,185,211,0.9)" })
1433
+ ] }, `town-prop-${index}`);
1434
+ }
1435
+ return /* @__PURE__ */ jsxs5("g", { children: [
1436
+ /* @__PURE__ */ jsx18("rect", { x, y: y + 4, width: "26", height: "16", fill: "rgba(124,83,56,0.92)" }),
1437
+ /* @__PURE__ */ jsx18("rect", { x: x + 4, y, width: "18", height: "8", fill: "rgba(194,214,221,0.9)" })
1438
+ ] }, `town-prop-${index}`);
1439
+ }
1440
+ function renderTownBuilding(building, activeZoneId) {
1441
+ const rect2 = tileToRect(building);
1442
+ const active = building.zoneId === activeZoneId;
1443
+ const doorX = rect2.x + rect2.width / 2 - 24;
1444
+ const doorY = building.entrance === "top" ? rect2.y - 6 : rect2.y + rect2.height - 10;
1445
+ const stepY = building.entrance === "top" ? rect2.y - 22 : rect2.y + rect2.height + 6;
1446
+ return /* @__PURE__ */ jsxs5("g", { children: [
1447
+ /* @__PURE__ */ jsx18("rect", { x: doorX + 4, y: stepY, width: "40", height: "22", fill: "rgba(193,165,104,0.92)", opacity: "0.9" }),
1448
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x - 6, y: rect2.y - 6, width: rect2.width + 12, height: rect2.height + 12, fill: building.wall, opacity: active ? 1 : 0.82 }),
1449
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + 8, y: rect2.y + 8, width: rect2.width - 16, height: rect2.height - 16, fill: building.floor }),
1450
+ Array.from({ length: building.cols * building.rows }, (_, index) => {
1451
+ const col = building.col + index % building.cols;
1452
+ const row = building.row + Math.floor(index / building.cols);
1453
+ const tile = tileToRect({ col, row, cols: 1, rows: 1 });
1454
+ return /* @__PURE__ */ jsx18(
1455
+ "rect",
1456
+ {
1457
+ x: tile.x + 8,
1458
+ y: tile.y + 8,
1459
+ width: TILE_SIZE - 16,
1460
+ height: TILE_SIZE - 16,
1461
+ fill: (col + row) % 2 === 0 ? "rgba(255,255,255,0.16)" : "rgba(0,0,0,0.04)"
1462
+ },
1463
+ `${building.zoneId}-floor-${col}-${row}`
1464
+ );
1465
+ }),
1466
+ building.walls.map((wall, index) => {
1467
+ const wallRect = tileToRect(wall);
1468
+ return /* @__PURE__ */ jsx18(
1469
+ "rect",
1470
+ {
1471
+ x: wallRect.x + 4,
1472
+ y: wallRect.y + 4,
1473
+ width: wallRect.width - 8,
1474
+ height: wallRect.height - 8,
1475
+ fill: building.wall,
1476
+ opacity: "0.88"
1477
+ },
1478
+ `${building.zoneId}-wall-${index}`
1479
+ );
1480
+ }),
1481
+ /* @__PURE__ */ jsx18("rect", { x: doorX, y: doorY, width: "48", height: "16", fill: "rgba(226,204,139,0.98)" }),
1482
+ /* @__PURE__ */ jsx18("rect", { x: doorX + 8, y: doorY + 4, width: "32", height: "8", fill: "rgba(91,65,44,0.55)" }),
1483
+ building.props.map(renderTownProp),
1484
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x - 8, y: rect2.y - 8, width: rect2.width + 16, height: rect2.height + 16, fill: "none", stroke: active ? ZONE_PIXEL_PALETTE[building.zoneId].stroke : "rgba(17,17,19,0.34)", strokeWidth: active ? 5 : 3 })
1485
+ ] }, `town-building-${building.zoneId}`);
1486
+ }
1487
+ function PixelAtlasMap(props) {
1488
+ const activeZoneId = props.zones.find((zone) => zone.active)?.id;
1489
+ return /* @__PURE__ */ jsxs5(
1490
+ "svg",
1491
+ {
1492
+ viewBox: `0 0 ${props.stageWidth} ${props.stageHeight}`,
1493
+ className: "pointer-events-none absolute inset-0 h-full w-full",
1494
+ preserveAspectRatio: "xMidYMid meet",
1495
+ shapeRendering: "crispEdges",
1496
+ "aria-hidden": "true",
1497
+ children: [
1498
+ /* @__PURE__ */ jsx18("rect", { x: 0, y: 0, width: STAGE_WIDTH, height: STAGE_HEIGHT, fill: "rgba(247,244,236,0.98)" }),
1499
+ renderGrassTiles(),
1500
+ TOWN_WATER_TILES.map(renderTownWater),
1501
+ TOWN_PATH_TILES.map(renderTownPath),
1502
+ renderTownPlaza(),
1503
+ TOWN_FENCE_TILES.map(renderTownFence),
1504
+ TOWN_SHRUB_POINTS.map(renderShrub),
1505
+ TOWN_TREE_POINTS.map(renderTree),
1506
+ TOWN_FLOWER_POINTS.map(renderFlower),
1507
+ TOWN_BUILDINGS.map((building) => renderTownBuilding(building, activeZoneId)),
1508
+ props.zones.map((zone) => {
1509
+ const building = TOWN_BUILDINGS.find((item) => item.zoneId === zone.id);
1510
+ if (!building) return null;
1511
+ const layout = WORKBOARD_ZONE_LAYOUT[zone.id];
1512
+ const active = zone.active;
1513
+ const palette = ZONE_PIXEL_PALETTE[zone.id];
1514
+ const rect2 = tileToRect(building);
1515
+ const label = fitPixelLabel(zone.title, 112);
1516
+ const signWidth = Math.max(104, estimateTextWidth(label) + 34);
1517
+ const signX = rect2.x + 14;
1518
+ const signY = rect2.y - 30;
1519
+ const hubX = layout.hubX / 100 * STAGE_WIDTH;
1520
+ const hubY = layout.hubY / 100 * STAGE_HEIGHT;
1521
+ return /* @__PURE__ */ jsxs5("g", { children: [
1522
+ /* @__PURE__ */ jsx18("rect", { x: signX - 5, y: signY + 5, width: signWidth, height: "22", fill: "rgba(72,50,33,0.34)" }),
1523
+ /* @__PURE__ */ jsx18("rect", { x: signX, y: signY, width: signWidth, height: "22", fill: "rgba(250,236,178,0.98)", stroke: palette.stroke, strokeWidth: active ? 4 : 3 }),
1524
+ /* @__PURE__ */ jsx18("rect", { x: signX + 7, y: signY + 22, width: "6", height: "16", fill: "rgba(111,78,47,0.86)" }),
1525
+ /* @__PURE__ */ jsx18("rect", { x: signX + signWidth - 13, y: signY + 22, width: "6", height: "16", fill: "rgba(111,78,47,0.86)" }),
1526
+ /* @__PURE__ */ jsx18("rect", { x: hubX - 16, y: hubY - 16, width: "32", height: "32", fill: "rgba(255,252,247,0.94)", stroke: palette.stroke, strokeWidth: "3" }),
1527
+ /* @__PURE__ */ jsx18("rect", { x: hubX - 8, y: hubY - 8, width: "16", height: "16", fill: palette.fillStrong }),
1528
+ /* @__PURE__ */ jsx18("rect", { x: hubX - 3, y: hubY - 3, width: "6", height: "6", fill: palette.stroke }),
1529
+ /* @__PURE__ */ jsx18(
1530
+ "text",
1531
+ {
1532
+ x: signX + 10,
1533
+ y: signY + 15,
1534
+ fill: "rgba(17,17,19,0.78)",
1535
+ fontSize: "10",
1536
+ fontWeight: "700",
1537
+ fontFamily: "var(--font-geist-mono, var(--font-sans))",
1538
+ children: label
1539
+ }
1540
+ ),
1541
+ /* @__PURE__ */ jsx18("rect", { x: rect2.x + rect2.width - 44, y: rect2.y + 14, width: "30", height: "24", fill: palette.fillStrong, stroke: "rgba(17,17,19,0.34)", strokeWidth: "2" }),
1542
+ /* @__PURE__ */ jsx18(
1543
+ "text",
1544
+ {
1545
+ x: rect2.x + rect2.width - 29,
1546
+ y: rect2.y + 31,
1547
+ textAnchor: "end",
1548
+ fill: palette.stroke,
1549
+ fontSize: "15",
1550
+ fontWeight: "800",
1551
+ fontFamily: "var(--font-geist-mono, var(--font-sans))",
1552
+ children: zone.count
1553
+ }
1554
+ )
1555
+ ] }, `zone-${zone.id}`);
1556
+ })
1557
+ ]
1558
+ }
1559
+ );
1560
+ }
1561
+ function PixelRoute(props) {
1562
+ if (props.points.length < 2) return null;
1563
+ const d = props.points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x} ${point.y}`).join(" ");
1564
+ return /* @__PURE__ */ jsx18(
1565
+ "path",
1566
+ {
1567
+ d,
1568
+ fill: "none",
1569
+ className: props.className,
1570
+ strokeWidth: 3,
1571
+ strokeLinecap: "square",
1572
+ strokeLinejoin: "miter",
1573
+ strokeDasharray: props.dashed ? "8 8" : void 0
1574
+ }
1575
+ );
1576
+ }
1577
+ function PixelHoverTag(props) {
1578
+ if (!props.tag) return null;
1579
+ const text = fitPixelLabel(props.tag.label, 160);
1580
+ const width = Math.max(78, estimateTextWidth(text) + 18);
1581
+ const height = 20;
1582
+ const x = Math.min(Math.max(props.tag.x - width / 2, 6), props.stageWidth - width - 6);
1583
+ const y = Math.min(Math.max(props.tag.y - height - 14, 6), props.stageHeight - height - 6);
1584
+ return /* @__PURE__ */ jsxs5("g", { pointerEvents: "none", children: [
1585
+ /* @__PURE__ */ jsx18("rect", { x, y, width, height, fill: "rgba(255,252,247,0.98)", stroke: "rgba(17,17,19,0.44)", strokeWidth: "2" }),
1586
+ /* @__PURE__ */ jsx18("rect", { x: x + 10, y: y + height, width: "10", height: "6", fill: "rgba(255,252,247,0.98)", stroke: "rgba(17,17,19,0.44)", strokeWidth: "2" }),
1587
+ /* @__PURE__ */ jsx18(
1588
+ "text",
1589
+ {
1590
+ x: x + 8,
1591
+ y: y + 13,
1592
+ fill: "rgba(17,17,19,0.72)",
1593
+ fontSize: "10",
1594
+ fontWeight: "700",
1595
+ fontFamily: "var(--font-geist-mono, var(--font-sans))",
1596
+ children: text
1597
+ }
1598
+ )
1599
+ ] });
1600
+ }
1601
+
1602
+ // src/components/workboard-pixel-agent.tsx
1603
+ import * as React3 from "react";
1604
+ import { jsx as jsx19, jsxs as jsxs6 } from "react/jsx-runtime";
1605
+ var PIXEL_PALETTES = [
1606
+ {
1607
+ skin: "#f1c7a5",
1608
+ hair: "#4c3327",
1609
+ outfit: "#3f7c63",
1610
+ accent: "#d4eadf",
1611
+ outline: "#241a16"
1612
+ },
1613
+ {
1614
+ skin: "#dbb08a",
1615
+ hair: "#2d2a3b",
1616
+ outfit: "#8b6b37",
1617
+ accent: "#ead6bf",
1618
+ outline: "#231f18"
1619
+ },
1620
+ {
1621
+ skin: "#f0d1b5",
1622
+ hair: "#70462d",
1623
+ outfit: "#667b42",
1624
+ accent: "#dde8c7",
1625
+ outline: "#2a251d"
1626
+ },
1627
+ {
1628
+ skin: "#c98d66",
1629
+ hair: "#3b281f",
1630
+ outfit: "#925a45",
1631
+ accent: "#efdfd1",
1632
+ outline: "#241b16"
1633
+ }
1634
+ ];
1635
+ function hashText(value) {
1636
+ let hash = 0;
1637
+ for (const char of value) {
1638
+ hash = hash * 31 + char.charCodeAt(0) >>> 0;
1639
+ }
1640
+ return hash;
1641
+ }
1642
+ function rect(x, y, w, h, fill) {
1643
+ return `<rect x="${x}" y="${y}" width="${w}" height="${h}" fill="${fill}" />`;
1644
+ }
1645
+ function buildPixelAvatarSvg(params) {
1646
+ const seed = hashText(`${params.agentId}:${params.name}`);
1647
+ const palette = PIXEL_PALETTES[seed % PIXEL_PALETTES.length];
1648
+ const hairVariant = seed % 3;
1649
+ const eyeVariant = (seed >> 2) % 2;
1650
+ const accentVariant = (seed >> 4) % 3;
1651
+ const facingUp = params.direction === "up";
1652
+ const facingSide = params.direction === "left" || params.direction === "right";
1653
+ const parts = [
1654
+ `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" shape-rendering="crispEdges">`,
1655
+ rect(0, 0, 16, 16, "transparent"),
1656
+ rect(3, 2, 10, 2, palette.hair),
1657
+ rect(2, 4, 12, 1, palette.hair),
1658
+ rect(3, 5, 10, 5, palette.skin),
1659
+ rect(4, 10, 8, 4, palette.outfit),
1660
+ rect(2, 10, 2, 3, palette.outfit),
1661
+ rect(12, 10, 2, 3, palette.outfit),
1662
+ rect(5, 14, 2, 2, palette.outline),
1663
+ rect(9, 14, 2, 2, palette.outline),
1664
+ rect(4, 11, 1, 3, palette.outline),
1665
+ rect(11, 11, 1, 3, palette.outline),
1666
+ rect(6, 4, 4, 1, palette.accent)
1667
+ ];
1668
+ if (facingUp) {
1669
+ parts.push(
1670
+ rect(3, 4, 10, 5, palette.hair),
1671
+ rect(5, 8, 6, 2, palette.hair),
1672
+ rect(6, 11, 4, 2, palette.accent)
1673
+ );
1674
+ } else if (facingSide) {
1675
+ parts.push(
1676
+ rect(5, 6, 1, 1, palette.outline),
1677
+ rect(9, 7, 2, 1, palette.outline),
1678
+ rect(8, 8, 2, 1, "#9d6f4d"),
1679
+ rect(11, 5, 2, 4, palette.hair)
1680
+ );
1681
+ } else {
1682
+ parts.push(
1683
+ rect(6, 6, 1, 1, palette.outline),
1684
+ rect(9, 6, 1, 1, palette.outline),
1685
+ rect(7, 8, 2, 1, "#9d6f4d")
1686
+ );
1687
+ }
1688
+ if (hairVariant === 0) {
1689
+ parts.push(rect(2, 3, 1, 4, palette.hair), rect(13, 3, 1, 4, palette.hair));
1690
+ } else if (hairVariant === 1) {
1691
+ parts.push(rect(2, 3, 2, 2, palette.hair), rect(12, 3, 2, 2, palette.hair));
1692
+ } else {
1693
+ parts.push(rect(4, 1, 8, 1, palette.hair), rect(3, 2, 1, 2, palette.hair), rect(12, 2, 1, 2, palette.hair));
1694
+ }
1695
+ if (!facingUp && eyeVariant === 1) {
1696
+ parts.push(rect(5, 6, 1, 1, palette.outline), rect(10, 6, 1, 1, palette.outline));
1697
+ }
1698
+ if (accentVariant === 0) {
1699
+ parts.push(rect(6, 11, 4, 1, palette.accent));
1700
+ } else if (accentVariant === 1) {
1701
+ parts.push(rect(4, 12, 8, 1, palette.accent));
1702
+ } else {
1703
+ parts.push(rect(7, 10, 2, 3, palette.accent));
1704
+ }
1705
+ parts.push(rect(4, 9, 1, 1, palette.outline), rect(11, 9, 1, 1, palette.outline), `</svg>`);
1706
+ return parts.join("");
1707
+ }
1708
+ function svgToDataUri(svg) {
1709
+ return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
1710
+ }
1711
+ function WorkboardPixelAgent(props) {
1712
+ const src = React3.useMemo(
1713
+ () => svgToDataUri(buildPixelAvatarSvg({ agentId: props.agentId, name: props.name, direction: props.direction })),
1714
+ [props.agentId, props.name, props.direction]
1715
+ );
1716
+ const flipped = props.direction === "left";
1717
+ return /* @__PURE__ */ jsxs6(
1718
+ "span",
1719
+ {
1720
+ className: cn(
1721
+ "relative inline-flex overflow-visible",
1722
+ props.faded ? "opacity-65" : "opacity-100",
1723
+ props.className
1724
+ ),
1725
+ style: { width: props.size, height: props.size },
1726
+ "aria-hidden": "true",
1727
+ children: [
1728
+ /* @__PURE__ */ jsx19("span", { className: "absolute bottom-[1px] left-1/2 h-[4px] w-[70%] -translate-x-1/2 bg-[rgba(45,36,25,0.24)]" }),
1729
+ /* @__PURE__ */ jsx19(
1730
+ "img",
1731
+ {
1732
+ src,
1733
+ alt: "",
1734
+ width: props.size,
1735
+ height: props.size,
1736
+ draggable: false,
1737
+ className: "h-full w-full [image-rendering:pixelated]",
1738
+ style: {
1739
+ animation: props.walking ? "workboard-sprite-step 0.42s steps(2, end) infinite" : void 0,
1740
+ transform: flipped ? "scaleX(-1)" : void 0
1741
+ }
1742
+ }
1743
+ )
1744
+ ]
1745
+ }
1746
+ );
1747
+ }
1748
+
1749
+ // src/components/workboard-room-layout.ts
1750
+ var WORKBOARD_ROOM_PALETTE = {
1751
+ engaged: {
1752
+ floorA: "rgba(217,160,111,0.98)",
1753
+ floorB: "rgba(198,139,94,0.98)",
1754
+ wall: "rgba(107,58,45,0.98)",
1755
+ wallLight: "rgba(154,86,61,0.94)",
1756
+ rug: "rgba(183,69,50,0.72)",
1757
+ rugStrong: "rgba(226,118,77,0.86)",
1758
+ accent: "rgba(39,110,80,0.95)"
1759
+ },
1760
+ steady: {
1761
+ floorA: "rgba(218,190,111,0.98)",
1762
+ floorB: "rgba(197,166,91,0.98)",
1763
+ wall: "rgba(94,76,51,0.98)",
1764
+ wallLight: "rgba(139,113,70,0.94)",
1765
+ rug: "rgba(148,169,72,0.72)",
1766
+ rugStrong: "rgba(188,207,91,0.88)",
1767
+ accent: "rgba(112,132,38,0.92)"
1768
+ },
1769
+ quiet: {
1770
+ floorA: "rgba(205,194,166,0.98)",
1771
+ floorB: "rgba(186,174,148,0.98)",
1772
+ wall: "rgba(89,82,73,0.98)",
1773
+ wallLight: "rgba(126,117,103,0.94)",
1774
+ rug: "rgba(123,132,144,0.62)",
1775
+ rugStrong: "rgba(171,179,186,0.86)",
1776
+ accent: "rgba(103,96,87,0.9)"
1777
+ },
1778
+ drift: {
1779
+ floorA: "rgba(223,166,95,0.98)",
1780
+ floorB: "rgba(202,137,75,0.98)",
1781
+ wall: "rgba(121,66,36,0.98)",
1782
+ wallLight: "rgba(172,94,49,0.94)",
1783
+ rug: "rgba(196,89,44,0.72)",
1784
+ rugStrong: "rgba(236,145,65,0.9)",
1785
+ accent: "rgba(161,91,32,0.92)"
1786
+ }
1787
+ };
1788
+ var LARGE_ROOM_FLOORS = [
1789
+ { col: 2, row: 3, cols: 8, rows: 6 },
1790
+ { col: 11, row: 3, cols: 8, rows: 6 },
1791
+ { col: 21, row: 3, cols: 8, rows: 6 },
1792
+ { col: 30, row: 3, cols: 8, rows: 6 },
1793
+ { col: 2, row: 15, cols: 8, rows: 7 },
1794
+ { col: 11, row: 15, cols: 8, rows: 7 },
1795
+ { col: 21, row: 15, cols: 8, rows: 7 },
1796
+ { col: 30, row: 15, cols: 8, rows: 7 }
1797
+ ];
1798
+ var LARGE_ROOM_CORRIDORS = [
1799
+ { col: 1, row: 10, cols: 38, rows: 4 },
1800
+ { col: 19, row: 0, cols: 2, rows: 24 },
1801
+ { col: 9, row: 8, cols: 11, rows: 2 },
1802
+ { col: 20, row: 8, cols: 11, rows: 2 },
1803
+ { col: 9, row: 14, cols: 11, rows: 2 },
1804
+ { col: 20, row: 14, cols: 11, rows: 2 }
1805
+ ];
1806
+ var LARGE_ROOM_WALLS = [
1807
+ { col: 1, row: 2, cols: 38, rows: 1 },
1808
+ { col: 1, row: 22, cols: 38, rows: 1 },
1809
+ { col: 1, row: 2, cols: 1, rows: 21 },
1810
+ { col: 38, row: 2, cols: 1, rows: 21 },
1811
+ { col: 10, row: 3, cols: 1, rows: 6 },
1812
+ { col: 20, row: 3, cols: 1, rows: 6 },
1813
+ { col: 29, row: 3, cols: 1, rows: 6 },
1814
+ { col: 10, row: 15, cols: 1, rows: 7 },
1815
+ { col: 20, row: 15, cols: 1, rows: 7 },
1816
+ { col: 29, row: 15, cols: 1, rows: 7 },
1817
+ { col: 2, row: 9, cols: 17, rows: 1 },
1818
+ { col: 21, row: 9, cols: 17, rows: 1 },
1819
+ { col: 2, row: 14, cols: 17, rows: 1 },
1820
+ { col: 21, row: 14, cols: 17, rows: 1 }
1821
+ ];
1822
+ var LARGE_ROOM_DOORS = [
1823
+ { col: 5, row: 9, cols: 2, rows: 1 },
1824
+ { col: 14, row: 9, cols: 2, rows: 1 },
1825
+ { col: 24, row: 9, cols: 2, rows: 1 },
1826
+ { col: 33, row: 9, cols: 2, rows: 1 },
1827
+ { col: 5, row: 14, cols: 2, rows: 1 },
1828
+ { col: 14, row: 14, cols: 2, rows: 1 },
1829
+ { col: 24, row: 14, cols: 2, rows: 1 },
1830
+ { col: 33, row: 14, cols: 2, rows: 1 },
1831
+ { col: 20, row: 5, cols: 1, rows: 2 },
1832
+ { col: 20, row: 17, cols: 1, rows: 2 }
1833
+ ];
1834
+ var LARGE_ROOM_EXTERIOR_PATHS = [
1835
+ { col: 0, row: 10, cols: 40, rows: 4 },
1836
+ { col: 19, row: 0, cols: 2, rows: 24 },
1837
+ { col: 0, row: 0, cols: 6, rows: 1 },
1838
+ { col: 34, row: 0, cols: 6, rows: 1 },
1839
+ { col: 0, row: 23, cols: 6, rows: 1 },
1840
+ { col: 34, row: 23, cols: 6, rows: 1 }
1841
+ ];
1842
+ var LARGE_ROOM_TREES = [
1843
+ { col: 1, row: 2 },
1844
+ { col: 13, row: 1 },
1845
+ { col: 26, row: 1 },
1846
+ { col: 38, row: 2 },
1847
+ { col: 1, row: 22 },
1848
+ { col: 13, row: 22 },
1849
+ { col: 26, row: 22 },
1850
+ { col: 38, row: 22 }
1851
+ ];
1852
+ var LARGE_ROOM_SHRUBS = [
1853
+ { col: 6, row: 1 },
1854
+ { col: 12, row: 9 },
1855
+ { col: 27, row: 9 },
1856
+ { col: 33, row: 1 },
1857
+ { col: 6, row: 22 },
1858
+ { col: 12, row: 14 },
1859
+ { col: 27, row: 14 },
1860
+ { col: 33, row: 22 }
1861
+ ];
1862
+ function createRoomPlan(zoneId) {
1863
+ const focusedRugs = {
1864
+ engaged: [
1865
+ { col: 17, row: 9, cols: 6, rows: 6 },
1866
+ { col: 2, row: 3, cols: 8, rows: 6 },
1867
+ { col: 30, row: 3, cols: 8, rows: 6 }
1868
+ ],
1869
+ steady: [
1870
+ { col: 2, row: 10, cols: 36, rows: 4 },
1871
+ { col: 18, row: 3, cols: 4, rows: 19 }
1872
+ ],
1873
+ quiet: [
1874
+ { col: 17, row: 9, cols: 6, rows: 6 },
1875
+ { col: 2, row: 15, cols: 8, rows: 7 },
1876
+ { col: 30, row: 15, cols: 8, rows: 7 }
1877
+ ],
1878
+ drift: [
1879
+ { col: 16, row: 8, cols: 8, rows: 8 },
1880
+ { col: 2, row: 3, cols: 8, rows: 6 },
1881
+ { col: 30, row: 15, cols: 8, rows: 7 }
1882
+ ]
1883
+ };
1884
+ const focusedProps = {
1885
+ engaged: [
1886
+ { id: "engaged-board", kind: "board", x: 140, y: 654 },
1887
+ { id: "engaged-blueprint", kind: "blueprint", x: 1260, y: 650 },
1888
+ { id: "engaged-console-a", kind: "console", x: 710, y: 158 },
1889
+ { id: "engaged-rack-a", kind: "rack", x: 1352, y: 152 },
1890
+ { id: "engaged-table-a", kind: "table", x: 206, y: 206 },
1891
+ { id: "engaged-table-b", kind: "table", x: 1214, y: 688 }
1892
+ ],
1893
+ steady: [
1894
+ { id: "steady-board", kind: "board", x: 140, y: 654 },
1895
+ { id: "steady-blueprint", kind: "blueprint", x: 1260, y: 650 },
1896
+ { id: "steady-bench-a", kind: "bench", x: 210, y: 200 },
1897
+ { id: "steady-rack-a", kind: "rack", x: 706, y: 156 },
1898
+ { id: "steady-crate-a", kind: "crate", x: 904, y: 682 },
1899
+ { id: "steady-console-a", kind: "console", x: 1326, y: 192 }
1900
+ ],
1901
+ quiet: [
1902
+ { id: "quiet-board", kind: "board", x: 140, y: 654 },
1903
+ { id: "quiet-blueprint", kind: "blueprint", x: 1260, y: 650 },
1904
+ { id: "quiet-bed-a", kind: "bed", x: 196, y: 182 },
1905
+ { id: "quiet-bed-b", kind: "bed", x: 1292, y: 182 },
1906
+ { id: "quiet-bed-c", kind: "bed", x: 684, y: 684 },
1907
+ { id: "quiet-plant-a", kind: "plant", x: 906, y: 166 }
1908
+ ],
1909
+ drift: [
1910
+ { id: "drift-board", kind: "board", x: 140, y: 654 },
1911
+ { id: "drift-blueprint", kind: "blueprint", x: 1260, y: 650 },
1912
+ { id: "drift-console-a", kind: "console", x: 706, y: 156 },
1913
+ { id: "drift-rack-a", kind: "rack", x: 1328, y: 190 },
1914
+ { id: "drift-crate-a", kind: "crate", x: 210, y: 684 },
1915
+ { id: "drift-bench-a", kind: "bench", x: 1190, y: 686 }
1916
+ ]
1917
+ };
1918
+ return {
1919
+ floors: LARGE_ROOM_FLOORS,
1920
+ corridors: LARGE_ROOM_CORRIDORS,
1921
+ rugs: focusedRugs[zoneId],
1922
+ walls: LARGE_ROOM_WALLS,
1923
+ doors: LARGE_ROOM_DOORS,
1924
+ exteriorPaths: LARGE_ROOM_EXTERIOR_PATHS,
1925
+ trees: LARGE_ROOM_TREES,
1926
+ shrubs: LARGE_ROOM_SHRUBS,
1927
+ props: focusedProps[zoneId]
1928
+ };
1929
+ }
1930
+ var WORKBOARD_ROOM_PLANS = {
1931
+ engaged: createRoomPlan("engaged"),
1932
+ steady: createRoomPlan("steady"),
1933
+ quiet: createRoomPlan("quiet"),
1934
+ drift: createRoomPlan("drift")
1935
+ };
1936
+ var WORKBOARD_FOCUSED_STATIONS_BY_ZONE = {
1937
+ engaged: [
1938
+ { x: 250, y: 206 },
1939
+ { x: 686, y: 194 },
1940
+ { x: 1236, y: 206 },
1941
+ { x: 360, y: 460 },
1942
+ { x: 940, y: 460 },
1943
+ { x: 250, y: 714 },
1944
+ { x: 686, y: 730 },
1945
+ { x: 1236, y: 714 }
1946
+ ],
1947
+ steady: [
1948
+ { x: 246, y: 196 },
1949
+ { x: 668, y: 206 },
1950
+ { x: 934, y: 206 },
1951
+ { x: 1350, y: 196 },
1952
+ { x: 366, y: 480 },
1953
+ { x: 250, y: 728 },
1954
+ { x: 800, y: 730 },
1955
+ { x: 1350, y: 728 }
1956
+ ],
1957
+ quiet: [
1958
+ { x: 250, y: 206 },
1959
+ { x: 688, y: 206 },
1960
+ { x: 1238, y: 206 },
1961
+ { x: 360, y: 480 },
1962
+ { x: 940, y: 480 },
1963
+ { x: 250, y: 714 },
1964
+ { x: 688, y: 730 },
1965
+ { x: 1238, y: 714 }
1966
+ ],
1967
+ drift: [
1968
+ { x: 250, y: 206 },
1969
+ { x: 688, y: 194 },
1970
+ { x: 1238, y: 206 },
1971
+ { x: 360, y: 480 },
1972
+ { x: 940, y: 480 },
1973
+ { x: 250, y: 714 },
1974
+ { x: 800, y: 730 },
1975
+ { x: 1238, y: 714 }
1976
+ ]
1977
+ };
1978
+ var WORKBOARD_FOCUSED_PATROL_ROUTES = {
1979
+ engaged: [
1980
+ [
1981
+ { x: 240, y: 200 },
1982
+ { x: 800, y: 200 },
1983
+ { x: 1360, y: 200 },
1984
+ { x: 1360, y: 480 },
1985
+ { x: 1360, y: 720 },
1986
+ { x: 800, y: 720 },
1987
+ { x: 240, y: 720 },
1988
+ { x: 240, y: 480 }
1989
+ ],
1990
+ [
1991
+ { x: 420, y: 320 },
1992
+ { x: 800, y: 320 },
1993
+ { x: 1180, y: 320 },
1994
+ { x: 1180, y: 480 },
1995
+ { x: 1180, y: 620 },
1996
+ { x: 800, y: 620 },
1997
+ { x: 420, y: 620 },
1998
+ { x: 420, y: 480 }
1999
+ ],
2000
+ [
2001
+ { x: 240, y: 480 },
2002
+ { x: 800, y: 480 },
2003
+ { x: 1360, y: 480 },
2004
+ { x: 800, y: 480 },
2005
+ { x: 800, y: 200 },
2006
+ { x: 800, y: 720 },
2007
+ { x: 800, y: 480 }
2008
+ ]
2009
+ ],
2010
+ steady: [
2011
+ [
2012
+ { x: 240, y: 480 },
2013
+ { x: 800, y: 480 },
2014
+ { x: 1360, y: 480 },
2015
+ { x: 1360, y: 720 },
2016
+ { x: 800, y: 720 },
2017
+ { x: 240, y: 720 }
2018
+ ],
2019
+ [
2020
+ { x: 240, y: 200 },
2021
+ { x: 800, y: 200 },
2022
+ { x: 1360, y: 200 },
2023
+ { x: 1360, y: 480 },
2024
+ { x: 800, y: 480 },
2025
+ { x: 240, y: 480 }
2026
+ ],
2027
+ [
2028
+ { x: 800, y: 160 },
2029
+ { x: 800, y: 800 },
2030
+ { x: 1080, y: 800 },
2031
+ { x: 1080, y: 480 },
2032
+ { x: 520, y: 480 },
2033
+ { x: 520, y: 800 },
2034
+ { x: 800, y: 800 }
2035
+ ]
2036
+ ],
2037
+ quiet: [
2038
+ [
2039
+ { x: 240, y: 200 },
2040
+ { x: 800, y: 200 },
2041
+ { x: 1360, y: 200 },
2042
+ { x: 1360, y: 720 },
2043
+ { x: 800, y: 720 },
2044
+ { x: 240, y: 720 }
2045
+ ],
2046
+ [
2047
+ { x: 420, y: 480 },
2048
+ { x: 800, y: 480 },
2049
+ { x: 1180, y: 480 },
2050
+ { x: 1180, y: 640 },
2051
+ { x: 800, y: 640 },
2052
+ { x: 420, y: 640 }
2053
+ ],
2054
+ [
2055
+ { x: 800, y: 200 },
2056
+ { x: 800, y: 720 },
2057
+ { x: 420, y: 720 },
2058
+ { x: 420, y: 480 },
2059
+ { x: 1180, y: 480 },
2060
+ { x: 1180, y: 720 },
2061
+ { x: 800, y: 720 }
2062
+ ]
2063
+ ],
2064
+ drift: [
2065
+ [
2066
+ { x: 240, y: 200 },
2067
+ { x: 800, y: 200 },
2068
+ { x: 1360, y: 200 },
2069
+ { x: 1360, y: 620 },
2070
+ { x: 800, y: 720 },
2071
+ { x: 240, y: 620 }
2072
+ ],
2073
+ [
2074
+ { x: 240, y: 480 },
2075
+ { x: 800, y: 480 },
2076
+ { x: 1360, y: 480 },
2077
+ { x: 1360, y: 720 },
2078
+ { x: 800, y: 720 },
2079
+ { x: 240, y: 720 }
2080
+ ],
2081
+ [
2082
+ { x: 800, y: 160 },
2083
+ { x: 800, y: 720 },
2084
+ { x: 1080, y: 620 },
2085
+ { x: 1080, y: 320 },
2086
+ { x: 520, y: 320 },
2087
+ { x: 520, y: 620 },
2088
+ { x: 800, y: 720 }
2089
+ ]
2090
+ ]
2091
+ };
2092
+
2093
+ // src/components/workboard-stage.tsx
2094
+ import { Fragment, jsx as jsx20, jsxs as jsxs7 } from "react/jsx-runtime";
2095
+ var WORKBOARD_STAGE_HEIGHT = 960;
2096
+ var WORKBOARD_STAGE_WIDTH = 1600;
2097
+ var NODE_PLACEMENTS = [
2098
+ { left: 18, top: 26, delay: 0 },
2099
+ { left: 42, top: 19, delay: 0.4 },
2100
+ { left: 66, top: 29, delay: 0.8 },
2101
+ { left: 29, top: 54, delay: 1.2 },
2102
+ { left: 57, top: 56, delay: 1.6 },
2103
+ { left: 75, top: 47, delay: 2 },
2104
+ { left: 14, top: 70, delay: 2.4 },
2105
+ { left: 46, top: 74, delay: 2.8 }
2106
+ ];
2107
+ var WORKBOARD_ZONE_DEFINITIONS = [
2108
+ {
2109
+ id: "engaged",
2110
+ title: "\u5C55\u5F00\u4E2D",
2111
+ subtitle: "active field",
2112
+ description: "\u5F53\u524D\u5BF9\u5916\u6700\u6709\u52A8\u52BF\u7684\u4E00\u7EC4 agent\u3002",
2113
+ badge: "Live",
2114
+ areaClassName: "bg-[linear-gradient(145deg,rgba(238,248,244,0.92),rgba(248,251,249,0.78))]",
2115
+ borderClassName: "border-emerald-300/65",
2116
+ glowClassName: "bg-[radial-gradient(circle,rgba(52,144,111,0.18),transparent_70%)]",
2117
+ nodeClassName: "border-emerald-300/70 bg-[linear-gradient(145deg,rgba(248,252,250,0.98),rgba(235,247,242,0.94))]",
2118
+ lineClassName: "stroke-emerald-400/55"
2119
+ },
2120
+ {
2121
+ id: "steady",
2122
+ title: "\u6301\u7EED\u63A8\u8FDB",
2123
+ subtitle: "steady lane",
2124
+ description: "\u5904\u4E8E\u7A33\u5B9A\u8282\u594F\uFF0C\u6301\u7EED\u5411\u524D\u63A8\u8FDB\u7684\u4E00\u7EC4 agent\u3002",
2125
+ badge: "Steady",
2126
+ areaClassName: "bg-[linear-gradient(145deg,rgba(244,246,236,0.9),rgba(252,250,244,0.75))]",
2127
+ borderClassName: "border-lime-300/60",
2128
+ glowClassName: "bg-[radial-gradient(circle,rgba(151,169,72,0.16),transparent_72%)]",
2129
+ nodeClassName: "border-lime-300/65 bg-[linear-gradient(145deg,rgba(252,252,247,0.98),rgba(243,246,233,0.95))]",
2130
+ lineClassName: "stroke-lime-400/50"
2131
+ },
2132
+ {
2133
+ id: "quiet",
2134
+ title: "\u9759\u5019\u4E2D",
2135
+ subtitle: "quiet deck",
2136
+ description: "\u6682\u65F6\u5B89\u9759\u3001\u7B49\u5F85\u4E0B\u4E00\u6B21\u89E6\u53D1\u7684\u4E00\u7EC4 agent\u3002",
2137
+ badge: "Quiet",
2138
+ areaClassName: "bg-[linear-gradient(145deg,rgba(244,243,239,0.92),rgba(251,250,248,0.76))]",
2139
+ borderClassName: "border-stone-300/65",
2140
+ glowClassName: "bg-[radial-gradient(circle,rgba(130,124,112,0.14),transparent_72%)]",
2141
+ nodeClassName: "border-stone-300/65 bg-[linear-gradient(145deg,rgba(252,251,249,0.98),rgba(241,239,234,0.96))]",
2142
+ lineClassName: "stroke-stone-400/50"
2143
+ },
2144
+ {
2145
+ id: "drift",
2146
+ title: "\u8F7B\u5FAE\u6CE2\u52A8",
2147
+ subtitle: "watch deck",
2148
+ description: "\u51FA\u73B0\u4E2D\u65AD\u3001\u544A\u8B66\u6216\u9700\u8981\u91CD\u65B0\u89C2\u5BDF\u7684\u4E00\u7EC4 agent\u3002",
2149
+ badge: "Watch",
2150
+ areaClassName: "bg-[linear-gradient(145deg,rgba(251,241,235,0.92),rgba(252,248,244,0.76))]",
2151
+ borderClassName: "border-amber-300/70",
2152
+ glowClassName: "bg-[radial-gradient(circle,rgba(194,121,55,0.18),transparent_72%)]",
2153
+ nodeClassName: "border-amber-300/70 bg-[linear-gradient(145deg,rgba(253,250,245,0.98),rgba(249,239,229,0.95))]",
2154
+ lineClassName: "stroke-amber-400/55"
2155
+ }
2156
+ ];
2157
+ function createHoverTag(params) {
2158
+ return {
2159
+ id: params.id,
2160
+ label: params.label,
2161
+ x: params.point.x,
2162
+ y: params.point.y
2163
+ };
2164
+ }
2165
+ function resolveAgentGlyph(item) {
2166
+ const hasIssue = item.snapshot.current.some((entry) => entry.status === "issue") || item.snapshot.recent.some((entry) => entry.status === "issue") || item.snapshot.signals.some((entry) => entry.tone === "warning");
2167
+ if (hasIssue) {
2168
+ return {
2169
+ label: "!",
2170
+ className: "border-amber-700/50 bg-amber-400 text-amber-950"
2171
+ };
2172
+ }
2173
+ if (item.snapshot.current.some((entry) => entry.status === "active")) {
2174
+ return {
2175
+ label: ">",
2176
+ className: "border-emerald-700/45 bg-emerald-400 text-emerald-950"
2177
+ };
2178
+ }
2179
+ if (item.running) {
2180
+ return {
2181
+ label: "~",
2182
+ className: "border-lime-700/45 bg-lime-300 text-lime-950"
2183
+ };
2184
+ }
2185
+ return {
2186
+ label: ".",
2187
+ className: "border-stone-500/45 bg-stone-300 text-stone-800"
2188
+ };
2189
+ }
2190
+ function resolveZoneId(item) {
2191
+ const hasIssue = item.snapshot.current.some((entry) => entry.status === "issue") || item.snapshot.recent.some((entry) => entry.status === "issue") || item.snapshot.signals.some((entry) => entry.tone === "warning");
2192
+ if (hasIssue) return "drift";
2193
+ const hasActive = item.snapshot.current.some((entry) => entry.status === "active");
2194
+ if (hasActive) return "engaged";
2195
+ const hasRecentMotion = item.running && (item.snapshot.current.length > 0 || item.snapshot.recent.length > 0 || item.currentCount > 0);
2196
+ if (hasRecentMotion) return "steady";
2197
+ return "quiet";
2198
+ }
2199
+ function resolveZoneDefinition(zoneId) {
2200
+ return WORKBOARD_ZONE_DEFINITIONS.find((item) => item.id === zoneId) || WORKBOARD_ZONE_DEFINITIONS[0];
2201
+ }
2202
+ function formatWorkboardRelativeTime(value) {
2203
+ if (!value) return "-";
2204
+ const date = new Date(value);
2205
+ if (Number.isNaN(date.getTime())) return value;
2206
+ const delta = Math.max(0, Date.now() - date.getTime());
2207
+ const minutes = Math.floor(delta / 6e4);
2208
+ if (minutes < 1) return "just now";
2209
+ if (minutes < 60) return `${minutes}m ago`;
2210
+ const hours = Math.floor(minutes / 60);
2211
+ if (hours < 24) return `${hours}h ago`;
2212
+ const days = Math.floor(hours / 24);
2213
+ return `${days}d ago`;
2214
+ }
2215
+ function buildWorkboardTilePath(params) {
2216
+ const midX = params.from.x + (params.to.x - params.from.x) * 0.5;
2217
+ return `M ${params.from.x} ${params.from.y} H ${midX} V ${params.to.y} H ${params.to.x}`;
2218
+ }
2219
+ function toStagePoint(zoneId, placement) {
2220
+ const zone = WORKBOARD_ZONE_LAYOUT[zoneId];
2221
+ const left = zone.x + zone.w * placement.left / 100;
2222
+ const top = zone.y + zone.h * placement.top / 100;
2223
+ return {
2224
+ x: left / 100 * WORKBOARD_STAGE_WIDTH,
2225
+ y: top / 100 * WORKBOARD_STAGE_HEIGHT
2226
+ };
2227
+ }
2228
+ function toZoneHubPoint(zoneId) {
2229
+ const zone = WORKBOARD_ZONE_LAYOUT[zoneId];
2230
+ return {
2231
+ x: zone.hubX / 100 * WORKBOARD_STAGE_WIDTH,
2232
+ y: zone.hubY / 100 * WORKBOARD_STAGE_HEIGHT
2233
+ };
2234
+ }
2235
+ function deriveStageNodes(board) {
2236
+ const items = board?.agents || [];
2237
+ const buckets = /* @__PURE__ */ new Map();
2238
+ items.forEach((item) => {
2239
+ const zoneId = resolveZoneId(item);
2240
+ const group = buckets.get(zoneId) || [];
2241
+ group.push(item);
2242
+ buckets.set(zoneId, group);
2243
+ });
2244
+ return WORKBOARD_ZONE_DEFINITIONS.flatMap((zone) => {
2245
+ const group = buckets.get(zone.id) || [];
2246
+ return group.map((item, index) => ({
2247
+ item,
2248
+ zone,
2249
+ placement: NODE_PLACEMENTS[index % NODE_PLACEMENTS.length]
2250
+ }));
2251
+ });
2252
+ }
2253
+ function deriveFocusedClusterNodes(items, zoneId) {
2254
+ const stations = WORKBOARD_FOCUSED_STATIONS_BY_ZONE[zoneId];
2255
+ return items.map((item, index) => {
2256
+ const station = stations[index % stations.length];
2257
+ return {
2258
+ item,
2259
+ x: station.x,
2260
+ y: station.y,
2261
+ delay: index * 0.18
2262
+ };
2263
+ });
2264
+ }
2265
+ function buildOverviewPatrolRoute(params) {
2266
+ const anchor = toStagePoint(params.zoneId, params.placement);
2267
+ const hub = toZoneHubPoint(params.zoneId);
2268
+ const gate = WORKBOARD_ZONE_GATE_POINTS[params.zoneId];
2269
+ const laneIndex = Math.round((params.placement.left + params.placement.top) / 32) % 3;
2270
+ const laneOffset = (laneIndex - 1) * 10;
2271
+ const roadY = params.zoneId === "engaged" || params.zoneId === "steady" ? 460 : 500;
2272
+ const plazaLane = {
2273
+ x: WORKBOARD_TOWN_PLAZA_POINT.x + laneOffset,
2274
+ y: WORKBOARD_TOWN_PLAZA_POINT.y + (roadY < WORKBOARD_TOWN_PLAZA_POINT.y ? -10 : 10)
2275
+ };
2276
+ return [
2277
+ anchor,
2278
+ { x: anchor.x, y: hub.y },
2279
+ hub,
2280
+ { x: gate.x, y: hub.y },
2281
+ gate,
2282
+ { x: gate.x, y: roadY },
2283
+ { x: plazaLane.x, y: roadY },
2284
+ plazaLane,
2285
+ WORKBOARD_TOWN_PLAZA_POINT,
2286
+ plazaLane,
2287
+ { x: plazaLane.x, y: roadY },
2288
+ { x: gate.x, y: roadY },
2289
+ gate,
2290
+ { x: gate.x, y: hub.y },
2291
+ hub,
2292
+ { x: anchor.x, y: hub.y },
2293
+ anchor
2294
+ ];
2295
+ }
2296
+ function buildFocusedPatrolRoute(params) {
2297
+ const routes = WORKBOARD_FOCUSED_PATROL_ROUTES[params.zoneId];
2298
+ return routes[params.index % routes.length];
2299
+ }
2300
+ function WorkboardStageZone(props) {
2301
+ const layout = WORKBOARD_ZONE_LAYOUT[props.zone.id];
2302
+ const hubPoint = {
2303
+ x: layout.hubX / 100 * WORKBOARD_STAGE_WIDTH,
2304
+ y: layout.hubY / 100 * WORKBOARD_STAGE_HEIGHT
2305
+ };
2306
+ return /* @__PURE__ */ jsx20(
2307
+ "button",
2308
+ {
2309
+ type: "button",
2310
+ onClick: () => props.onSelect?.(props.zone.id),
2311
+ onMouseEnter: () => props.onHoverChange?.(
2312
+ createHoverTag({
2313
+ id: `zone-${props.zone.id}`,
2314
+ label: `${props.zone.title} \xB7 ${props.count}`,
2315
+ point: { x: hubPoint.x + 24, y: hubPoint.y - 18 }
2316
+ })
2317
+ ),
2318
+ onMouseLeave: () => props.onHoverChange?.(null),
2319
+ onFocus: () => props.onHoverChange?.(
2320
+ createHoverTag({
2321
+ id: `zone-${props.zone.id}`,
2322
+ label: `${props.zone.title} \xB7 ${props.count}`,
2323
+ point: { x: hubPoint.x + 24, y: hubPoint.y - 18 }
2324
+ })
2325
+ ),
2326
+ onBlur: () => props.onHoverChange?.(null),
2327
+ className: "absolute z-10 bg-transparent text-left focus:outline-none",
2328
+ "aria-label": `${props.zone.title} ${props.count}`,
2329
+ style: {
2330
+ left: `${layout.x}%`,
2331
+ top: `${layout.y}%`,
2332
+ width: `${layout.w}%`,
2333
+ height: `${layout.h}%`,
2334
+ clipPath: "polygon(0 0,100% 0,100% calc(100% - 16px),calc(100% - 16px) calc(100% - 16px),calc(100% - 16px) 100%,0 100%)"
2335
+ }
2336
+ }
2337
+ );
2338
+ }
2339
+ function WorkboardStageAgentNode(props) {
2340
+ const compact = props.mode === "overview";
2341
+ const avatarSize = compact ? 28 : 36;
2342
+ const direction = "direction" in props.point ? props.point.direction : void 0;
2343
+ const walking = "state" in props.point ? props.point.state === "walking" : false;
2344
+ const glyph = resolveAgentGlyph(props.item);
2345
+ return /* @__PURE__ */ jsx20(
2346
+ "button",
2347
+ {
2348
+ type: "button",
2349
+ onClick: () => props.onSelect?.(props.item.id),
2350
+ onMouseEnter: () => props.onHoverChange?.(
2351
+ createHoverTag({
2352
+ id: `agent-${props.item.id}`,
2353
+ label: `${props.item.name} \xB7 ${props.item.posture}`,
2354
+ point: props.point
2355
+ })
2356
+ ),
2357
+ onMouseLeave: () => props.onHoverChange?.(null),
2358
+ onFocus: () => props.onHoverChange?.(
2359
+ createHoverTag({
2360
+ id: `agent-${props.item.id}`,
2361
+ label: `${props.item.name} \xB7 ${props.item.posture}`,
2362
+ point: props.point
2363
+ })
2364
+ ),
2365
+ onBlur: () => props.onHoverChange?.(null),
2366
+ className: cn(
2367
+ "group absolute z-20 -translate-x-1/2 -translate-y-1/2 transition-all duration-300 focus:outline-none",
2368
+ props.active ? "scale-[1.08]" : "hover:scale-[1.04] focus-visible:scale-[1.04]",
2369
+ props.faded ? "opacity-30 saturate-50" : "opacity-100"
2370
+ ),
2371
+ style: {
2372
+ left: props.point.x,
2373
+ top: props.point.y
2374
+ },
2375
+ "aria-label": props.item.name,
2376
+ children: /* @__PURE__ */ jsxs7("span", { className: "relative block", children: [
2377
+ props.active ? /* @__PURE__ */ jsxs7(Fragment, { children: [
2378
+ /* @__PURE__ */ jsx20(
2379
+ "span",
2380
+ {
2381
+ className: "pointer-events-none absolute -inset-2 border border-foreground/45",
2382
+ style: { animation: "workboard-pulse 1.6s steps(2, end) infinite" }
2383
+ }
2384
+ ),
2385
+ /* @__PURE__ */ jsx20("span", { className: "pointer-events-none absolute -left-1 -top-1 h-1.5 w-1.5 bg-foreground/70" }),
2386
+ /* @__PURE__ */ jsx20("span", { className: "pointer-events-none absolute -right-1 -bottom-1 h-1.5 w-1.5 bg-foreground/60" })
2387
+ ] }) : null,
2388
+ !props.active && !props.faded ? /* @__PURE__ */ jsx20("span", { className: "pointer-events-none absolute -inset-1 border border-foreground/22 opacity-0 transition-opacity duration-200 group-hover:opacity-100 group-focus-visible:opacity-100" }) : null,
2389
+ /* @__PURE__ */ jsx20(
2390
+ WorkboardPixelAgent,
2391
+ {
2392
+ agentId: props.item.id,
2393
+ name: props.item.name,
2394
+ size: avatarSize,
2395
+ active: props.active,
2396
+ faded: props.faded,
2397
+ direction,
2398
+ walking,
2399
+ className: cn(
2400
+ props.active ? "shadow-[0_0_0_1px_rgba(17,17,19,0.14),0_3px_0_rgba(17,17,19,0.22)]" : "",
2401
+ compact ? "" : "scale-[1.03]"
2402
+ )
2403
+ }
2404
+ ),
2405
+ /* @__PURE__ */ jsx20(
2406
+ "span",
2407
+ {
2408
+ className: cn(
2409
+ "absolute -bottom-1 -right-1 grid size-3 place-items-center border text-[8px] font-bold leading-none",
2410
+ glyph.className
2411
+ ),
2412
+ "aria-hidden": "true",
2413
+ children: glyph.label
2414
+ }
2415
+ )
2416
+ ] })
2417
+ }
2418
+ );
2419
+ }
2420
+
2421
+ // src/components/workboard-game-atlas.tsx
2422
+ import { jsx as jsx21, jsxs as jsxs8 } from "react/jsx-runtime";
2423
+ var PIXEL_PANEL_CLIP = "polygon(0 6px,6px 6px,6px 0,calc(100% - 6px) 0,calc(100% - 6px) 6px,100% 6px,100% calc(100% - 6px),calc(100% - 6px) calc(100% - 6px),calc(100% - 0px) 100%,6px 100%,6px calc(100% - 6px),0 calc(100% - 6px))";
2424
+ function WorkboardAtlasPortalSign(props) {
2425
+ const zone = props.zones.find((item) => item.id === props.activeZoneId) || props.zones[0];
2426
+ if (!zone) return null;
2427
+ const point = toZoneHubPoint(zone.id);
2428
+ const left = Math.min(Math.max(point.x + 22, 18), WORKBOARD_STAGE_WIDTH - 198);
2429
+ const top = Math.min(Math.max(point.y - 54, 76), WORKBOARD_STAGE_HEIGHT - 120);
2430
+ return /* @__PURE__ */ jsxs8(
2431
+ "button",
2432
+ {
2433
+ type: "button",
2434
+ onClick: () => props.onSelectZone(zone.id),
2435
+ className: "absolute z-30 w-40 border-[3px] border-[#6e4d2f] bg-[#f8de8d] px-3 py-2 text-left shadow-[5px_5px_0_rgba(72,50,33,0.28)] transition-[filter,transform] hover:-translate-y-0.5 hover:brightness-105 focus:outline-none focus-visible:-translate-y-0.5",
2436
+ style: { left, top, clipPath: PIXEL_PANEL_CLIP },
2437
+ children: [
2438
+ /* @__PURE__ */ jsx21("span", { className: "absolute left-3 top-full h-5 w-2 bg-[#6e4d2f]" }),
2439
+ /* @__PURE__ */ jsx21("span", { className: "absolute right-3 top-full h-5 w-2 bg-[#6e4d2f]" }),
2440
+ /* @__PURE__ */ jsx21("span", { className: "block text-[9px] uppercase tracking-[0.18em] text-[#6e4d2f]/70", children: "zone gate" }),
2441
+ /* @__PURE__ */ jsx21("span", { className: "mt-1 block truncate text-sm font-semibold leading-5 tracking-[-0.04em] text-[#352516]", children: zone.title }),
2442
+ /* @__PURE__ */ jsxs8("span", { className: "mt-1 grid grid-cols-[1fr_auto] items-center gap-2", children: [
2443
+ /* @__PURE__ */ jsx21("span", { className: "h-2 border border-[#6e4d2f]/35 bg-[#fff1bd]", children: /* @__PURE__ */ jsx21(
2444
+ "span",
2445
+ {
2446
+ className: "block h-full bg-[#6e4d2f]/70",
2447
+ style: { width: `${Math.max(8, Math.min(100, zone.count * 18))}%` }
2448
+ }
2449
+ ) }),
2450
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px] font-semibold text-[#6e4d2f]/75", children: zone.count })
2451
+ ] })
2452
+ ]
2453
+ }
2454
+ );
2455
+ }
2456
+ function WorkboardGameAtlas(props) {
2457
+ const [hoveredTag, setHoveredTag] = React4.useState(null);
2458
+ const zones = props.gameMap.zones;
2459
+ const actors = props.gameMap.actors;
2460
+ return /* @__PURE__ */ jsxs8(
2461
+ "div",
2462
+ {
2463
+ className: "relative overflow-hidden border-2 border-border/70 bg-[linear-gradient(145deg,rgba(251,250,247,0.96),rgba(245,248,245,0.88))]",
2464
+ style: { width: WORKBOARD_STAGE_WIDTH, height: WORKBOARD_STAGE_HEIGHT, clipPath: PIXEL_PANEL_CLIP, imageRendering: "pixelated" },
2465
+ children: [
2466
+ /* @__PURE__ */ jsxs8("div", { className: "absolute left-3 top-3 z-20 px-1", children: [
2467
+ /* @__PURE__ */ jsx21("div", { className: "text-[10px] uppercase tracking-[0.2em] text-foreground/42", children: "world atlas" }),
2468
+ /* @__PURE__ */ jsx21("div", { className: "mt-1 text-[11px] text-foreground/66", children: "enter a zone" })
2469
+ ] }),
2470
+ /* @__PURE__ */ jsx21(
2471
+ "div",
2472
+ {
2473
+ className: "absolute right-3 top-3 z-20 border border-border/50 bg-background/72 px-2 py-1 text-right",
2474
+ style: { clipPath: PIXEL_PANEL_CLIP },
2475
+ children: /* @__PURE__ */ jsx21("div", { className: "text-[10px] uppercase tracking-[0.16em] text-foreground/42", children: props.flowMode })
2476
+ }
2477
+ ),
2478
+ /* @__PURE__ */ jsx21(PixelAtlasMap, { zones, stageWidth: WORKBOARD_STAGE_WIDTH, stageHeight: WORKBOARD_STAGE_HEIGHT }),
2479
+ zones.map((zone) => /* @__PURE__ */ jsx21(
2480
+ WorkboardStageZone,
2481
+ {
2482
+ zone: resolveZoneDefinition(zone.id),
2483
+ count: zone.count,
2484
+ active: zone.id === props.activeZoneId,
2485
+ onSelect: props.onSelectZone,
2486
+ onHoverChange: setHoveredTag
2487
+ },
2488
+ zone.id
2489
+ )),
2490
+ /* @__PURE__ */ jsx21(
2491
+ WorkboardAtlasPortalSign,
2492
+ {
2493
+ activeZoneId: props.activeZoneId,
2494
+ zones,
2495
+ onSelectZone: props.onSelectZone
2496
+ }
2497
+ ),
2498
+ /* @__PURE__ */ jsxs8(
2499
+ "svg",
2500
+ {
2501
+ viewBox: `0 0 ${WORKBOARD_STAGE_WIDTH} ${WORKBOARD_STAGE_HEIGHT}`,
2502
+ className: "pointer-events-none absolute inset-0 h-full w-full",
2503
+ preserveAspectRatio: "xMidYMid meet",
2504
+ "aria-hidden": "true",
2505
+ children: [
2506
+ props.gameMap.corridors.map((route) => /* @__PURE__ */ jsx21(
2507
+ PixelRoute,
2508
+ {
2509
+ points: route.points,
2510
+ className: cn("stroke-foreground/8", route.active ? "opacity-100" : "opacity-60"),
2511
+ dashed: true
2512
+ },
2513
+ route.id
2514
+ )),
2515
+ actors.map((actor) => {
2516
+ const faded = actor.zoneId !== props.activeZoneId;
2517
+ return /* @__PURE__ */ jsxs8("g", { opacity: faded ? 0.18 : 0.72, children: [
2518
+ /* @__PURE__ */ jsx21(
2519
+ "rect",
2520
+ {
2521
+ x: actor.overviewGate.x - 5,
2522
+ y: actor.overviewGate.y - 5,
2523
+ width: "10",
2524
+ height: "10",
2525
+ fill: "rgba(255,252,247,0.92)",
2526
+ stroke: "rgba(17,17,19,0.36)",
2527
+ strokeWidth: "2"
2528
+ }
2529
+ ),
2530
+ props.selectedAgentId === actor.id ? /* @__PURE__ */ jsx21(
2531
+ "rect",
2532
+ {
2533
+ x: actor.overviewGate.x - 9,
2534
+ y: actor.overviewGate.y - 9,
2535
+ width: "18",
2536
+ height: "18",
2537
+ fill: "none",
2538
+ stroke: "rgba(17,17,19,0.42)",
2539
+ strokeWidth: "2",
2540
+ strokeDasharray: "3 4"
2541
+ }
2542
+ ) : null
2543
+ ] }, `gate-${actor.id}`);
2544
+ })
2545
+ ]
2546
+ }
2547
+ ),
2548
+ actors.map((actor) => /* @__PURE__ */ jsx21(
2549
+ WorkboardStageAgentNode,
2550
+ {
2551
+ item: actor.agent,
2552
+ zone: resolveZoneDefinition(actor.zoneId),
2553
+ point: props.motionFrames[actor.id] || actor.overviewAnchor,
2554
+ active: props.selectedAgentId === actor.id,
2555
+ faded: actor.zoneId !== props.activeZoneId,
2556
+ mode: "overview",
2557
+ onSelect: (agentId) => props.onSelectAgent?.(agentId, actor.zoneId),
2558
+ onHoverChange: setHoveredTag
2559
+ },
2560
+ actor.id
2561
+ )),
2562
+ /* @__PURE__ */ jsx21(
2563
+ "svg",
2564
+ {
2565
+ viewBox: `0 0 ${WORKBOARD_STAGE_WIDTH} ${WORKBOARD_STAGE_HEIGHT}`,
2566
+ className: "pointer-events-none absolute inset-0 h-full w-full",
2567
+ preserveAspectRatio: "xMidYMid meet",
2568
+ "aria-hidden": "true",
2569
+ children: /* @__PURE__ */ jsx21(PixelHoverTag, { tag: hoveredTag, stageWidth: WORKBOARD_STAGE_WIDTH, stageHeight: WORKBOARD_STAGE_HEIGHT })
2570
+ }
2571
+ )
2572
+ ]
2573
+ }
2574
+ );
2575
+ }
2576
+
2577
+ // src/components/workboard-game-inspector.tsx
2578
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
2579
+ import {
2580
+ AnimatedPxlKitIcon,
2581
+ PxlKitIcon,
2582
+ isAnimatedIcon
2583
+ } from "@pxlkit/core";
2584
+ import { RadarPing } from "@pxlkit/effects";
2585
+ import { Clock as PixelClock, Sparkles as PixelSparkles, WarningTriangle } from "@pxlkit/feedback";
2586
+ import { Crown } from "@pxlkit/gamification";
2587
+ import { jsx as jsx22, jsxs as jsxs9 } from "react/jsx-runtime";
2588
+ var PIXEL_PANEL_CLIP2 = "polygon(0 6px,6px 6px,6px 0,calc(100% - 6px) 0,calc(100% - 6px) 6px,100% 6px,100% calc(100% - 6px),calc(100% - 6px) calc(100% - 6px),calc(100% - 0px) 100%,6px 100%,6px calc(100% - 6px),0 calc(100% - 6px))";
2589
+ function activityIcon(kind) {
2590
+ if (kind === "focus") return Crown;
2591
+ if (kind === "progress") return RadarPing;
2592
+ return PixelClock;
2593
+ }
2594
+ function ActivityLine(props) {
2595
+ const icon = activityIcon(props.item.kind);
2596
+ return /* @__PURE__ */ jsxs9("li", { className: "flex items-start gap-2 border-b border-border/50 py-2 last:border-b-0", children: [
2597
+ /* @__PURE__ */ jsx22("span", { className: "mt-0.5 inline-flex size-5 items-center justify-center border border-border/60 bg-background/82 text-foreground/70", children: isAnimatedIcon(icon) ? /* @__PURE__ */ jsx22(AnimatedPxlKitIcon, { icon, size: 14, colorful: true, speed: 0.8 }) : /* @__PURE__ */ jsx22(PxlKitIcon, { icon, size: 14, colorful: true }) }),
2598
+ /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1", children: [
2599
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center gap-1.5", children: [
2600
+ /* @__PURE__ */ jsx22("span", { className: "text-sm font-medium text-foreground", children: props.item.title }),
2601
+ /* @__PURE__ */ jsx22("span", { className: "border border-border/50 bg-background/72 px-1.5 py-0.5 text-[10px] uppercase tracking-[0.12em] text-foreground/54", children: props.item.status })
2602
+ ] }),
2603
+ /* @__PURE__ */ jsx22("div", { className: "mt-1 text-sm leading-6 text-muted-foreground", children: props.item.summary })
2604
+ ] })
2605
+ ] });
2606
+ }
2607
+ function CueLine(props) {
2608
+ return /* @__PURE__ */ jsxs9(
2609
+ "li",
2610
+ {
2611
+ className: cn(
2612
+ "flex items-start justify-between gap-3 border-b border-border/50 py-2 text-sm last:border-b-0",
2613
+ props.item.tone === "warning" ? "text-amber-800" : "text-foreground"
2614
+ ),
2615
+ children: [
2616
+ /* @__PURE__ */ jsxs9("span", { className: "inline-flex items-center gap-1.5 text-foreground/48", children: [
2617
+ props.item.tone === "warning" ? /* @__PURE__ */ jsx22(PxlKitIcon, { icon: WarningTriangle, size: 12, colorful: true }) : /* @__PURE__ */ jsx22(PxlKitIcon, { icon: PixelSparkles, size: 12, colorful: true }),
2618
+ props.item.label
2619
+ ] }),
2620
+ /* @__PURE__ */ jsx22("span", { className: "border border-border/50 bg-background/72 px-1.5 py-0.5 font-medium", children: props.item.value })
2621
+ ]
2622
+ }
2623
+ );
2624
+ }
2625
+ function PixelStat(props) {
2626
+ return /* @__PURE__ */ jsxs9("div", { className: "border border-border/60 bg-background/72 px-2 py-1.5", children: [
2627
+ /* @__PURE__ */ jsx22("div", { className: "text-[10px] uppercase tracking-[0.14em] text-foreground/42", children: props.label }),
2628
+ /* @__PURE__ */ jsx22("div", { className: "mt-1 text-sm font-medium text-foreground", children: props.value })
2629
+ ] });
2630
+ }
2631
+ function SpriteRoster(props) {
2632
+ const visiblePeers = props.peers.filter((item) => item.id !== props.selectedAgentId).slice(0, 6);
2633
+ if (visiblePeers.length === 0) {
2634
+ return /* @__PURE__ */ jsx22("div", { className: "border border-dashed border-border/70 bg-background/70 px-3 py-2 text-sm text-muted-foreground", children: "\u5F53\u524D\u533A\u57DF\u6CA1\u6709\u5176\u4ED6 sprite\u3002" });
2635
+ }
2636
+ return /* @__PURE__ */ jsx22("div", { className: "grid gap-1.5", children: visiblePeers.map((item, index) => /* @__PURE__ */ jsxs9(
2637
+ "button",
2638
+ {
2639
+ type: "button",
2640
+ onClick: () => props.onSelectAgent?.(item.id),
2641
+ className: "inline-flex items-center justify-between gap-2 border border-border/70 bg-background/90 px-2 py-1.5 text-sm text-foreground transition-colors hover:border-foreground/20",
2642
+ children: [
2643
+ /* @__PURE__ */ jsxs9("span", { className: "inline-flex items-center gap-2", children: [
2644
+ /* @__PURE__ */ jsx22("span", { className: "inline-flex h-5 min-w-5 items-center justify-center border border-border/60 bg-background/72 px-1 text-[10px] uppercase tracking-[0.12em] text-foreground/54", children: index + 1 }),
2645
+ /* @__PURE__ */ jsx22(WorkboardPixelAgent, { agentId: item.id, name: item.name, size: 18 }),
2646
+ /* @__PURE__ */ jsx22("span", { children: item.name })
2647
+ ] }),
2648
+ /* @__PURE__ */ jsx22("span", { className: "text-[10px] uppercase tracking-[0.12em] text-foreground/42", children: item.momentum })
2649
+ ]
2650
+ },
2651
+ item.id
2652
+ )) });
2653
+ }
2654
+ function WorkboardGameInspector(props) {
2655
+ return /* @__PURE__ */ jsx22("aside", { className: "absolute bottom-3 right-3 z-30 w-[min(420px,calc(100%-24px))]", children: /* @__PURE__ */ jsxs9(
2656
+ "div",
2657
+ {
2658
+ className: "relative border-2 border-border/70 bg-[rgba(255,252,247,0.94)] shadow-[0_6px_0_rgba(17,17,19,0.12)] backdrop-blur-[1px]",
2659
+ style: { clipPath: PIXEL_PANEL_CLIP2 },
2660
+ children: [
2661
+ /* @__PURE__ */ jsx22("span", { className: "pointer-events-none absolute left-2 top-2 h-2 w-2 bg-foreground/16" }),
2662
+ /* @__PURE__ */ jsx22("span", { className: "pointer-events-none absolute right-2 top-2 h-2 w-2 bg-foreground/10" }),
2663
+ /* @__PURE__ */ jsx22("span", { className: "pointer-events-none absolute bottom-2 left-2 h-2 w-2 bg-foreground/10" }),
2664
+ /* @__PURE__ */ jsxs9(
2665
+ "button",
2666
+ {
2667
+ type: "button",
2668
+ onClick: props.onToggleCollapsed,
2669
+ className: "flex w-full items-center justify-between px-3 py-2 text-left",
2670
+ "aria-expanded": !props.collapsed,
2671
+ children: [
2672
+ /* @__PURE__ */ jsx22("span", { className: "text-xs uppercase tracking-[0.16em] text-foreground/46", children: "codex log" }),
2673
+ /* @__PURE__ */ jsxs9("span", { className: "inline-flex items-center gap-1 text-xs text-foreground/56", children: [
2674
+ props.collapsed ? "Open" : "Close",
2675
+ props.collapsed ? /* @__PURE__ */ jsx22(ChevronUpIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx22(ChevronDownIcon, { className: "size-3.5" })
2676
+ ] })
2677
+ ]
2678
+ }
2679
+ ),
2680
+ !props.collapsed ? /* @__PURE__ */ jsxs9("div", { className: "border-t-2 border-border/70 px-3 pb-3 pt-2", children: [
2681
+ /* @__PURE__ */ jsxs9("div", { className: "border border-border/60 bg-background/72 px-2.5 py-2", children: [
2682
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-start justify-between gap-3", children: [
2683
+ /* @__PURE__ */ jsxs9("div", { children: [
2684
+ /* @__PURE__ */ jsx22("p", { className: "text-[10px] uppercase tracking-[0.16em] text-foreground/42", children: "active zone" }),
2685
+ /* @__PURE__ */ jsx22("h3", { className: "mt-1 text-xl font-semibold tracking-[-0.04em] text-foreground", children: props.activeZone.title })
2686
+ ] }),
2687
+ /* @__PURE__ */ jsx22("span", { className: "border border-border/60 bg-background px-1.5 py-0.5 text-[10px] uppercase tracking-[0.14em] text-foreground/52", children: props.stageLevel === "clusters" ? "world" : "room" })
2688
+ ] }),
2689
+ /* @__PURE__ */ jsx22("p", { className: "mt-2 text-sm leading-6 text-muted-foreground", children: props.activeZone.description }),
2690
+ /* @__PURE__ */ jsx22("p", { className: "mt-1 text-[11px] uppercase tracking-[0.14em] text-foreground/42", children: props.activeZone.subtitle })
2691
+ ] }),
2692
+ /* @__PURE__ */ jsxs9("div", { className: "mt-3", children: [
2693
+ /* @__PURE__ */ jsx22("p", { className: "text-xs uppercase tracking-[0.12em] text-foreground/46", children: "nearby sprites" }),
2694
+ /* @__PURE__ */ jsx22("div", { className: "mt-2 max-h-28 overflow-auto pr-1", children: /* @__PURE__ */ jsx22(
2695
+ SpriteRoster,
2696
+ {
2697
+ peers: props.selectedPeers,
2698
+ selectedAgentId: props.selected?.id || "",
2699
+ onSelectAgent: props.onSelectAgent
2700
+ }
2701
+ ) })
2702
+ ] }),
2703
+ props.selected ? /* @__PURE__ */ jsxs9("div", { className: "mt-3 border-t-2 border-border/70 pt-3", children: [
2704
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-start justify-between gap-3", children: [
2705
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
2706
+ /* @__PURE__ */ jsx22(WorkboardPixelAgent, { agentId: props.selected.id, name: props.selected.name, size: 24, active: true }),
2707
+ /* @__PURE__ */ jsxs9("div", { children: [
2708
+ /* @__PURE__ */ jsx22("p", { className: "text-xs uppercase tracking-[0.12em] text-foreground/46", children: "selected sprite" }),
2709
+ /* @__PURE__ */ jsx22("p", { className: "text-sm font-semibold text-foreground", children: props.selected.name })
2710
+ ] })
2711
+ ] }),
2712
+ /* @__PURE__ */ jsx22("span", { className: "border border-border/60 bg-background px-1.5 py-0.5 text-[10px] uppercase tracking-[0.14em] text-foreground/52", children: formatWorkboardRelativeTime(props.selected.collectedAt) })
2713
+ ] }),
2714
+ /* @__PURE__ */ jsxs9("div", { className: "mt-3 grid grid-cols-3 gap-1.5", children: [
2715
+ /* @__PURE__ */ jsx22(PixelStat, { label: "posture", value: props.selected.posture }),
2716
+ /* @__PURE__ */ jsx22(PixelStat, { label: "tempo", value: props.selected.momentum }),
2717
+ /* @__PURE__ */ jsx22(PixelStat, { label: "life", value: props.selected.running ? "live" : "quiet" })
2718
+ ] }),
2719
+ /* @__PURE__ */ jsxs9("div", { className: "mt-3 border border-border/60 bg-background/78 px-2.5 py-2", children: [
2720
+ /* @__PURE__ */ jsx22("p", { className: "text-[10px] uppercase tracking-[0.14em] text-foreground/42", children: "speech line" }),
2721
+ /* @__PURE__ */ jsxs9("p", { className: "mt-1 text-sm leading-6 text-foreground", children: [
2722
+ "\u201C",
2723
+ props.selected.snapshot.current[0]?.summary || props.selected.headline,
2724
+ "\u201D"
2725
+ ] }),
2726
+ /* @__PURE__ */ jsx22("p", { className: "mt-1 text-[11px] text-muted-foreground", children: props.selected.statusText })
2727
+ ] }),
2728
+ /* @__PURE__ */ jsxs9("div", { className: "mt-3", children: [
2729
+ /* @__PURE__ */ jsx22("p", { className: "text-xs uppercase tracking-[0.12em] text-foreground/46", children: "quest beats" }),
2730
+ /* @__PURE__ */ jsx22("ul", { className: "mt-1 border border-border/60 bg-background/74 px-2 py-1", children: (props.selected.snapshot.current || []).slice(0, 3).map((item) => /* @__PURE__ */ jsx22(ActivityLine, { item }, item.id)) })
2731
+ ] }),
2732
+ /* @__PURE__ */ jsxs9("div", { className: "mt-3", children: [
2733
+ /* @__PURE__ */ jsx22("p", { className: "text-xs uppercase tracking-[0.12em] text-foreground/46", children: "recent drops" }),
2734
+ (props.selected.snapshot.recent || []).length === 0 ? /* @__PURE__ */ jsx22("p", { className: "mt-1 border border-dashed border-border/60 bg-background/70 px-2 py-2 text-sm text-muted-foreground", children: "\u6682\u65E0\u8FD1\u671F\u7247\u6BB5\u3002" }) : /* @__PURE__ */ jsx22("ul", { className: "mt-1 border border-border/60 bg-background/74 px-2 py-1", children: (props.selected.snapshot.recent || []).slice(0, 3).map((item) => /* @__PURE__ */ jsx22(ActivityLine, { item }, item.id)) })
2735
+ ] }),
2736
+ /* @__PURE__ */ jsxs9("div", { className: "mt-3", children: [
2737
+ /* @__PURE__ */ jsx22("p", { className: "text-xs uppercase tracking-[0.12em] text-foreground/46", children: "world cues" }),
2738
+ (props.selected.snapshot.signals || []).length === 0 ? /* @__PURE__ */ jsx22("p", { className: "mt-1 border border-dashed border-border/60 bg-background/70 px-2 py-2 text-sm text-muted-foreground", children: "\u5F53\u524D\u6CA1\u6709\u516C\u5F00\u7EBF\u7D22\u3002" }) : /* @__PURE__ */ jsx22("ul", { className: "mt-1 border border-border/60 bg-background/74 px-2 py-1", children: (props.selected.snapshot.signals || []).slice(0, 4).map((item) => /* @__PURE__ */ jsx22(CueLine, { item }, item.label)) })
2739
+ ] })
2740
+ ] }) : /* @__PURE__ */ jsx22("div", { className: "mt-3 border border-dashed border-border/60 bg-background/70 px-3 py-3 text-sm text-muted-foreground", children: "\u9009\u4E2D\u4E00\u4E2A sprite \u540E\uFF0C\u8FD9\u91CC\u4F1A\u51FA\u73B0\u5B83\u7684\u516C\u5F00\u7247\u6BB5\u4E0E\u7EBF\u7D22\u3002" })
2741
+ ] }) : null
2742
+ ]
2743
+ }
2744
+ ) });
2745
+ }
2746
+
2747
+ // src/components/workboard-game-map.ts
2748
+ var OVERVIEW_ROUTE_DWELL = 0.22;
2749
+ var FOCUSED_ROUTE_DWELL = 0.28;
2750
+ var PIXEL_STEP_SIZE = 2;
2751
+ function resolveOverviewGate(params) {
2752
+ if (params.zoneId === "engaged" || params.zoneId === "quiet") {
2753
+ return params.route[1] || params.route[0] || toZoneHubPoint(params.zoneId);
2754
+ }
2755
+ return params.route[2] || params.route[1] || params.route[0] || toZoneHubPoint(params.zoneId);
2756
+ }
2757
+ var FOCUSED_PROP_ITEMS = {
2758
+ engaged: [],
2759
+ steady: [],
2760
+ quiet: [],
2761
+ drift: []
2762
+ };
2763
+ var FOCUSED_AREA_LABELS = {
2764
+ engaged: [
2765
+ { id: "engaged-briefing", label: "briefing node", x: 166, y: 118 },
2766
+ { id: "engaged-relay", label: "relay desk", x: 660, y: 118 },
2767
+ { id: "engaged-dispatch", label: "dispatch rack", x: 1194, y: 118 }
2768
+ ],
2769
+ steady: [
2770
+ { id: "steady-focus", label: "focus lane", x: 166, y: 118 },
2771
+ { id: "steady-desk", label: "steady desk", x: 660, y: 118 },
2772
+ { id: "steady-throughput", label: "throughput rail", x: 1194, y: 118 }
2773
+ ],
2774
+ quiet: [
2775
+ { id: "quiet-standby", label: "standby desk", x: 166, y: 118 },
2776
+ { id: "quiet-idle", label: "idle rail", x: 660, y: 118 },
2777
+ { id: "quiet-sleep", label: "sleep shelf", x: 1194, y: 118 }
2778
+ ],
2779
+ drift: [
2780
+ { id: "drift-watch", label: "watch point", x: 166, y: 118 },
2781
+ { id: "drift-issue", label: "issue console", x: 660, y: 118 },
2782
+ { id: "drift-signal", label: "signal rack", x: 1194, y: 118 }
2783
+ ]
2784
+ };
2785
+ function buildZones(params) {
2786
+ return WORKBOARD_ZONE_DEFINITIONS.map((zone) => ({
2787
+ id: zone.id,
2788
+ title: zone.title,
2789
+ subtitle: zone.subtitle,
2790
+ description: zone.description,
2791
+ badge: zone.badge,
2792
+ count: params.board.agents.filter((item) => resolveZoneId(item) === zone.id).length,
2793
+ active: zone.id === params.activeZoneId,
2794
+ hub: toZoneHubPoint(zone.id)
2795
+ }));
2796
+ }
2797
+ function buildActors(params) {
2798
+ const stageNodes = deriveStageNodes(params.board);
2799
+ const focusedLookup = /* @__PURE__ */ new Map();
2800
+ WORKBOARD_ZONE_DEFINITIONS.forEach((zone) => {
2801
+ const items = params.board.agents.filter((item) => resolveZoneId(item) === zone.id);
2802
+ const focusedNodes = deriveFocusedClusterNodes(items, zone.id);
2803
+ focusedNodes.forEach((node, index) => {
2804
+ focusedLookup.set(node.item.id, {
2805
+ anchor: { x: node.x, y: node.y },
2806
+ routeId: `patrol-${zone.id}-${index % 3}`
2807
+ });
2808
+ });
2809
+ });
2810
+ return stageNodes.map((node) => {
2811
+ const overviewRoute = buildOverviewPatrolRoute({
2812
+ zoneId: node.zone.id,
2813
+ placement: node.placement
2814
+ });
2815
+ const focused = focusedLookup.get(node.item.id);
2816
+ return {
2817
+ id: node.item.id,
2818
+ agent: node.item,
2819
+ zoneId: node.zone.id,
2820
+ overviewAnchor: overviewRoute[0] || toZoneHubPoint(node.zone.id),
2821
+ overviewRoute,
2822
+ overviewGate: resolveOverviewGate({
2823
+ zoneId: node.zone.id,
2824
+ route: overviewRoute
2825
+ }),
2826
+ focusedAnchor: focused?.anchor,
2827
+ focusedStation: focused?.anchor,
2828
+ focusedRouteId: focused?.routeId,
2829
+ active: node.item.id === params.selectedAgentId
2830
+ };
2831
+ });
2832
+ }
2833
+ function buildPatrols(params) {
2834
+ const laneCount = Math.max(
2835
+ 1,
2836
+ Math.min(
2837
+ 3,
2838
+ params.board.agents.filter((item) => resolveZoneId(item) === params.activeZoneId).length
2839
+ )
2840
+ );
2841
+ return Array.from({ length: laneCount }, (_, index) => ({
2842
+ id: `patrol-${params.activeZoneId}-${index}`,
2843
+ kind: "patrol",
2844
+ points: buildFocusedPatrolRoute({ index, zoneId: params.activeZoneId }),
2845
+ zoneId: params.activeZoneId,
2846
+ dwellRatio: FOCUSED_ROUTE_DWELL,
2847
+ snapSize: PIXEL_STEP_SIZE,
2848
+ label: `lane ${index + 1}`,
2849
+ active: index === 0
2850
+ }));
2851
+ }
2852
+ function buildCorridors(params) {
2853
+ return params.actors.map((actor) => ({
2854
+ id: `corridor-${actor.id}`,
2855
+ kind: "corridor",
2856
+ points: actor.overviewRoute,
2857
+ zoneId: actor.zoneId,
2858
+ dwellRatio: OVERVIEW_ROUTE_DWELL,
2859
+ snapSize: PIXEL_STEP_SIZE,
2860
+ label: actor.agent.name,
2861
+ active: actor.zoneId === params.activeZoneId
2862
+ }));
2863
+ }
2864
+ function buildWorkboardGameMapConfig(params) {
2865
+ const zones = buildZones(params);
2866
+ const actors = buildActors(params);
2867
+ const patrols = buildPatrols(params);
2868
+ const corridors = buildCorridors({ actors, activeZoneId: params.activeZoneId });
2869
+ return {
2870
+ board: params.board,
2871
+ activeZoneId: params.activeZoneId,
2872
+ selectedAgentId: params.selectedAgentId,
2873
+ zones,
2874
+ actors,
2875
+ corridors,
2876
+ patrols,
2877
+ pointsOfInterest: [
2878
+ ...FOCUSED_PROP_ITEMS[params.activeZoneId],
2879
+ {
2880
+ id: `${params.activeZoneId}-hub`,
2881
+ kind: "hub",
2882
+ x: 800,
2883
+ y: 480,
2884
+ active: true
2885
+ }
2886
+ ],
2887
+ areaLabels: FOCUSED_AREA_LABELS[params.activeZoneId]
2888
+ };
2889
+ }
2890
+
2891
+ // src/components/workboard-game-room.tsx
2892
+ import * as React5 from "react";
2893
+
2894
+ // src/components/workboard-room-field.tsx
2895
+ import { jsx as jsx23, jsxs as jsxs10 } from "react/jsx-runtime";
2896
+ var TILE_SIZE2 = 40;
2897
+ var GRID_COLS2 = 40;
2898
+ var GRID_ROWS2 = 24;
2899
+ var STAGE_WIDTH2 = TILE_SIZE2 * GRID_COLS2;
2900
+ var STAGE_HEIGHT2 = TILE_SIZE2 * GRID_ROWS2;
2901
+ function tileToRect2(tile) {
2902
+ return {
2903
+ x: tile.col * TILE_SIZE2,
2904
+ y: tile.row * TILE_SIZE2,
2905
+ width: tile.cols * TILE_SIZE2,
2906
+ height: tile.rows * TILE_SIZE2
2907
+ };
2908
+ }
2909
+ function estimateTextWidth2(text) {
2910
+ return Array.from(text).reduce((acc, char) => acc + (char.charCodeAt(0) > 255 ? 9 : 6), 0);
2911
+ }
2912
+ function renderGrassTiles2() {
2913
+ return Array.from({ length: GRID_COLS2 * GRID_ROWS2 }, (_, index) => {
2914
+ const col = index % GRID_COLS2;
2915
+ const row = Math.floor(index / GRID_COLS2);
2916
+ const rect2 = tileToRect2({ col, row, cols: 1, rows: 1 });
2917
+ const fill = (col + row) % 2 === 0 ? "rgba(104,136,76,0.98)" : "rgba(91,124,70,0.98)";
2918
+ return /* @__PURE__ */ jsxs10("g", { children: [
2919
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: TILE_SIZE2, height: TILE_SIZE2, fill }),
2920
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x + 6, y: rect2.y + 8, width: "4", height: "4", fill: "rgba(40,78,45,0.24)" }),
2921
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x + 27, y: rect2.y + 25, width: "4", height: "4", fill: "rgba(142,161,89,0.18)" })
2922
+ ] }, `room-grass-${col}-${row}`);
2923
+ });
2924
+ }
2925
+ function renderPath(tile, index) {
2926
+ const rect2 = tileToRect2(tile);
2927
+ return /* @__PURE__ */ jsxs10("g", { children: [
2928
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, fill: "rgba(210,184,120,0.98)" }),
2929
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y + rect2.height - 5, width: rect2.width, height: "5", fill: "rgba(137,104,67,0.28)" }),
2930
+ Array.from({ length: Math.max(1, tile.cols * tile.rows) }, (_, chipIndex) => /* @__PURE__ */ jsx23(
2931
+ "rect",
2932
+ {
2933
+ x: rect2.x + 8 + chipIndex % Math.max(1, tile.cols) * TILE_SIZE2,
2934
+ y: rect2.y + 10 + Math.floor(chipIndex / Math.max(1, tile.cols)) * TILE_SIZE2,
2935
+ width: "12",
2936
+ height: "4",
2937
+ fill: "rgba(96,74,51,0.16)"
2938
+ },
2939
+ `room-path-chip-${index}-${chipIndex}`
2940
+ ))
2941
+ ] }, `room-path-${index}`);
2942
+ }
2943
+ function renderTree2(point, index) {
2944
+ const x = point.col * TILE_SIZE2;
2945
+ const y = point.row * TILE_SIZE2;
2946
+ return /* @__PURE__ */ jsxs10("g", { children: [
2947
+ /* @__PURE__ */ jsx23("rect", { x: x + 17, y: y + 22, width: "8", height: "18", fill: "rgba(92,62,40,0.94)" }),
2948
+ /* @__PURE__ */ jsx23("rect", { x: x + 10, y: y + 10, width: "22", height: "18", fill: "rgba(74,126,76,0.98)" }),
2949
+ /* @__PURE__ */ jsx23("rect", { x: x + 5, y: y + 17, width: "32", height: "16", fill: "rgba(61,112,65,0.98)" }),
2950
+ /* @__PURE__ */ jsx23("rect", { x: x + 14, y: y + 4, width: "18", height: "12", fill: "rgba(91,150,82,0.96)" })
2951
+ ] }, `room-tree-${index}`);
2952
+ }
2953
+ function renderShrub2(point, index) {
2954
+ const x = point.col * TILE_SIZE2;
2955
+ const y = point.row * TILE_SIZE2;
2956
+ return /* @__PURE__ */ jsxs10("g", { children: [
2957
+ /* @__PURE__ */ jsx23("rect", { x: x + 5, y: y + 22, width: "12", height: "9", fill: "rgba(70,121,66,0.96)" }),
2958
+ /* @__PURE__ */ jsx23("rect", { x: x + 15, y: y + 18, width: "18", height: "12", fill: "rgba(87,142,75,0.96)" }),
2959
+ /* @__PURE__ */ jsx23("rect", { x: x + 28, y: y + 24, width: "7", height: "7", fill: "rgba(58,103,58,0.9)" })
2960
+ ] }, `room-shrub-${index}`);
2961
+ }
2962
+ function renderFloor(tile, index, palette) {
2963
+ return /* @__PURE__ */ jsx23("g", { children: Array.from({ length: tile.cols * tile.rows }, (_, plankIndex) => {
2964
+ const col = tile.col + plankIndex % tile.cols;
2965
+ const row = tile.row + Math.floor(plankIndex / tile.cols);
2966
+ const rect2 = tileToRect2({ col, row, cols: 1, rows: 1 });
2967
+ return /* @__PURE__ */ jsxs10("g", { children: [
2968
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: TILE_SIZE2, height: TILE_SIZE2, fill: (col + row) % 2 === 0 ? palette.floorA : palette.floorB }),
2969
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y + TILE_SIZE2 - 4, width: TILE_SIZE2, height: "4", fill: "rgba(81,56,39,0.2)" }),
2970
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x + 10, y: rect2.y + 12, width: "10", height: "4", fill: "rgba(248,214,149,0.13)" })
2971
+ ] }, `room-plank-${index}-${col}-${row}`);
2972
+ }) }, `room-floor-${index}`);
2973
+ }
2974
+ function renderBlock(tile, index, fill, stroke) {
2975
+ const rect2 = tileToRect2(tile);
2976
+ return /* @__PURE__ */ jsx23(
2977
+ "rect",
2978
+ {
2979
+ x: rect2.x,
2980
+ y: rect2.y,
2981
+ width: rect2.width,
2982
+ height: rect2.height,
2983
+ fill,
2984
+ stroke,
2985
+ strokeWidth: stroke ? 2 : void 0
2986
+ },
2987
+ `room-block-${index}`
2988
+ );
2989
+ }
2990
+ function renderWall(tile, index, palette) {
2991
+ const rect2 = tileToRect2(tile);
2992
+ return /* @__PURE__ */ jsxs10("g", { children: [
2993
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, fill: palette.wall }),
2994
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x + 4, y: rect2.y + 4, width: Math.max(0, rect2.width - 8), height: Math.max(0, rect2.height - 8), fill: palette.wallLight }),
2995
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: "5", fill: "rgba(242,168,97,0.32)" })
2996
+ ] }, `room-wall-${index}`);
2997
+ }
2998
+ function renderDoor(tile, index) {
2999
+ const rect2 = tileToRect2(tile);
3000
+ return /* @__PURE__ */ jsxs10("g", { children: [
3001
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, fill: "rgba(215,184,110,0.98)" }),
3002
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x + 8, y: rect2.y + 10, width: Math.max(10, rect2.width - 16), height: "5", fill: "rgba(95,69,43,0.34)" })
3003
+ ] }, `room-door-${index}`);
3004
+ }
3005
+ function renderRug(tile, index, palette) {
3006
+ const rect2 = tileToRect2(tile);
3007
+ return /* @__PURE__ */ jsxs10("g", { children: [
3008
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x, y: rect2.y, width: rect2.width, height: rect2.height, fill: index === 0 ? palette.rug : palette.rugStrong }),
3009
+ /* @__PURE__ */ jsx23("rect", { x: rect2.x + 8, y: rect2.y + 8, width: Math.max(0, rect2.width - 16), height: Math.max(0, rect2.height - 16), fill: "none", stroke: palette.accent, strokeWidth: "3", opacity: "0.42" })
3010
+ ] }, `room-rug-${index}`);
3011
+ }
3012
+ function renderRoomProp(prop) {
3013
+ if (prop.kind === "hub") return null;
3014
+ if (prop.kind === "board") {
3015
+ return /* @__PURE__ */ jsxs10("g", { children: [
3016
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y, width: "170", height: "82", fill: "rgba(196,127,73,0.98)", stroke: "rgba(94,61,39,0.9)", strokeWidth: "4" }),
3017
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 12, y: prop.y + 12, width: "54", height: "24", fill: "rgba(255,241,189,0.92)" }),
3018
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 78, y: prop.y + 12, width: "72", height: "18", fill: "rgba(255,241,189,0.86)" }),
3019
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 18, y: prop.y + 48, width: "132", height: "18", fill: "rgba(255,241,189,0.78)" })
3020
+ ] });
3021
+ }
3022
+ if (prop.kind === "blueprint") {
3023
+ return /* @__PURE__ */ jsxs10("g", { children: [
3024
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y + 22, width: "120", height: "34", fill: "rgba(111,78,47,0.9)" }),
3025
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 10, y: prop.y, width: "100", height: "64", fill: "rgba(67,116,138,0.96)", stroke: "rgba(53,37,22,0.6)", strokeWidth: "3" }),
3026
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 22, y: prop.y + 12, width: "30", height: "18", fill: "none", stroke: "rgba(219,236,224,0.78)", strokeWidth: "3" }),
3027
+ /* @__PURE__ */ jsx23("path", { d: `M ${prop.x + 64} ${prop.y + 16} H ${prop.x + 94} V ${prop.y + 42} H ${prop.x + 44}`, fill: "none", stroke: "rgba(219,236,224,0.7)", strokeWidth: "3" })
3028
+ ] });
3029
+ }
3030
+ if (prop.kind === "desk" || prop.kind === "table") {
3031
+ return /* @__PURE__ */ jsxs10("g", { children: [
3032
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y, width: prop.kind === "table" ? 48 : 32, height: prop.kind === "table" ? 24 : 12, fill: "rgba(126,91,67,0.94)" }),
3033
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 4, y: prop.y + 16, width: "6", height: "14", fill: "rgba(77,57,43,0.9)" }),
3034
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 32, y: prop.y + 16, width: "6", height: "14", fill: "rgba(77,57,43,0.9)" })
3035
+ ] });
3036
+ }
3037
+ if (prop.kind === "rack") {
3038
+ return /* @__PURE__ */ jsxs10("g", { children: [
3039
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y, width: "14", height: "36", fill: "rgba(88,82,75,0.92)" }),
3040
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 5, y: prop.y + 6, width: "24", height: "5", fill: "rgba(166,149,112,0.88)" }),
3041
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 5, y: prop.y + 18, width: "24", height: "5", fill: "rgba(166,149,112,0.88)" }),
3042
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 5, y: prop.y + 30, width: "24", height: "5", fill: "rgba(166,149,112,0.88)" })
3043
+ ] });
3044
+ }
3045
+ if (prop.kind === "console") {
3046
+ return /* @__PURE__ */ jsxs10("g", { children: [
3047
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y, width: "34", height: "22", fill: "rgba(72,95,86,0.94)" }),
3048
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 7, y: prop.y + 6, width: "18", height: "6", fill: "rgba(215,236,222,0.92)" }),
3049
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 7, y: prop.y + 22, width: "6", height: "12", fill: "rgba(56,64,60,0.88)" }),
3050
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 22, y: prop.y + 22, width: "6", height: "12", fill: "rgba(56,64,60,0.88)" })
3051
+ ] });
3052
+ }
3053
+ if (prop.kind === "bench") {
3054
+ return /* @__PURE__ */ jsxs10("g", { children: [
3055
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y, width: "44", height: "10", fill: "rgba(145,108,76,0.92)" }),
3056
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 7, y: prop.y + 10, width: "5", height: "14", fill: "rgba(82,62,49,0.9)" }),
3057
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 32, y: prop.y + 10, width: "5", height: "14", fill: "rgba(82,62,49,0.9)" })
3058
+ ] });
3059
+ }
3060
+ if (prop.kind === "plant") {
3061
+ return /* @__PURE__ */ jsxs10("g", { children: [
3062
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 6, y: prop.y + 18, width: "16", height: "12", fill: "rgba(127,90,63,0.92)" }),
3063
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 3, y: prop.y + 8, width: "22", height: "14", fill: "rgba(100,145,80,0.92)" }),
3064
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 10, y: prop.y, width: "8", height: "12", fill: "rgba(136,179,101,0.9)" })
3065
+ ] });
3066
+ }
3067
+ if (prop.kind === "bed") {
3068
+ return /* @__PURE__ */ jsxs10("g", { children: [
3069
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y, width: "52", height: "34", fill: "rgba(111,82,63,0.95)" }),
3070
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 5, y: prop.y + 5, width: "18", height: "12", fill: "rgba(238,229,198,0.94)" }),
3071
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 25, y: prop.y + 5, width: "22", height: "24", fill: "rgba(151,168,181,0.84)" })
3072
+ ] });
3073
+ }
3074
+ return /* @__PURE__ */ jsxs10("g", { children: [
3075
+ /* @__PURE__ */ jsx23("rect", { x: prop.x, y: prop.y + 4, width: "20", height: "18", fill: "rgba(143,105,70,0.9)" }),
3076
+ /* @__PURE__ */ jsx23("rect", { x: prop.x + 20, y: prop.y + 8, width: "14", height: "14", fill: "rgba(177,136,94,0.9)" })
3077
+ ] });
3078
+ }
3079
+ function renderAreaLabel(label, index) {
3080
+ const width = Math.max(72, estimateTextWidth2(label.label) + 16);
3081
+ return /* @__PURE__ */ jsxs10("g", { opacity: "0.94", children: [
3082
+ /* @__PURE__ */ jsx23("rect", { x: label.x, y: label.y, width, height: "20", fill: "rgba(250,236,178,0.94)", stroke: "rgba(110,77,47,0.64)", strokeWidth: "2" }),
3083
+ /* @__PURE__ */ jsx23(
3084
+ "text",
3085
+ {
3086
+ x: label.x + 7,
3087
+ y: label.y + 14,
3088
+ fill: "rgba(17,17,19,0.72)",
3089
+ fontSize: "9",
3090
+ fontWeight: "700",
3091
+ fontFamily: "var(--font-geist-mono, var(--font-sans))",
3092
+ children: label.label
3093
+ }
3094
+ )
3095
+ ] }, `room-label-${index}`);
3096
+ }
3097
+ function renderHub(zone, palette) {
3098
+ return /* @__PURE__ */ jsxs10("g", { children: [
3099
+ /* @__PURE__ */ jsx23("rect", { x: 736, y: 422, width: "128", height: "116", fill: "rgba(250,236,178,0.94)", stroke: palette.accent, strokeWidth: "4" }),
3100
+ /* @__PURE__ */ jsx23("rect", { x: 758, y: 444, width: "84", height: "70", fill: palette.rugStrong, opacity: "0.9" }),
3101
+ /* @__PURE__ */ jsx23("rect", { x: 788, y: 470, width: "28", height: "22", fill: palette.accent, opacity: "0.92" }),
3102
+ /* @__PURE__ */ jsx23("rect", { x: 774, y: 540, width: "52", height: "14", fill: "rgba(111,78,47,0.82)" }),
3103
+ /* @__PURE__ */ jsx23(
3104
+ "text",
3105
+ {
3106
+ x: "800",
3107
+ y: "414",
3108
+ textAnchor: "middle",
3109
+ fill: palette.accent,
3110
+ fontSize: "10",
3111
+ fontWeight: "800",
3112
+ fontFamily: "var(--font-geist-mono, var(--font-sans))",
3113
+ children: zone.badge.toUpperCase()
3114
+ }
3115
+ )
3116
+ ] });
3117
+ }
3118
+ function WorkboardRoomField(props) {
3119
+ const plan = WORKBOARD_ROOM_PLANS[props.zone.id];
3120
+ const palette = WORKBOARD_ROOM_PALETTE[props.zone.id];
3121
+ const externalPoi = props.pointsOfInterest.filter((item) => item.kind !== "hub");
3122
+ return /* @__PURE__ */ jsxs10(
3123
+ "svg",
3124
+ {
3125
+ viewBox: `0 0 ${props.stageWidth} ${props.stageHeight}`,
3126
+ className: "pointer-events-none absolute inset-0 h-full w-full",
3127
+ preserveAspectRatio: "xMidYMid meet",
3128
+ shapeRendering: "crispEdges",
3129
+ "aria-hidden": "true",
3130
+ children: [
3131
+ /* @__PURE__ */ jsx23("rect", { x: 0, y: 0, width: STAGE_WIDTH2, height: STAGE_HEIGHT2, fill: "rgba(247,244,236,0.98)" }),
3132
+ renderGrassTiles2(),
3133
+ plan.exteriorPaths.map(renderPath),
3134
+ plan.trees.map(renderTree2),
3135
+ plan.shrubs.map(renderShrub2),
3136
+ plan.floors.map((tile, index) => renderFloor(tile, index, palette)),
3137
+ plan.corridors.map((tile, index) => renderBlock(tile, index, "rgba(203,171,104,0.96)", "rgba(92,67,45,0.2)")),
3138
+ plan.rugs.map((tile, index) => renderRug(tile, index, palette)),
3139
+ plan.props.map((prop) => /* @__PURE__ */ jsx23("g", { opacity: "0.96", children: renderRoomProp(prop) }, prop.id)),
3140
+ externalPoi.map((poi) => /* @__PURE__ */ jsx23("g", { opacity: "0.58", children: renderRoomProp(poi) }, poi.id)),
3141
+ props.areaLabels.map(renderAreaLabel),
3142
+ plan.walls.map((tile, index) => renderWall(tile, index, palette)),
3143
+ plan.doors.map(renderDoor),
3144
+ renderHub(props.zone, palette)
3145
+ ]
3146
+ }
3147
+ );
3148
+ }
3149
+
3150
+ // src/components/workboard-game-room.tsx
3151
+ import { jsx as jsx24, jsxs as jsxs11 } from "react/jsx-runtime";
3152
+ var PIXEL_PANEL_CLIP3 = "polygon(0 6px,6px 6px,6px 0,calc(100% - 6px) 0,calc(100% - 6px) 6px,100% 6px,100% calc(100% - 6px),calc(100% - 6px) calc(100% - 6px),calc(100% - 0px) 100%,6px 100%,6px calc(100% - 6px),0 calc(100% - 6px))";
3153
+ function resolveRoomPoint(params) {
3154
+ return params.motionFrames?.[params.agentId] || params.fallback;
3155
+ }
3156
+ function ActiveSpeechBubble(props) {
3157
+ if (!props.item || !props.point) return null;
3158
+ const line = props.item.snapshot.current[0]?.summary || props.item.snapshot.recent[0]?.summary || props.item.headline;
3159
+ return /* @__PURE__ */ jsxs11(
3160
+ "div",
3161
+ {
3162
+ className: "pointer-events-none absolute z-30 max-w-[20rem] border-[3px] border-[#6e4d2f] bg-[#fff1bd] px-3 py-2 text-sm shadow-[5px_5px_0_rgba(72,50,33,0.22)]",
3163
+ style: {
3164
+ left: Math.min(Math.max(props.point.x + 28, 48), WORKBOARD_STAGE_WIDTH - 260),
3165
+ top: Math.min(Math.max(props.point.y - 74, 20), WORKBOARD_STAGE_HEIGHT - 72),
3166
+ clipPath: PIXEL_PANEL_CLIP3
3167
+ },
3168
+ children: [
3169
+ /* @__PURE__ */ jsxs11("p", { className: "text-[9px] uppercase tracking-[0.14em] text-[#6e4d2f]/70", children: [
3170
+ props.item.name,
3171
+ " \xB7 ",
3172
+ props.item.posture
3173
+ ] }),
3174
+ /* @__PURE__ */ jsxs11("p", { className: "mt-1 leading-6 text-[#352516]", children: [
3175
+ "\u201C",
3176
+ line,
3177
+ "\u201D"
3178
+ ] }),
3179
+ /* @__PURE__ */ jsx24("span", { className: "absolute -bottom-[11px] left-5 block h-[11px] w-[16px] border-x-[3px] border-b-[3px] border-[#6e4d2f] bg-[#fff1bd]" })
3180
+ ]
3181
+ }
3182
+ );
3183
+ }
3184
+ function resolveQuestEntries(item) {
3185
+ if (!item) return [];
3186
+ return [...item.snapshot.current, ...item.snapshot.recent].slice(0, 3);
3187
+ }
3188
+ function WorkboardRoomQuestLedger(props) {
3189
+ const entries = resolveQuestEntries(props.item);
3190
+ return /* @__PURE__ */ jsxs11(
3191
+ "div",
3192
+ {
3193
+ className: "pointer-events-none absolute left-[128px] top-[662px] z-10 w-64 border-[3px] border-[#6e4d2f] bg-[#c48752] px-3 py-2 shadow-[5px_5px_0_rgba(72,50,33,0.24)]",
3194
+ style: { clipPath: PIXEL_PANEL_CLIP3 },
3195
+ children: [
3196
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between gap-2", children: [
3197
+ /* @__PURE__ */ jsx24("div", { className: "text-[9px] uppercase tracking-[0.18em] text-[#fff1bd]/80", children: "wall board" }),
3198
+ /* @__PURE__ */ jsx24("div", { className: "text-[9px] uppercase tracking-[0.14em] text-[#fff1bd]/70", children: props.item ? props.item.posture : "empty" })
3199
+ ] }),
3200
+ /* @__PURE__ */ jsx24("div", { className: "mt-2 space-y-1.5", children: entries.length > 0 ? entries.map((entry, index) => /* @__PURE__ */ jsxs11("div", { className: "grid grid-cols-[1.5rem_1fr] gap-2 bg-[#fff1bd]/88 px-1.5 py-1 text-xs shadow-[2px_2px_0_rgba(72,50,33,0.18)]", children: [
3201
+ /* @__PURE__ */ jsxs11("span", { className: "grid h-5 place-items-center border border-[#6e4d2f]/35 bg-[#f8de8d] text-[9px] font-semibold text-[#6e4d2f]/70", children: [
3202
+ "Q",
3203
+ index + 1
3204
+ ] }),
3205
+ /* @__PURE__ */ jsxs11("span", { className: "min-w-0", children: [
3206
+ /* @__PURE__ */ jsx24("span", { className: "block truncate font-semibold leading-5 text-[#352516]", children: entry.title }),
3207
+ /* @__PURE__ */ jsx24("span", { className: "block truncate text-[11px] leading-4 text-[#6e4d2f]/70", children: entry.status })
3208
+ ] })
3209
+ ] }, entry.id)) : /* @__PURE__ */ jsx24("div", { className: "bg-[#fff1bd]/88 px-2 py-1 text-xs leading-5 text-[#6e4d2f]/70", children: "No public quest beats." }) })
3210
+ ]
3211
+ }
3212
+ );
3213
+ }
3214
+ function WorkboardRoomMiniMap(props) {
3215
+ const miniWidth = 144;
3216
+ const miniHeight = 88;
3217
+ const scaleX = miniWidth / WORKBOARD_STAGE_WIDTH;
3218
+ const scaleY = miniHeight / WORKBOARD_STAGE_HEIGHT;
3219
+ return /* @__PURE__ */ jsxs11(
3220
+ "div",
3221
+ {
3222
+ className: "pointer-events-none absolute left-[1228px] top-[662px] z-10 border-[3px] border-[#6e4d2f] bg-[#8a6040] p-2 shadow-[5px_5px_0_rgba(72,50,33,0.24)]",
3223
+ style: { clipPath: PIXEL_PANEL_CLIP3 },
3224
+ "aria-hidden": "true",
3225
+ children: [
3226
+ /* @__PURE__ */ jsx24("div", { className: "mb-1 text-[9px] uppercase tracking-[0.16em] text-[#fff1bd]/75", children: "floor plan" }),
3227
+ /* @__PURE__ */ jsxs11("svg", { width: miniWidth, height: miniHeight, viewBox: `0 0 ${miniWidth} ${miniHeight}`, shapeRendering: "crispEdges", children: [
3228
+ /* @__PURE__ */ jsx24("rect", { x: "0", y: "0", width: miniWidth, height: miniHeight, fill: "rgba(241,225,166,0.95)" }),
3229
+ /* @__PURE__ */ jsx24("rect", { x: "6", y: "6", width: miniWidth - 12, height: miniHeight - 12, fill: "rgba(255,241,189,0.9)", stroke: "rgba(110,77,47,0.42)", strokeWidth: "2" }),
3230
+ /* @__PURE__ */ jsx24("line", { x1: "12", y1: miniHeight / 2, x2: miniWidth - 12, y2: miniHeight / 2, stroke: "rgba(110,77,47,0.18)", strokeWidth: "2" }),
3231
+ /* @__PURE__ */ jsx24("line", { x1: miniWidth / 2, y1: "12", x2: miniWidth / 2, y2: miniHeight - 12, stroke: "rgba(110,77,47,0.18)", strokeWidth: "2" }),
3232
+ props.nodes.map((node) => {
3233
+ const active = node.item.id === props.activeItemId;
3234
+ return /* @__PURE__ */ jsx24(
3235
+ "rect",
3236
+ {
3237
+ x: Math.max(8, Math.min(miniWidth - 10, node.x * scaleX)),
3238
+ y: Math.max(8, Math.min(miniHeight - 10, node.y * scaleY)),
3239
+ width: active ? 6 : 4,
3240
+ height: active ? 6 : 4,
3241
+ fill: active ? "rgba(53,37,22,0.86)" : "rgba(110,77,47,0.48)"
3242
+ },
3243
+ node.item.id
3244
+ );
3245
+ }),
3246
+ props.activePoint ? /* @__PURE__ */ jsx24(
3247
+ "rect",
3248
+ {
3249
+ x: Math.max(8, Math.min(miniWidth - 12, props.activePoint.x * scaleX)) - 3,
3250
+ y: Math.max(8, Math.min(miniHeight - 12, props.activePoint.y * scaleY)) - 3,
3251
+ width: "10",
3252
+ height: "10",
3253
+ fill: "none",
3254
+ stroke: "rgba(53,37,22,0.86)",
3255
+ strokeWidth: "2"
3256
+ }
3257
+ ) : null
3258
+ ] })
3259
+ ]
3260
+ }
3261
+ );
3262
+ }
3263
+ function RoomEntranceSign(props) {
3264
+ return /* @__PURE__ */ jsxs11(
3265
+ "div",
3266
+ {
3267
+ className: "pointer-events-none absolute left-[674px] top-[850px] z-10 border-[3px] border-[#6e4d2f] bg-[#fff1bd] px-3 py-2 text-center shadow-[4px_4px_0_rgba(72,50,33,0.22)]",
3268
+ style: { clipPath: PIXEL_PANEL_CLIP3 },
3269
+ "aria-hidden": "true",
3270
+ children: [
3271
+ /* @__PURE__ */ jsx24("div", { className: "text-[9px] uppercase tracking-[0.18em] text-[#6e4d2f]/65", children: props.zone.subtitle }),
3272
+ /* @__PURE__ */ jsxs11("div", { className: "mt-1 text-[11px] font-bold uppercase tracking-[0.12em] text-[#352516]", children: [
3273
+ props.flowMode,
3274
+ " room"
3275
+ ] })
3276
+ ]
3277
+ }
3278
+ );
3279
+ }
3280
+ function buildRoomPolylinePath(points) {
3281
+ return points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x} ${point.y}`).join(" ");
3282
+ }
3283
+ function RoomPatrolTrail(props) {
3284
+ const d = buildRoomPolylinePath(props.route.points);
3285
+ return /* @__PURE__ */ jsxs11("g", { opacity: props.route.active ? 0.68 : 0.34, children: [
3286
+ /* @__PURE__ */ jsx24(
3287
+ "path",
3288
+ {
3289
+ d,
3290
+ fill: "none",
3291
+ stroke: "rgba(101,75,52,0.34)",
3292
+ strokeWidth: props.route.active ? 12 : 8,
3293
+ strokeLinecap: "square",
3294
+ strokeLinejoin: "miter"
3295
+ }
3296
+ ),
3297
+ /* @__PURE__ */ jsx24(
3298
+ "path",
3299
+ {
3300
+ d,
3301
+ fill: "none",
3302
+ stroke: "rgba(245,211,142,0.38)",
3303
+ strokeWidth: props.route.active ? 4 : 3,
3304
+ strokeDasharray: "6 12",
3305
+ strokeLinecap: "square",
3306
+ strokeLinejoin: "miter"
3307
+ }
3308
+ ),
3309
+ props.route.points.map((point, index) => /* @__PURE__ */ jsx24(
3310
+ "rect",
3311
+ {
3312
+ x: point.x - 4,
3313
+ y: point.y - 4,
3314
+ width: "8",
3315
+ height: "8",
3316
+ fill: "rgba(245,211,142,0.52)"
3317
+ },
3318
+ `${props.route.id}-trail-${index}`
3319
+ ))
3320
+ ] });
3321
+ }
3322
+ function RoomHubMapObject(props) {
3323
+ return /* @__PURE__ */ jsx24(
3324
+ "div",
3325
+ {
3326
+ className: "pointer-events-none absolute z-20 -translate-x-1/2 -translate-y-1/2",
3327
+ style: { left: props.point.x, top: props.point.y },
3328
+ "aria-hidden": "true",
3329
+ children: /* @__PURE__ */ jsxs11("div", { className: "relative h-24 w-28", children: [
3330
+ /* @__PURE__ */ jsx24("div", { className: "absolute left-4 top-5 h-14 w-20 border-[3px] border-[#6e4d2f] bg-[#f8de8d] shadow-[4px_4px_0_rgba(72,50,33,0.28)]" }),
3331
+ /* @__PURE__ */ jsx24("div", { className: "absolute left-8 top-8 h-7 w-12 bg-[#6b8f83]" }),
3332
+ /* @__PURE__ */ jsx24("div", { className: "absolute left-11 top-11 h-2 w-6 bg-[#cde6d9]" }),
3333
+ /* @__PURE__ */ jsx24("div", { className: "absolute left-7 top-[3.9rem] h-3 w-4 bg-[#6e4d2f]" }),
3334
+ /* @__PURE__ */ jsx24("div", { className: "absolute right-7 top-[3.9rem] h-3 w-4 bg-[#6e4d2f]" }),
3335
+ /* @__PURE__ */ jsxs11("div", { className: "absolute left-1/2 top-0 -translate-x-1/2 border-2 border-[#6e4d2f] bg-[#fff1bd] px-2 py-1 text-center shadow-[3px_3px_0_rgba(72,50,33,0.18)]", children: [
3336
+ /* @__PURE__ */ jsx24("div", { className: "text-[8px] uppercase tracking-[0.13em] text-[#6e4d2f]/70", children: props.badge }),
3337
+ /* @__PURE__ */ jsx24("div", { className: "max-w-20 truncate text-[10px] font-bold leading-3 text-[#352516]", children: props.title })
3338
+ ] })
3339
+ ] })
3340
+ }
3341
+ );
3342
+ }
3343
+ function renderRoomStationObject(params) {
3344
+ const deskFill = params.active ? "rgba(158,107,65,0.98)" : "rgba(138,96,65,0.84)";
3345
+ const chairFill = params.active ? "rgba(93,128,103,0.95)" : "rgba(91,91,78,0.72)";
3346
+ return /* @__PURE__ */ jsxs11("g", { opacity: params.active ? 0.96 : 0.66, children: [
3347
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x - 23, y: params.node.y - 19, width: "46", height: "24", fill: deskFill, stroke: "rgba(72,50,33,0.56)", strokeWidth: "3" }),
3348
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x - 15, y: params.node.y - 14, width: "18", height: "7", fill: "rgba(231,211,148,0.76)" }),
3349
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x + 7, y: params.node.y - 15, width: "10", height: "9", fill: "rgba(79,102,96,0.9)" }),
3350
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x - 12, y: params.node.y + 8, width: "24", height: "12", fill: chairFill }),
3351
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x - 21, y: params.node.y + 22, width: "8", height: "7", fill: "rgba(72,50,33,0.34)" }),
3352
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x + 13, y: params.node.y + 22, width: "8", height: "7", fill: "rgba(72,50,33,0.34)" }),
3353
+ /* @__PURE__ */ jsx24("rect", { x: params.node.x + 20, y: params.node.y - 26, width: "22", height: "14", fill: "rgba(250,236,178,0.92)", stroke: "rgba(110,77,47,0.54)", strokeWidth: "2" }),
3354
+ /* @__PURE__ */ jsx24(
3355
+ "text",
3356
+ {
3357
+ x: params.node.x + 31,
3358
+ y: params.node.y - 16,
3359
+ textAnchor: "middle",
3360
+ fill: "rgba(72,50,33,0.78)",
3361
+ fontSize: "8",
3362
+ fontWeight: "800",
3363
+ fontFamily: "var(--font-geist-mono, var(--font-sans))",
3364
+ children: params.index + 1
3365
+ }
3366
+ ),
3367
+ params.active ? /* @__PURE__ */ jsx24("rect", { x: params.node.x - 29, y: params.node.y - 25, width: "58", height: "58", fill: "none", stroke: "rgba(17,17,19,0.42)", strokeWidth: "2", strokeDasharray: "4 5" }) : null
3368
+ ] }, `focused-station-${params.node.item.id}`);
3369
+ }
3370
+ function WorkboardGameRoom(props) {
3371
+ const [hoveredTag, setHoveredTag] = React5.useState(null);
3372
+ const hubPoint = React5.useMemo(
3373
+ () => props.gameMap.pointsOfInterest.find((item) => item.kind === "hub") || {
3374
+ id: `${props.zone.id}-hub-fallback`,
3375
+ kind: "hub",
3376
+ x: WORKBOARD_STAGE_WIDTH / 2,
3377
+ y: WORKBOARD_STAGE_HEIGHT / 2
3378
+ },
3379
+ [props.gameMap.pointsOfInterest, props.zone.id]
3380
+ );
3381
+ const center = { x: hubPoint.x, y: hubPoint.y };
3382
+ const roomNodes = React5.useMemo(
3383
+ () => props.gameMap.actors.filter((actor) => actor.zoneId === props.zone.id && actor.focusedAnchor).map((actor) => ({
3384
+ item: actor.agent,
3385
+ x: actor.focusedAnchor?.x || hubPoint.x,
3386
+ y: actor.focusedAnchor?.y || hubPoint.y,
3387
+ routeId: actor.focusedRouteId
3388
+ })),
3389
+ [hubPoint.x, hubPoint.y, props.gameMap.actors, props.zone.id]
3390
+ );
3391
+ const activeItem = React5.useMemo(
3392
+ () => props.items.find((item) => item.id === props.selectedAgentId) || props.items[0] || null,
3393
+ [props.items, props.selectedAgentId]
3394
+ );
3395
+ const activeNode = React5.useMemo(
3396
+ () => activeItem ? roomNodes.find((entry) => entry.item.id === activeItem.id) || null : null,
3397
+ [activeItem, roomNodes]
3398
+ );
3399
+ const activePoint = React5.useMemo(() => {
3400
+ if (!activeItem || !activeNode) return null;
3401
+ return resolveRoomPoint({
3402
+ agentId: activeNode.item.id,
3403
+ fallback: { x: activeNode.x, y: activeNode.y },
3404
+ motionFrames: props.motionFrames
3405
+ });
3406
+ }, [activeItem, activeNode, props.motionFrames]);
3407
+ return /* @__PURE__ */ jsxs11(
3408
+ "div",
3409
+ {
3410
+ className: cn(
3411
+ "relative overflow-hidden border-2 bg-[linear-gradient(145deg,rgba(251,250,247,0.96),rgba(245,248,245,0.88))]",
3412
+ props.zone.borderClassName
3413
+ ),
3414
+ style: { width: WORKBOARD_STAGE_WIDTH, height: WORKBOARD_STAGE_HEIGHT, clipPath: PIXEL_PANEL_CLIP3, imageRendering: "pixelated" },
3415
+ children: [
3416
+ /* @__PURE__ */ jsx24(
3417
+ WorkboardRoomField,
3418
+ {
3419
+ zone: props.zone,
3420
+ stageWidth: WORKBOARD_STAGE_WIDTH,
3421
+ stageHeight: WORKBOARD_STAGE_HEIGHT,
3422
+ pointsOfInterest: props.gameMap.pointsOfInterest,
3423
+ areaLabels: props.gameMap.areaLabels
3424
+ }
3425
+ ),
3426
+ /* @__PURE__ */ jsx24("div", { className: cn("pointer-events-none absolute left-1/2 top-1/2 h-56 w-56 -translate-x-1/2 -translate-y-1/2", props.zone.glowClassName) }),
3427
+ /* @__PURE__ */ jsx24(RoomEntranceSign, { zone: props.zone, flowMode: props.flowMode }),
3428
+ /* @__PURE__ */ jsxs11(
3429
+ "svg",
3430
+ {
3431
+ viewBox: `0 0 ${WORKBOARD_STAGE_WIDTH} ${WORKBOARD_STAGE_HEIGHT}`,
3432
+ className: "pointer-events-none absolute inset-0 h-full w-full",
3433
+ preserveAspectRatio: "xMidYMid meet",
3434
+ "aria-hidden": "true",
3435
+ children: [
3436
+ props.gameMap.patrols.map((route) => /* @__PURE__ */ jsx24(RoomPatrolTrail, { route }, route.id)),
3437
+ activePoint ? /* @__PURE__ */ jsxs11("g", { children: [
3438
+ /* @__PURE__ */ jsx24(
3439
+ "path",
3440
+ {
3441
+ d: buildWorkboardTilePath({ from: center, to: activePoint }),
3442
+ fill: "none",
3443
+ stroke: "rgba(245,211,142,0.58)",
3444
+ strokeWidth: "10",
3445
+ strokeOpacity: "0.52",
3446
+ strokeLinecap: "square"
3447
+ }
3448
+ ),
3449
+ /* @__PURE__ */ jsx24(
3450
+ "rect",
3451
+ {
3452
+ x: activePoint.x - 14,
3453
+ y: activePoint.y - 14,
3454
+ width: "28",
3455
+ height: "28",
3456
+ fill: "none",
3457
+ className: props.zone.lineClassName,
3458
+ strokeWidth: "2",
3459
+ strokeDasharray: "4 5",
3460
+ style: { animation: "workboard-dash 1.5s linear infinite" }
3461
+ }
3462
+ ),
3463
+ /* @__PURE__ */ jsx24(
3464
+ "rect",
3465
+ {
3466
+ x: center.x - 7,
3467
+ y: center.y - 7,
3468
+ width: "14",
3469
+ height: "14",
3470
+ fill: "none",
3471
+ className: props.zone.lineClassName,
3472
+ strokeWidth: "2",
3473
+ strokeOpacity: "0.72"
3474
+ }
3475
+ )
3476
+ ] }) : null,
3477
+ roomNodes.map((node) => {
3478
+ const point = resolveRoomPoint({
3479
+ agentId: node.item.id,
3480
+ fallback: { x: node.x, y: node.y },
3481
+ motionFrames: props.motionFrames
3482
+ });
3483
+ const path = buildWorkboardTilePath({ from: center, to: point });
3484
+ return /* @__PURE__ */ jsx24(
3485
+ "path",
3486
+ {
3487
+ d: path,
3488
+ fill: "none",
3489
+ className: cn(
3490
+ props.zone.lineClassName,
3491
+ props.selectedAgentId === node.item.id ? "opacity-78" : "opacity-34"
3492
+ ),
3493
+ strokeWidth: props.selectedAgentId === node.item.id ? 2.8 : 1.5,
3494
+ strokeDasharray: props.selectedAgentId === node.item.id ? "6 8" : "4 10",
3495
+ style: {
3496
+ animation: `workboard-dash ${props.flowMode === "turbo" ? "1.6s" : "2.8s"} linear infinite`
3497
+ }
3498
+ },
3499
+ `focused-line-${node.item.id}`
3500
+ );
3501
+ }),
3502
+ roomNodes.map(
3503
+ (node, index) => renderRoomStationObject({
3504
+ node,
3505
+ index,
3506
+ active: props.selectedAgentId === node.item.id
3507
+ })
3508
+ )
3509
+ ]
3510
+ }
3511
+ ),
3512
+ /* @__PURE__ */ jsx24(RoomHubMapObject, { badge: props.zone.badge, title: props.zone.title, point: hubPoint }),
3513
+ /* @__PURE__ */ jsxs11(
3514
+ "div",
3515
+ {
3516
+ className: "pointer-events-none absolute z-20 border-2 border-[#6e4d2f]/70 bg-[#fff1bd]/90 px-2 py-1 text-[10px] uppercase tracking-[0.14em] text-[#6e4d2f]/70 shadow-[3px_3px_0_rgba(72,50,33,0.16)]",
3517
+ style: { left: hubPoint.x + 38, top: hubPoint.y + 18, clipPath: PIXEL_PANEL_CLIP3 },
3518
+ children: [
3519
+ "sprites ",
3520
+ props.items.length
3521
+ ]
3522
+ }
3523
+ ),
3524
+ roomNodes.map((node) => {
3525
+ const point = resolveRoomPoint({
3526
+ agentId: node.item.id,
3527
+ fallback: { x: node.x, y: node.y },
3528
+ motionFrames: props.motionFrames
3529
+ });
3530
+ return /* @__PURE__ */ jsx24(
3531
+ WorkboardStageAgentNode,
3532
+ {
3533
+ item: node.item,
3534
+ zone: props.zone,
3535
+ point,
3536
+ active: props.selectedAgentId === node.item.id,
3537
+ faded: false,
3538
+ mode: "focused",
3539
+ onSelect: props.onSelectAgent,
3540
+ onHoverChange: setHoveredTag
3541
+ },
3542
+ `focused-node-${node.item.id}`
3543
+ );
3544
+ }),
3545
+ /* @__PURE__ */ jsx24(WorkboardRoomQuestLedger, { item: activeItem }),
3546
+ /* @__PURE__ */ jsx24(WorkboardRoomMiniMap, { activePoint, activeItemId: activeItem?.id, nodes: roomNodes }),
3547
+ /* @__PURE__ */ jsx24(ActiveSpeechBubble, { item: activeItem, point: activePoint }),
3548
+ /* @__PURE__ */ jsx24(
3549
+ "svg",
3550
+ {
3551
+ viewBox: `0 0 ${WORKBOARD_STAGE_WIDTH} ${WORKBOARD_STAGE_HEIGHT}`,
3552
+ className: "pointer-events-none absolute inset-0 h-full w-full",
3553
+ preserveAspectRatio: "xMidYMid meet",
3554
+ "aria-hidden": "true",
3555
+ children: /* @__PURE__ */ jsx24(PixelHoverTag, { tag: hoveredTag, stageWidth: WORKBOARD_STAGE_WIDTH, stageHeight: WORKBOARD_STAGE_HEIGHT })
3556
+ }
3557
+ )
3558
+ ]
3559
+ }
3560
+ );
3561
+ }
3562
+
3563
+ // src/components/workboard-motion.ts
3564
+ import * as React6 from "react";
3565
+ function distance(a, b) {
3566
+ return Math.hypot(b.x - a.x, b.y - a.y);
3567
+ }
3568
+ function resolveDirection(params) {
3569
+ const dx = params.to.x - params.from.x;
3570
+ const dy = params.to.y - params.from.y;
3571
+ if (Math.abs(dx) >= Math.abs(dy)) {
3572
+ return dx < 0 ? "left" : "right";
3573
+ }
3574
+ return dy < 0 ? "up" : "down";
3575
+ }
3576
+ function resolveRouteFrame(params) {
3577
+ if (params.route.length < 2) {
3578
+ return {
3579
+ ...params.route[0] || { x: 0, y: 0 },
3580
+ direction: "down",
3581
+ state: "dwell"
3582
+ };
3583
+ }
3584
+ const dwellRatio = Math.min(Math.max(params.dwellRatio || 0, 0), 0.72);
3585
+ if (dwellRatio > 0) {
3586
+ const legCount = params.route.length;
3587
+ const legProgress = params.progress % 1 * legCount;
3588
+ const legIndex = Math.floor(legProgress) % legCount;
3589
+ const localProgress = legProgress - Math.floor(legProgress);
3590
+ const from = params.route[legIndex];
3591
+ const to = params.route[(legIndex + 1) % params.route.length];
3592
+ const travelRatio = 1 - dwellRatio;
3593
+ const direction = resolveDirection({ from, to });
3594
+ if (localProgress > travelRatio) {
3595
+ return {
3596
+ ...to,
3597
+ direction,
3598
+ state: "dwell"
3599
+ };
3600
+ }
3601
+ const easedProgress = Math.min(localProgress / travelRatio, 1);
3602
+ return {
3603
+ x: from.x + (to.x - from.x) * easedProgress,
3604
+ y: from.y + (to.y - from.y) * easedProgress,
3605
+ direction,
3606
+ state: "walking"
3607
+ };
3608
+ }
3609
+ const segments = params.route.map((point, index) => {
3610
+ const next = params.route[(index + 1) % params.route.length];
3611
+ return {
3612
+ from: point,
3613
+ to: next,
3614
+ length: distance(point, next)
3615
+ };
3616
+ });
3617
+ const totalLength = segments.reduce((acc, segment) => acc + segment.length, 0);
3618
+ if (totalLength <= 0) {
3619
+ return {
3620
+ ...params.route[0],
3621
+ direction: "down",
3622
+ state: "dwell"
3623
+ };
3624
+ }
3625
+ let cursor = params.progress % 1 * totalLength;
3626
+ for (const segment of segments) {
3627
+ if (cursor <= segment.length) {
3628
+ const ratio = segment.length <= 0 ? 0 : cursor / segment.length;
3629
+ return {
3630
+ x: segment.from.x + (segment.to.x - segment.from.x) * ratio,
3631
+ y: segment.from.y + (segment.to.y - segment.from.y) * ratio,
3632
+ direction: resolveDirection({ from: segment.from, to: segment.to }),
3633
+ state: "walking"
3634
+ };
3635
+ }
3636
+ cursor -= segment.length;
3637
+ }
3638
+ return {
3639
+ ...params.route[0],
3640
+ direction: "down",
3641
+ state: "dwell"
3642
+ };
3643
+ }
3644
+ function snapPoint(params) {
3645
+ const snapSize = params.snapSize || 0;
3646
+ if (snapSize <= 0) {
3647
+ return params.point;
3648
+ }
3649
+ return {
3650
+ ...params.point,
3651
+ x: Math.round(params.point.x / snapSize) * snapSize,
3652
+ y: Math.round(params.point.y / snapSize) * snapSize
3653
+ };
3654
+ }
3655
+ function useWorkboardMotion(params) {
3656
+ const [frames, setFrames] = React6.useState({});
3657
+ React6.useEffect(() => {
3658
+ if (params.nodes.length === 0) {
3659
+ setFrames({});
3660
+ return;
3661
+ }
3662
+ const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3663
+ if (reduceMotion) {
3664
+ const stillFrames = params.nodes.reduce(
3665
+ (acc, node) => {
3666
+ acc[node.id] = {
3667
+ x: node.anchor.x,
3668
+ y: node.anchor.y,
3669
+ direction: "down",
3670
+ state: "dwell"
3671
+ };
3672
+ return acc;
3673
+ },
3674
+ {}
3675
+ );
3676
+ setFrames(stillFrames);
3677
+ return;
3678
+ }
3679
+ let rafId = 0;
3680
+ let start = performance.now();
3681
+ const frame = (now) => {
3682
+ const elapsed = (now - start) / 1e3;
3683
+ const flowFactor = params.flowMode === "turbo" ? 1.45 : 0.78;
3684
+ const nextFrames = params.nodes.reduce(
3685
+ (acc, node) => {
3686
+ if (node.mode === "route" && node.route && node.route.length > 1) {
3687
+ const routePoint = resolveRouteFrame({
3688
+ route: node.route,
3689
+ progress: elapsed * node.speed * 0.09 * flowFactor + node.phase * 0.07,
3690
+ dwellRatio: node.dwellRatio
3691
+ });
3692
+ acc[node.id] = snapPoint({ point: routePoint, snapSize: node.snapSize });
3693
+ return acc;
3694
+ }
3695
+ const t = elapsed * node.speed * flowFactor + node.phase;
3696
+ const driftX = Math.sin(t) * node.swayX + Math.cos(t * 0.61 + node.phase) * node.swayX * 0.4;
3697
+ const driftY = Math.cos(t * 0.9 + node.phase * 0.8) * node.swayY + Math.sin(t * 0.55) * node.swayY * 0.35;
3698
+ const point = {
3699
+ x: node.anchor.x + driftX,
3700
+ y: node.anchor.y + driftY
3701
+ };
3702
+ acc[node.id] = snapPoint({
3703
+ snapSize: node.snapSize,
3704
+ point: {
3705
+ ...point,
3706
+ direction: resolveDirection({ from: node.anchor, to: point }),
3707
+ state: "walking"
3708
+ }
3709
+ });
3710
+ return acc;
3711
+ },
3712
+ {}
3713
+ );
3714
+ setFrames(nextFrames);
3715
+ rafId = window.requestAnimationFrame(frame);
3716
+ };
3717
+ rafId = window.requestAnimationFrame(frame);
3718
+ return () => {
3719
+ window.cancelAnimationFrame(rafId);
3720
+ };
3721
+ }, [params.flowMode, params.nodes]);
3722
+ return frames;
3723
+ }
3724
+
3725
+ // src/components/workboard.tsx
3726
+ import { Fragment as Fragment3, jsx as jsx25, jsxs as jsxs12 } from "react/jsx-runtime";
3727
+ var PIXEL_PANEL_CLIP4 = "polygon(0 6px,6px 6px,6px 0,calc(100% - 6px) 0,calc(100% - 6px) 6px,100% 6px,100% calc(100% - 6px),calc(100% - 6px) calc(100% - 6px),calc(100% - 0px) 100%,6px 100%,6px calc(100% - 6px),0 calc(100% - 6px))";
3728
+ function resolveSelectedAgent(params) {
3729
+ const items = params.board?.agents || [];
3730
+ if (items.length === 0) return null;
3731
+ return items.find((item) => item.id === params.selectedAgentId) || items[0] || null;
3732
+ }
3733
+ function resolveZoneLead(params) {
3734
+ const items = (params.board?.agents || []).filter((item) => resolveZoneId(item) === params.zoneId);
3735
+ if (items.length === 0) return null;
3736
+ return items.find((item) => item.snapshot.current.some((entry) => entry.status === "active")) || items.find((item) => item.running) || items[0] || null;
3737
+ }
3738
+ function useWorkboardStageViewportScale(params) {
3739
+ const [size, setSize] = React7.useState({ width: WORKBOARD_STAGE_WIDTH, height: WORKBOARD_STAGE_HEIGHT });
3740
+ React7.useEffect(() => {
3741
+ const element = params.viewportRef.current;
3742
+ if (!element) return void 0;
3743
+ const updateSize = () => {
3744
+ const rect2 = element.getBoundingClientRect();
3745
+ setSize({
3746
+ width: Math.max(1, rect2.width),
3747
+ height: Math.max(1, rect2.height)
3748
+ });
3749
+ };
3750
+ updateSize();
3751
+ if (typeof ResizeObserver === "undefined") {
3752
+ window.addEventListener("resize", updateSize);
3753
+ return () => window.removeEventListener("resize", updateSize);
3754
+ }
3755
+ const observer = new ResizeObserver(updateSize);
3756
+ observer.observe(element);
3757
+ return () => observer.disconnect();
3758
+ }, [params.viewportRef]);
3759
+ const horizontalPadding = params.isFullscreen ? 0 : 16;
3760
+ const verticalHudReserve = params.isFullscreen ? 0 : 20;
3761
+ const widthScale = (size.width - horizontalPadding) / WORKBOARD_STAGE_WIDTH;
3762
+ const heightScale = (size.height - verticalHudReserve) / WORKBOARD_STAGE_HEIGHT;
3763
+ const rawScale = params.isFullscreen ? Math.max(widthScale, heightScale) : Math.min(widthScale, heightScale);
3764
+ return Math.max(params.isFullscreen ? 1 : 0.72, Math.min(params.isFullscreen ? 4 : 1.35, rawScale || 1));
3765
+ }
3766
+ function WorkboardScaledStage(props) {
3767
+ return /* @__PURE__ */ jsx25(
3768
+ "div",
3769
+ {
3770
+ className: "relative shrink-0",
3771
+ style: {
3772
+ width: WORKBOARD_STAGE_WIDTH * props.scale,
3773
+ height: WORKBOARD_STAGE_HEIGHT * props.scale
3774
+ },
3775
+ children: /* @__PURE__ */ jsx25(
3776
+ "div",
3777
+ {
3778
+ className: "absolute left-0 top-0",
3779
+ style: {
3780
+ width: WORKBOARD_STAGE_WIDTH,
3781
+ height: WORKBOARD_STAGE_HEIGHT,
3782
+ transform: `scale(${props.scale})`,
3783
+ transformOrigin: "left top",
3784
+ imageRendering: "pixelated"
3785
+ },
3786
+ children: props.children
3787
+ }
3788
+ )
3789
+ }
3790
+ );
3791
+ }
3792
+ function WorkboardGameHud(props) {
3793
+ const worldLine = [
3794
+ `sprites ${props.board.summary.totalAgents}`,
3795
+ `live ${props.board.summary.liveAgents}`,
3796
+ `active ${props.board.summary.activeAgents}`,
3797
+ `quiet ${props.board.summary.quietAgents}`,
3798
+ props.selected ? `focus ${props.selected.name}` : "focus world",
3799
+ `tick ${formatWorkboardRelativeTime(props.board.collectedAt)}`
3800
+ ];
3801
+ return /* @__PURE__ */ jsxs12(Fragment3, { children: [
3802
+ /* @__PURE__ */ jsxs12("div", { className: "absolute inset-x-2 top-2 z-40 flex flex-wrap items-start justify-between gap-2", children: [
3803
+ /* @__PURE__ */ jsxs12(
3804
+ "div",
3805
+ {
3806
+ className: "border-2 border-border/70 bg-[rgba(255,252,247,0.92)] px-3 py-2 shadow-[0_3px_0_rgba(17,17,19,0.12)]",
3807
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3808
+ children: [
3809
+ /* @__PURE__ */ jsx25("div", { className: "text-[10px] uppercase tracking-[0.2em] text-foreground/42", children: props.stageLevel === "clusters" ? "world map" : `${props.activeZone.title} room` }),
3810
+ /* @__PURE__ */ jsx25("div", { className: "mt-1 text-lg font-semibold leading-none tracking-[-0.06em] text-foreground", children: "Workboard Game World" }),
3811
+ /* @__PURE__ */ jsx25("div", { className: "mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-[10px] uppercase tracking-[0.16em] text-foreground/42", children: worldLine.map((item, index) => /* @__PURE__ */ jsxs12(React7.Fragment, { children: [
3812
+ index > 0 ? /* @__PURE__ */ jsx25("span", { className: "text-foreground/20", children: "/" }) : null,
3813
+ /* @__PURE__ */ jsx25("span", { children: item })
3814
+ ] }, item)) })
3815
+ ]
3816
+ }
3817
+ ),
3818
+ /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap justify-end gap-1.5", children: [
3819
+ props.stageLevel === "agents" ? /* @__PURE__ */ jsx25(
3820
+ "button",
3821
+ {
3822
+ type: "button",
3823
+ onClick: props.onBackToAtlas,
3824
+ className: "border-2 border-border/70 bg-[rgba(255,252,247,0.9)] px-2 py-1 text-[11px] uppercase tracking-[0.14em] shadow-[0_2px_0_rgba(17,17,19,0.12)] transition-[filter] hover:brightness-105",
3825
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3826
+ children: "world"
3827
+ }
3828
+ ) : null,
3829
+ /* @__PURE__ */ jsx25(
3830
+ "button",
3831
+ {
3832
+ type: "button",
3833
+ onClick: props.onToggleFlowMode,
3834
+ className: "border-2 border-border/70 bg-[rgba(255,252,247,0.9)] px-2 py-1 text-[11px] uppercase tracking-[0.14em] shadow-[0_2px_0_rgba(17,17,19,0.12)] transition-[filter] hover:brightness-105",
3835
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3836
+ "aria-pressed": props.flowMode === "turbo",
3837
+ children: props.flowMode
3838
+ }
3839
+ ),
3840
+ /* @__PURE__ */ jsxs12(
3841
+ "button",
3842
+ {
3843
+ type: "button",
3844
+ onClick: () => props.onRefresh?.(),
3845
+ className: "inline-flex items-center gap-1.5 border-2 border-border/70 bg-[rgba(255,252,247,0.9)] px-2 py-1 text-[11px] uppercase tracking-[0.14em] shadow-[0_2px_0_rgba(17,17,19,0.12)] transition-[filter] hover:brightness-105 disabled:opacity-45",
3846
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3847
+ disabled: !props.onRefresh,
3848
+ children: [
3849
+ /* @__PURE__ */ jsx25(RefreshCwIcon, { className: cn("size-3.5", props.loading ? "animate-spin" : "") }),
3850
+ "tick"
3851
+ ]
3852
+ }
3853
+ ),
3854
+ /* @__PURE__ */ jsxs12(
3855
+ "button",
3856
+ {
3857
+ type: "button",
3858
+ onClick: props.onToggleFullscreen,
3859
+ className: "inline-flex items-center gap-1.5 border-2 border-border/70 bg-[rgba(255,252,247,0.9)] px-2 py-1 text-[11px] uppercase tracking-[0.14em] shadow-[0_2px_0_rgba(17,17,19,0.12)] transition-[filter] hover:brightness-105",
3860
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3861
+ "aria-pressed": props.isFullscreen,
3862
+ children: [
3863
+ props.isFullscreen ? /* @__PURE__ */ jsx25(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx25(Maximize2Icon, { className: "size-3.5" }),
3864
+ props.isFullscreen ? "exit" : "full"
3865
+ ]
3866
+ }
3867
+ )
3868
+ ] })
3869
+ ] }),
3870
+ /* @__PURE__ */ jsx25("div", { className: "absolute inset-x-2 bottom-2 z-40 flex justify-center", children: /* @__PURE__ */ jsx25(
3871
+ "div",
3872
+ {
3873
+ className: "flex max-w-full gap-1 overflow-x-auto border-2 border-border/70 bg-[rgba(255,252,247,0.9)] px-2 py-1.5 shadow-[0_3px_0_rgba(17,17,19,0.12)]",
3874
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3875
+ "aria-label": "Workboard zone portals",
3876
+ children: props.zones.map((zone) => /* @__PURE__ */ jsx25(
3877
+ WorkboardZonePortalButton,
3878
+ {
3879
+ zone,
3880
+ current: zone.id === props.activeZone.id,
3881
+ onSelect: props.onSelectZone
3882
+ },
3883
+ zone.id
3884
+ ))
3885
+ }
3886
+ ) }),
3887
+ /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute bottom-[4.7rem] left-2 z-40 hidden border-2 border-border/60 bg-[rgba(255,252,247,0.82)] px-2 py-1 text-[9px] uppercase tracking-[0.13em] text-foreground/44 shadow-[0_2px_0_rgba(17,17,19,0.1)] md:block", style: { clipPath: PIXEL_PANEL_CLIP4 }, children: "left/right switch zone / enter open / esc world" })
3888
+ ] });
3889
+ }
3890
+ function WorkboardZonePortalButton(props) {
3891
+ return /* @__PURE__ */ jsxs12(
3892
+ "button",
3893
+ {
3894
+ type: "button",
3895
+ onClick: () => props.onSelect?.(props.zone.id),
3896
+ className: cn(
3897
+ "group inline-flex min-w-24 items-center gap-2 border border-border/50 px-2 py-1 text-left transition-[filter,transform] duration-200 hover:-translate-y-0.5 hover:brightness-105 focus:outline-none focus-visible:-translate-y-0.5",
3898
+ props.current ? "bg-foreground text-background" : "bg-[rgba(255,252,247,0.76)] text-foreground"
3899
+ ),
3900
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3901
+ "aria-pressed": props.current,
3902
+ children: [
3903
+ /* @__PURE__ */ jsx25(
3904
+ "span",
3905
+ {
3906
+ className: cn(
3907
+ "grid size-5 place-items-center border text-[10px] font-semibold leading-none",
3908
+ props.current ? "border-background/70" : "border-foreground/35"
3909
+ ),
3910
+ "aria-hidden": "true",
3911
+ children: props.zone.count
3912
+ }
3913
+ ),
3914
+ /* @__PURE__ */ jsxs12("span", { className: "min-w-0", children: [
3915
+ /* @__PURE__ */ jsx25("span", { className: "block truncate text-[10px] font-semibold uppercase tracking-[0.13em]", children: props.zone.badge }),
3916
+ /* @__PURE__ */ jsx25("span", { className: cn("block truncate text-[9px]", props.current ? "text-background/70" : "text-foreground/46"), children: "portal" })
3917
+ ] })
3918
+ ]
3919
+ }
3920
+ );
3921
+ }
3922
+ function WorkboardWorldPortalOverlay(props) {
3923
+ if (!props.portal) return null;
3924
+ return /* @__PURE__ */ jsxs12(
3925
+ "div",
3926
+ {
3927
+ className: "pointer-events-none absolute inset-0 z-50 grid place-items-center bg-[rgba(255,252,247,0.18)]",
3928
+ "aria-hidden": "true",
3929
+ children: [
3930
+ /* @__PURE__ */ jsx25("div", { className: "absolute inset-y-0 left-0 w-1/2 origin-left bg-[rgba(17,17,19,0.82)] workboard-portal-left" }),
3931
+ /* @__PURE__ */ jsx25("div", { className: "absolute inset-y-0 right-0 w-1/2 origin-right bg-[rgba(17,17,19,0.82)] workboard-portal-right" }),
3932
+ /* @__PURE__ */ jsxs12(
3933
+ "div",
3934
+ {
3935
+ className: "relative z-10 border-2 border-background/80 bg-[rgba(17,17,19,0.88)] px-4 py-3 text-center text-background shadow-[0_4px_0_rgba(17,17,19,0.2)] workboard-portal-label",
3936
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
3937
+ children: [
3938
+ /* @__PURE__ */ jsx25("div", { className: "text-[10px] uppercase tracking-[0.22em] text-background/58", children: props.portal.mode === "enter" ? "enter room" : "return atlas" }),
3939
+ /* @__PURE__ */ jsx25("div", { className: "mt-1 text-base font-semibold tracking-[-0.05em]", children: props.portal.label })
3940
+ ]
3941
+ }
3942
+ )
3943
+ ]
3944
+ },
3945
+ props.portal.key
3946
+ );
3947
+ }
3948
+ function WorkboardGameStyles() {
3949
+ return /* @__PURE__ */ jsx25("style", { children: `
3950
+ @keyframes workboard-dash {
3951
+ from { stroke-dashoffset: 0; }
3952
+ to { stroke-dashoffset: 18; }
3953
+ }
3954
+ @keyframes workboard-pulse {
3955
+ 0% { opacity: 0.15; transform: scale(0.98); }
3956
+ 50% { opacity: 0.5; transform: scale(1.08); }
3957
+ 100% { opacity: 0.15; transform: scale(0.98); }
3958
+ }
3959
+ @keyframes workboard-sprite-step {
3960
+ 0% { translate: 0 0; }
3961
+ 50% { translate: 0 -1px; }
3962
+ 100% { translate: 0 0; }
3963
+ }
3964
+ @keyframes workboard-world-scan {
3965
+ 0% { transform: translateY(-16px); opacity: 0.08; }
3966
+ 50% { opacity: 0.18; }
3967
+ 100% { transform: translateY(16px); opacity: 0.08; }
3968
+ }
3969
+ @keyframes workboard-portal-left {
3970
+ 0% { transform: scaleX(0); }
3971
+ 35% { transform: scaleX(1); }
3972
+ 70% { transform: scaleX(1); }
3973
+ 100% { transform: scaleX(0); }
3974
+ }
3975
+ @keyframes workboard-portal-right {
3976
+ 0% { transform: scaleX(0); }
3977
+ 35% { transform: scaleX(1); }
3978
+ 70% { transform: scaleX(1); }
3979
+ 100% { transform: scaleX(0); }
3980
+ }
3981
+ @keyframes workboard-portal-label {
3982
+ 0% { opacity: 0; transform: translateY(6px) scale(0.96); }
3983
+ 28% { opacity: 1; transform: translateY(0) scale(1); }
3984
+ 74% { opacity: 1; transform: translateY(0) scale(1); }
3985
+ 100% { opacity: 0; transform: translateY(-6px) scale(0.98); }
3986
+ }
3987
+ .workboard-portal-left { animation: workboard-portal-left 560ms steps(7, end) both; }
3988
+ .workboard-portal-right { animation: workboard-portal-right 560ms steps(7, end) both; }
3989
+ .workboard-portal-label { animation: workboard-portal-label 560ms steps(6, end) both; }
3990
+ @media (prefers-reduced-motion: reduce) {
3991
+ .workboard-portal-left,
3992
+ .workboard-portal-right,
3993
+ .workboard-portal-label {
3994
+ animation-duration: 1ms;
3995
+ }
3996
+ }
3997
+ ` });
3998
+ }
3999
+ function resolveAdjacentZoneId(params) {
4000
+ if (params.zones.length === 0) return params.activeZoneId;
4001
+ const currentIndex = Math.max(
4002
+ 0,
4003
+ params.zones.findIndex((zone) => zone.id === params.activeZoneId)
4004
+ );
4005
+ const nextIndex = (currentIndex + params.direction + params.zones.length) % params.zones.length;
4006
+ return params.zones[nextIndex]?.id || params.activeZoneId;
4007
+ }
4008
+ function Workboard(props) {
4009
+ const { board, loading, selectedAgentId, onRefresh, onSelectAgent, className } = props;
4010
+ const selected = resolveSelectedAgent({ board, selectedAgentId });
4011
+ const containerRef = React7.useRef(null);
4012
+ const stageViewportRef = React7.useRef(null);
4013
+ const [stageLevel, setStageLevel] = React7.useState("clusters");
4014
+ const [detailsCollapsed, setDetailsCollapsed] = React7.useState(true);
4015
+ const [flowMode, setFlowMode] = React7.useState("cruise");
4016
+ const [isFullscreen, setIsFullscreen] = React7.useState(false);
4017
+ const [portal, setPortal] = React7.useState(null);
4018
+ const previousSelectedIdRef = React7.useRef(selected?.id);
4019
+ const [activeZoneId, setActiveZoneId] = React7.useState(
4020
+ () => selected ? resolveZoneId(selected) : "engaged"
4021
+ );
4022
+ React7.useEffect(() => {
4023
+ if (!selected?.id) {
4024
+ previousSelectedIdRef.current = void 0;
4025
+ return;
4026
+ }
4027
+ if (selected.id === previousSelectedIdRef.current) return;
4028
+ previousSelectedIdRef.current = selected.id;
4029
+ setActiveZoneId(resolveZoneId(selected));
4030
+ }, [selected]);
4031
+ React7.useEffect(() => {
4032
+ if (!portal) return void 0;
4033
+ const timeout = window.setTimeout(() => setPortal(null), 620);
4034
+ return () => window.clearTimeout(timeout);
4035
+ }, [portal]);
4036
+ React7.useEffect(() => {
4037
+ const onFullscreenChange = () => {
4038
+ setIsFullscreen(document.fullscreenElement === containerRef.current);
4039
+ };
4040
+ document.addEventListener("fullscreenchange", onFullscreenChange);
4041
+ return () => document.removeEventListener("fullscreenchange", onFullscreenChange);
4042
+ }, []);
4043
+ const activeZone = resolveZoneDefinition(activeZoneId);
4044
+ const activeZoneItems = React7.useMemo(
4045
+ () => (board?.agents || []).filter((item) => resolveZoneId(item) === activeZoneId),
4046
+ [activeZoneId, board]
4047
+ );
4048
+ const selectedPeers = React7.useMemo(
4049
+ () => (board?.agents || []).filter((item) => resolveZoneId(item) === activeZoneId),
4050
+ [activeZoneId, board]
4051
+ );
4052
+ const gameMap = React7.useMemo(
4053
+ () => board ? buildWorkboardGameMapConfig({
4054
+ board,
4055
+ activeZoneId,
4056
+ selectedAgentId: selected?.id
4057
+ }) : null,
4058
+ [activeZoneId, board, selected?.id]
4059
+ );
4060
+ const motionNodes = React7.useMemo(() => {
4061
+ if (!gameMap) return [];
4062
+ if (stageLevel === "clusters") {
4063
+ return gameMap.actors.map((actor, index) => {
4064
+ const route = gameMap.corridors.find((item) => item.id === `corridor-${actor.id}`);
4065
+ return {
4066
+ id: actor.id,
4067
+ anchor: actor.overviewAnchor,
4068
+ swayX: 6 + index % 3 * 2.5,
4069
+ swayY: 4 + index % 4 * 1.7,
4070
+ phase: index * 0.9,
4071
+ speed: 0.8 + index % 5 * 0.08,
4072
+ mode: "route",
4073
+ route: route?.points || actor.overviewRoute,
4074
+ dwellRatio: route?.dwellRatio,
4075
+ snapSize: route?.snapSize
4076
+ };
4077
+ });
4078
+ }
4079
+ return gameMap.actors.filter((actor) => actor.zoneId === activeZoneId && actor.focusedAnchor).map((actor, index) => {
4080
+ const route = gameMap.patrols.find((item) => item.id === actor.focusedRouteId);
4081
+ const fallbackPoint = actor.focusedAnchor || {
4082
+ x: WORKBOARD_STAGE_WIDTH / 2,
4083
+ y: WORKBOARD_STAGE_HEIGHT / 2
4084
+ };
4085
+ return {
4086
+ id: actor.id,
4087
+ anchor: fallbackPoint,
4088
+ swayX: 9 + index % 3 * 2.2,
4089
+ swayY: 6 + index % 4 * 1.6,
4090
+ phase: index * 0.82,
4091
+ speed: 0.95 + index % 5 * 0.1,
4092
+ mode: "route",
4093
+ route: route?.points || gameMap.patrols[index % Math.max(gameMap.patrols.length, 1)]?.points || [fallbackPoint],
4094
+ dwellRatio: route?.dwellRatio,
4095
+ snapSize: route?.snapSize
4096
+ };
4097
+ });
4098
+ }, [activeZoneId, gameMap, stageLevel]);
4099
+ const motionFrames = useWorkboardMotion({ nodes: motionNodes, flowMode });
4100
+ const stageScale = useWorkboardStageViewportScale({
4101
+ viewportRef: stageViewportRef,
4102
+ isFullscreen
4103
+ });
4104
+ const triggerPortal = React7.useCallback((label, mode) => {
4105
+ setPortal((prev) => ({
4106
+ key: (prev?.key || 0) + 1,
4107
+ label,
4108
+ mode
4109
+ }));
4110
+ }, []);
4111
+ const openZone = React7.useCallback(
4112
+ (zoneId) => {
4113
+ triggerPortal(resolveZoneDefinition(zoneId).title, "enter");
4114
+ setActiveZoneId(zoneId);
4115
+ setStageLevel("agents");
4116
+ setDetailsCollapsed(true);
4117
+ const lead = resolveZoneLead({ board, zoneId });
4118
+ if (lead) onSelectAgent?.(lead.id);
4119
+ },
4120
+ [board, onSelectAgent, triggerPortal]
4121
+ );
4122
+ const openAgent = React7.useCallback(
4123
+ (agentId) => {
4124
+ const item = (board?.agents || []).find((entry) => entry.id === agentId);
4125
+ if (!item) return;
4126
+ const nextZoneId = resolveZoneId(item);
4127
+ if (stageLevel === "clusters" || nextZoneId !== activeZoneId) {
4128
+ triggerPortal(resolveZoneDefinition(nextZoneId).title, "enter");
4129
+ }
4130
+ setActiveZoneId(nextZoneId);
4131
+ setStageLevel("agents");
4132
+ setDetailsCollapsed(true);
4133
+ onSelectAgent?.(agentId);
4134
+ },
4135
+ [activeZoneId, board, onSelectAgent, stageLevel, triggerPortal]
4136
+ );
4137
+ const backToAtlas = React7.useCallback(() => {
4138
+ triggerPortal("World Atlas", "world");
4139
+ setStageLevel("clusters");
4140
+ }, [triggerPortal]);
4141
+ const handleWorldKeyDown = React7.useCallback(
4142
+ (event) => {
4143
+ if (!gameMap) return;
4144
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
4145
+ event.preventDefault();
4146
+ const nextZoneId = resolveAdjacentZoneId({
4147
+ zones: gameMap.zones,
4148
+ activeZoneId,
4149
+ direction: event.key === "ArrowRight" ? 1 : -1
4150
+ });
4151
+ setActiveZoneId(nextZoneId);
4152
+ return;
4153
+ }
4154
+ if (event.key === "Enter" && stageLevel === "clusters") {
4155
+ event.preventDefault();
4156
+ openZone(activeZoneId);
4157
+ return;
4158
+ }
4159
+ if (event.key === "Escape" && stageLevel === "agents") {
4160
+ event.preventDefault();
4161
+ backToAtlas();
4162
+ }
4163
+ },
4164
+ [activeZoneId, backToAtlas, gameMap, openZone, stageLevel]
4165
+ );
4166
+ const toggleFullscreen = React7.useCallback(async () => {
4167
+ const container = containerRef.current;
4168
+ if (!container) return;
4169
+ if (document.fullscreenElement === container) {
4170
+ await document.exitFullscreen();
4171
+ return;
4172
+ }
4173
+ if (document.fullscreenElement) await document.exitFullscreen();
4174
+ await container.requestFullscreen();
4175
+ }, []);
4176
+ if (!board || !gameMap) {
4177
+ return /* @__PURE__ */ jsx25(
4178
+ "div",
4179
+ {
4180
+ className: cn(
4181
+ "grid min-h-72 place-items-center border-2 border-dashed border-border/70 bg-[linear-gradient(145deg,rgba(247,244,236,0.84),rgba(255,255,255,0.95))] text-sm text-muted-foreground",
4182
+ className
4183
+ ),
4184
+ style: { clipPath: PIXEL_PANEL_CLIP4 },
4185
+ children: loading ? "\u6B63\u5728\u751F\u6210 workboard game world..." : "\u5F53\u524D\u6CA1\u6709\u53EF\u5C55\u793A\u7684 workboard game world\u3002"
4186
+ }
4187
+ );
4188
+ }
4189
+ return /* @__PURE__ */ jsxs12("section", { className: cn("min-h-full", className), children: [
4190
+ /* @__PURE__ */ jsx25(WorkboardGameStyles, {}),
4191
+ /* @__PURE__ */ jsxs12(
4192
+ "section",
4193
+ {
4194
+ ref: containerRef,
4195
+ tabIndex: 0,
4196
+ onKeyDown: handleWorldKeyDown,
4197
+ className: cn(
4198
+ "relative overflow-hidden bg-[linear-gradient(145deg,rgba(236,232,218,0.96),rgba(255,252,247,0.98)_42%,rgba(217,231,224,0.76))] outline-none focus-visible:ring-2 focus-visible:ring-foreground/30",
4199
+ isFullscreen ? "h-[100dvh] rounded-none" : "border-2 border-border/70 p-2 shadow-[0_8px_0_rgba(17,17,19,0.12)]"
4200
+ ),
4201
+ style: isFullscreen ? void 0 : { clipPath: PIXEL_PANEL_CLIP4 },
4202
+ children: [
4203
+ /* @__PURE__ */ jsx25("div", { className: "pointer-events-none absolute inset-0 z-10 bg-[linear-gradient(rgba(17,17,19,0.04)_1px,transparent_1px),linear-gradient(90deg,rgba(17,17,19,0.04)_1px,transparent_1px)] bg-[size:18px_18px] opacity-40" }),
4204
+ /* @__PURE__ */ jsx25(
4205
+ "div",
4206
+ {
4207
+ className: "pointer-events-none absolute inset-x-0 top-0 z-10 h-16 bg-[linear-gradient(180deg,transparent,rgba(17,17,19,0.08),transparent)]",
4208
+ style: { animation: "workboard-world-scan 4.5s steps(6, end) infinite" }
4209
+ }
4210
+ ),
4211
+ /* @__PURE__ */ jsx25(
4212
+ WorkboardGameHud,
4213
+ {
4214
+ board,
4215
+ stageLevel,
4216
+ activeZone,
4217
+ selected,
4218
+ flowMode,
4219
+ loading,
4220
+ isFullscreen,
4221
+ zones: gameMap.zones,
4222
+ onSelectZone: openZone,
4223
+ onBackToAtlas: backToAtlas,
4224
+ onToggleFlowMode: () => setFlowMode((prev) => prev === "cruise" ? "turbo" : "cruise"),
4225
+ onRefresh,
4226
+ onToggleFullscreen: toggleFullscreen
4227
+ }
4228
+ ),
4229
+ /* @__PURE__ */ jsxs12(
4230
+ "div",
4231
+ {
4232
+ ref: stageViewportRef,
4233
+ className: cn(
4234
+ "z-20 grid w-full place-items-center",
4235
+ isFullscreen ? "absolute inset-0 overflow-hidden" : "relative overflow-auto border-2 border-border/70 bg-[linear-gradient(145deg,rgba(251,250,247,0.96),rgba(245,248,245,0.88))]"
4236
+ ),
4237
+ style: isFullscreen ? void 0 : { minHeight: 720, clipPath: PIXEL_PANEL_CLIP4 },
4238
+ children: [
4239
+ /* @__PURE__ */ jsx25(WorkboardScaledStage, { scale: stageScale, children: stageLevel === "clusters" ? /* @__PURE__ */ jsx25(
4240
+ WorkboardGameAtlas,
4241
+ {
4242
+ board,
4243
+ gameMap,
4244
+ activeZoneId,
4245
+ selectedAgentId: selected?.id,
4246
+ flowMode,
4247
+ motionFrames,
4248
+ onSelectZone: openZone,
4249
+ onSelectAgent: (agentId) => openAgent(agentId)
4250
+ }
4251
+ ) : /* @__PURE__ */ jsx25(
4252
+ WorkboardGameRoom,
4253
+ {
4254
+ zone: activeZone,
4255
+ items: activeZoneItems,
4256
+ gameMap,
4257
+ selectedAgentId: selected?.id,
4258
+ motionFrames,
4259
+ flowMode,
4260
+ onSelectAgent: openAgent
4261
+ }
4262
+ ) }),
4263
+ /* @__PURE__ */ jsx25(WorkboardWorldPortalOverlay, { portal }),
4264
+ /* @__PURE__ */ jsx25(
4265
+ WorkboardGameInspector,
4266
+ {
4267
+ selected,
4268
+ activeZone,
4269
+ selectedPeers,
4270
+ stageLevel,
4271
+ collapsed: detailsCollapsed,
4272
+ onToggleCollapsed: () => setDetailsCollapsed((prev) => !prev),
4273
+ onSelectAgent: openAgent
4274
+ }
4275
+ )
4276
+ ]
4277
+ }
4278
+ )
4279
+ ]
4280
+ }
4281
+ )
4282
+ ] });
4283
+ }
497
4284
  export {
498
4285
  Badge,
499
4286
  Button,
@@ -504,6 +4291,17 @@ export {
504
4291
  CardFooter,
505
4292
  CardHeader,
506
4293
  CardTitle,
4294
+ Checkbox,
4295
+ Dialog,
4296
+ DialogClose,
4297
+ DialogContent,
4298
+ DialogDescription,
4299
+ DialogFooter,
4300
+ DialogHeader,
4301
+ DialogOverlay,
4302
+ DialogPortal,
4303
+ DialogTitle,
4304
+ DialogTrigger,
507
4305
  DropdownMenu,
508
4306
  DropdownMenuCheckboxItem,
509
4307
  DropdownMenuContent,
@@ -519,11 +4317,41 @@ export {
519
4317
  DropdownMenuSubContent,
520
4318
  DropdownMenuSubTrigger,
521
4319
  DropdownMenuTrigger,
4320
+ Input,
4321
+ Label,
522
4322
  Popover,
523
4323
  PopoverContent,
524
4324
  PopoverTrigger,
4325
+ Separator,
4326
+ Sheet,
4327
+ SheetClose,
4328
+ SheetContent,
4329
+ SheetDescription,
4330
+ SheetFooter,
4331
+ SheetHeader,
4332
+ SheetOverlay,
4333
+ SheetPortal,
4334
+ SheetTitle,
4335
+ SheetTrigger,
4336
+ Skeleton,
4337
+ Tabs,
4338
+ TabsContent,
4339
+ TabsList,
4340
+ TabsTrigger,
4341
+ Textarea,
525
4342
  Toaster,
4343
+ Toggle,
4344
+ ToggleGroup,
4345
+ ToggleGroupItem,
4346
+ Tooltip,
4347
+ TooltipContent,
4348
+ TooltipProvider,
4349
+ TooltipTrigger,
4350
+ Workboard,
526
4351
  badgeVariants,
4352
+ buildWorkboardGameMapConfig,
527
4353
  buttonVariants,
528
- cn
4354
+ cn,
4355
+ tabsListVariants,
4356
+ toggleVariants
529
4357
  };