react-os-shell 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +242 -0
  3. package/dist/Calculator-BNBRNV4P.js +184 -0
  4. package/dist/Calculator-BNBRNV4P.js.map +1 -0
  5. package/dist/Calendar-5EYUVGUU.js +423 -0
  6. package/dist/Calendar-5EYUVGUU.js.map +1 -0
  7. package/dist/Checkers-MIAHIKJH.js +214 -0
  8. package/dist/Checkers-MIAHIKJH.js.map +1 -0
  9. package/dist/Chess-C5BY45NA.js +190 -0
  10. package/dist/Chess-C5BY45NA.js.map +1 -0
  11. package/dist/ConfirmDialog-ZP4AHVUD.js +3 -0
  12. package/dist/ConfirmDialog-ZP4AHVUD.js.map +1 -0
  13. package/dist/CurrencyConverter-TYPU2IRF.js +223 -0
  14. package/dist/CurrencyConverter-TYPU2IRF.js.map +1 -0
  15. package/dist/Email-JEYYJ3YV.js +1835 -0
  16. package/dist/Email-JEYYJ3YV.js.map +1 -0
  17. package/dist/Game2048-3RH3ELRD.js +191 -0
  18. package/dist/Game2048-3RH3ELRD.js.map +1 -0
  19. package/dist/GeminiChat-BXLBJFT4.js +184 -0
  20. package/dist/GeminiChat-BXLBJFT4.js.map +1 -0
  21. package/dist/Minesweeper-VQGLAZON.js +270 -0
  22. package/dist/Minesweeper-VQGLAZON.js.map +1 -0
  23. package/dist/Notepad-YTZRCAXX.js +389 -0
  24. package/dist/Notepad-YTZRCAXX.js.map +1 -0
  25. package/dist/PomodoroTimer-HARIJN4S.js +196 -0
  26. package/dist/PomodoroTimer-HARIJN4S.js.map +1 -0
  27. package/dist/Spreadsheet-IOKEDNS6.js +446 -0
  28. package/dist/Spreadsheet-IOKEDNS6.js.map +1 -0
  29. package/dist/Sudoku-XHLYCEVT.js +197 -0
  30. package/dist/Sudoku-XHLYCEVT.js.map +1 -0
  31. package/dist/Tetris-ZHCZYL24.js +243 -0
  32. package/dist/Tetris-ZHCZYL24.js.map +1 -0
  33. package/dist/Weather-ROZ7TRNW.js +310 -0
  34. package/dist/Weather-ROZ7TRNW.js.map +1 -0
  35. package/dist/apps/index.d.ts +55 -0
  36. package/dist/apps/index.js +48 -0
  37. package/dist/apps/index.js.map +1 -0
  38. package/dist/chunk-5O2KEISQ.js +155 -0
  39. package/dist/chunk-5O2KEISQ.js.map +1 -0
  40. package/dist/chunk-D7PYW2QS.js +265 -0
  41. package/dist/chunk-D7PYW2QS.js.map +1 -0
  42. package/dist/chunk-GP4Y3VCB.js +806 -0
  43. package/dist/chunk-GP4Y3VCB.js.map +1 -0
  44. package/dist/chunk-NSU7OHPC.js +39 -0
  45. package/dist/chunk-NSU7OHPC.js.map +1 -0
  46. package/dist/chunk-PDFQNHW7.js +24 -0
  47. package/dist/chunk-PDFQNHW7.js.map +1 -0
  48. package/dist/chunk-RFTLYCSF.js +144 -0
  49. package/dist/chunk-RFTLYCSF.js.map +1 -0
  50. package/dist/chunk-SVBID2P6.js +142 -0
  51. package/dist/chunk-SVBID2P6.js.map +1 -0
  52. package/dist/chunk-TFGOLXGD.js +41 -0
  53. package/dist/chunk-TFGOLXGD.js.map +1 -0
  54. package/dist/chunk-WIJ45SYD.js +120 -0
  55. package/dist/chunk-WIJ45SYD.js.map +1 -0
  56. package/dist/chunk-WQIS72NL.js +1470 -0
  57. package/dist/chunk-WQIS72NL.js.map +1 -0
  58. package/dist/index.d.ts +642 -0
  59. package/dist/index.js +3443 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/sounds-NT4DEZGD.js +3 -0
  62. package/dist/sounds-NT4DEZGD.js.map +1 -0
  63. package/dist/styles.css +174 -0
  64. package/dist/types-CFIZ1_xt.d.ts +67 -0
  65. package/package.json +76 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/apps/Spreadsheet.tsx"],"names":[],"mappings":";;;;;;AAKA,IAAM,iBAAA,GAAoB,EAAA;AAC1B,SAAS,iBAAiB,CAAA,EAAW;AACnC,EAAA,OAAO,CAAA,CAAE,MAAA,GAAS,iBAAA,GAAoB,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,EAAG,iBAAA,GAAoB,CAAC,CAAC,CAAA,MAAA,CAAA,GAAM,CAAA;AAClF;AAEA,IAAM,KAAA,GAAQ,4BAAA;AACd,IAAM,YAAA,GAAe,EAAA;AACrB,IAAM,YAAA,GAAe,EAAA;AAErB,SAAS,SAAS,CAAA,EAAmB;AACnC,EAAA,IAAI,CAAA,GAAI,EAAA,EAAI,OAAO,KAAA,CAAM,CAAC,CAAA;AAC1B,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,EAAE,IAAI,CAAC,CAAA,GAAI,KAAA,CAAM,CAAA,GAAI,EAAE,CAAA;AACrD;AAEA,SAAS,YAAY,KAAA,EAA6B;AAChD,EAAA,OAAO,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,OAAM,EAAG,CAAC,GAAG,CAAA,MAAO;AAAA,IAC9C,GAAA,EAAK,OAAO,CAAC,CAAA,CAAA;AAAA,IACb,KAAA,EAAO,SAAS,CAAC,CAAA;AAAA,IACjB,KAAA,EAAO;AAAA,GACT,CAAE,CAAA;AACJ;AAEA,SAAS,aAAA,CAAc,MAAc,IAAA,EAA0B;AAC7D,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,IAAA,EAAK,EAAG,MAAM,KAAA,CAAM,IAAI,CAAA,CAAE,IAAA,CAAK,EAAE,CAAC,CAAA;AAChE;AAWA,SAAS,SAAS,IAAA,EAAqB;AACrC,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,OAAO,UAAA,EAAW;AAAA,IACtB,IAAA;AAAA,IACA,OAAA,EAAS,YAAY,YAAY,CAAA;AAAA,IACjC,IAAA,EAAM,aAAA,CAAc,YAAA,EAAc,YAAY,CAAA;AAAA,IAC9C,YAAY;AAAC,GACf;AACF;AAEA,SAAS,SAAS,IAAA,EAA0B;AAC1C,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,CAAE,IAAI,CAAA,IAAA,KAAQ;AAClC,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAI,CAAA,EAAG,OAAO,IAAA,CAAK,KAAA,CAAM,GAAI,CAAA,CAAE,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM,CAAA;AAClE,IAAA,MAAM,QAAkB,EAAC;AACzB,IAAA,IAAI,OAAA,GAAU,EAAA;AACd,IAAA,IAAI,QAAA,GAAW,KAAA;AACf,IAAA,KAAA,MAAW,MAAM,IAAA,EAAM;AACrB,MAAA,IAAI,OAAO,GAAA,EAAK;AAAE,QAAA,QAAA,GAAW,CAAC,QAAA;AAAU,QAAA;AAAA,MAAU;AAClD,MAAA,IAAI,EAAA,KAAO,GAAA,IAAO,CAAC,QAAA,EAAU;AAAE,QAAA,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,CAAA;AAAG,QAAA,OAAA,GAAU,EAAA;AAAI,QAAA;AAAA,MAAU;AACnF,MAAA,OAAA,IAAW,EAAA;AAAA,IACb;AACA,IAAA,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,CAAA;AACzB,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,CAAE,KAAK,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA;AACtC;AAEe,SAAR,WAAA,GAA+B;AACpC,EAAA,MAAM,YAAA,GAAe,OAAuB,IAAI,CAAA;AAChD,EAAA,MAAM,CAAC,QAAQ,SAAS,CAAA,GAAI,SAAkB,CAAC,QAAA,CAAS,SAAS,CAAC,CAAC,CAAA;AACnE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,CAAC,CAAA;AAI5C,EAAA,MAAM,YAAA,GAAe,MAAA,CAAkB,EAAE,CAAA;AACzC,EAAA,MAAM,gBAAA,GAAmB,OAAgB,MAAM,CAAA;AAC/C,EAAA,MAAM,aAAA,GAAgB,OAAO,KAAK,CAAA;AAClC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,cAAc,OAAA,EAAS;AACzB,MAAA,aAAA,CAAc,OAAA,GAAU,KAAA;AAAA,IAC1B,CAAA,MAAA,IAAW,gBAAA,CAAiB,OAAA,KAAY,MAAA,EAAQ;AAC9C,MAAA,YAAA,CAAa,OAAA,CAAQ,IAAA,CAAK,gBAAA,CAAiB,OAAO,CAAA;AAClD,MAAA,IAAI,aAAa,OAAA,CAAQ,MAAA,GAAS,EAAA,EAAI,YAAA,CAAa,QAAQ,KAAA,EAAM;AAAA,IACnE;AACA,IAAA,gBAAA,CAAiB,OAAA,GAAU,MAAA;AAAA,EAC7B,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AACX,EAAA,MAAM,IAAA,GAAO,YAAY,MAAM;AAC7B,IAAA,MAAM,IAAA,GAAO,YAAA,CAAa,OAAA,CAAQ,GAAA,EAAI;AACtC,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,aAAA,CAAc,OAAA,GAAU,IAAA;AACxB,IAAA,SAAA,CAAU,IAAI,CAAA;AAAA,EAChB,CAAA,EAAG,EAAE,CAAA;AACL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAqB;AACpC,MAAA,IAAI,CAAC,YAAA,CAAa,OAAA,EAAS,QAAA,CAAS,QAAA,CAAS,aAAa,CAAA,IAAK,CAAC,YAAA,CAAa,OAAA,EAAS,OAAA,CAAQ,eAAe,CAAA,EAAG;AAChH,MAAA,MAAM,MAAA,GAAA,CAAU,CAAA,CAAE,OAAA,IAAW,CAAA,CAAE,OAAA,KAAY,CAAC,CAAA,CAAE,QAAA,KAAa,CAAA,CAAE,GAAA,KAAQ,GAAA,IAAO,CAAA,CAAE,GAAA,KAAQ,GAAA,CAAA;AACtF,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,CAAA,CAAE,cAAA,EAAe;AACjB,QAAA,IAAA,EAAK;AAAA,MACP;AAAA,IACF,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,OAAO,CAAA;AAC1C,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,EAC5D,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AACT,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,UAAU,CAAA;AAC7C,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAI,SAAwB,IAAI,CAAA;AAChE,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,EAAE,CAAA;AACzC,EAAA,MAAM,OAAA,GAAU,OAAyB,IAAI,CAAA;AAC7C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,GAAG,CAAA;AAClD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,IAAI,CAAA;AAEnD,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAS,CAAA,IAAK,OAAO,CAAC,CAAA;AAC5C,EAAA,MAAM,OAAO,MAAA,CAAO,IAAA;AACpB,EAAA,MAAM,UAAU,MAAA,CAAO,OAAA;AACvB,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,UAAA,IAAc,EAAC;AACzC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAA8C,IAAI,CAAA;AACxF,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAA6F,IAAI,CAAA;AAInI,EAAA,MAAM,WAAA,GAAc,YAAY,MAAsC;AACpE,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,SAAA,CAAU,OAAO,GAAA,EAAK,SAAA,CAAU,IAAI,GAAG,CAAA;AAC3D,MAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,SAAA,CAAU,OAAO,GAAA,EAAK,SAAA,CAAU,IAAI,GAAG,CAAA;AAC3D,MAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,SAAA,CAAU,OAAO,GAAA,EAAK,SAAA,CAAU,IAAI,GAAG,CAAA;AAC3D,MAAA,MAAM,EAAA,GAAK,KAAK,GAAA,CAAI,SAAA,CAAU,OAAO,GAAA,EAAK,SAAA,CAAU,IAAI,GAAG,CAAA;AAC3D,MAAA,MAAM,QAAwC,EAAC;AAC/C,MAAA,KAAA,IAAS,IAAI,EAAA,EAAI,CAAA,IAAK,IAAI,CAAA,EAAA,EAAK,KAAA,IAAS,IAAI,EAAA,EAAI,CAAA,IAAK,EAAA,EAAI,CAAA,EAAA,QAAW,IAAA,CAAK,EAAE,KAAK,CAAA,EAAG,GAAA,EAAK,GAAG,CAAA;AAC3F,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAO,WAAA,GAAc,CAAC,WAAW,CAAA,GAAI,EAAC;AAAA,EACxC,CAAA,EAAG,CAAC,SAAA,EAAW,WAAW,CAAC,CAAA;AAE3B,EAAA,MAAM,eAAA,GAAkB,WAAA,CAAY,CAAC,GAAA,EAAsB,KAAA,KAAgB;AACzE,IAAA,MAAM,QAAQ,WAAA,EAAY;AAC1B,IAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AACxB,IAAA,SAAA,CAAU,CAAA,IAAA,KAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AACnC,MAAA,IAAI,CAAA,KAAM,WAAW,OAAO,CAAA;AAC5B,MAAA,MAAM,SAAS,EAAE,GAAI,CAAA,CAAE,UAAA,IAAc,EAAC,EAAG;AAIzC,MAAA,MAAM,QAAA,GAAW,CAAA,EAAG,KAAA,CAAM,CAAC,CAAA,CAAE,GAAG,CAAA,CAAA,EAAI,KAAA,CAAM,CAAC,CAAA,CAAE,GAAG,CAAA,CAAA;AAChD,MAAA,MAAM,OAAA,GAAU,KAAA,KAAU,MAAA,GAAY,KAAA,GAAQ,CAAA,CAAG,OAAO,QAAQ,CAAA,IAAK,EAAC,EAAG,GAAG,CAAA;AAC5E,MAAA,KAAA,MAAW,EAAE,GAAA,EAAK,GAAA,EAAI,IAAK,KAAA,EAAO;AAChC,QAAA,MAAM,CAAA,GAAI,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA;AACvB,QAAA,MAAA,CAAO,CAAC,CAAA,GAAI,EAAE,GAAI,MAAA,CAAO,CAAC,CAAA,IAAK,EAAC,EAAI,CAAC,GAAG,GAAG,OAAA,EAAQ;AAAA,MACrD;AACA,MAAA,OAAO,EAAE,GAAG,CAAA,EAAG,UAAA,EAAY,MAAA,EAAO;AAAA,IACpC,CAAC,CAAC,CAAA;AAAA,EACJ,CAAA,EAAG,CAAC,WAAA,EAAa,SAAS,CAAC,CAAA;AAE3B,EAAA,MAAM,QAAA,GAAW,SAAA,GAAY,EAAE,GAAA,EAAK,SAAA,CAAU,MAAA,CAAO,GAAA,EAAK,GAAA,EAAK,SAAA,CAAU,MAAA,CAAO,GAAA,EAAI,GAAI,WAAA;AACxF,EAAA,MAAM,YAAA,GAA0B,QAAA,GAAY,UAAA,CAAW,CAAA,EAAG,QAAA,CAAS,GAAG,CAAA,CAAA,EAAI,QAAA,CAAS,GAAG,CAAA,CAAE,CAAA,IAAK,KAAM,EAAC;AACpG,EAAA,MAAM,SAAA,GAAY,CAAC,CAAC,QAAA;AAEpB,EAAA,MAAM,iBAAA,GAAoB,WAAA,CAAY,CAAC,MAAA,KAA2B;AAChE,IAAA,SAAA,CAAU,CAAA,IAAA,KAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM,CAAA,KAAM,SAAA,GAAY,EAAE,GAAG,CAAA,EAAG,GAAG,MAAA,EAAO,GAAI,CAAC,CAAC,CAAA;AAAA,EACjF,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,YAAA,GAAe,WAAA,CAAY,CAAC,OAAA,KAAwB;AAExD,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,EAAG,CAAC,CAAA;AACjE,IAAA,IAAI,OAAA,KAAY,QAAQ,MAAA,EAAQ;AAC9B,MAAA,iBAAA,CAAkB,EAAE,IAAA,EAAM,OAAA,EAAS,SAAS,WAAA,CAAY,OAAO,GAAG,CAAA;AAAA,IACpE,CAAA,MAAO;AACL,MAAA,iBAAA,CAAkB,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,IACrC;AAAA,EACF,CAAA,EAAG,CAAC,iBAAA,EAAmB,OAAA,CAAQ,MAAM,CAAC,CAAA;AAGtC,EAAA,MAAM,WAAW,MAAM;AACrB,IAAA,MAAM,IAAA,GAAO,CAAA,MAAA,EAAS,MAAA,CAAO,MAAA,GAAS,CAAC,CAAA,CAAA;AACvC,IAAA,SAAA,CAAU,UAAQ,CAAC,GAAG,MAAM,QAAA,CAAS,IAAI,CAAC,CAAC,CAAA;AAC3C,IAAA,YAAA,CAAa,OAAO,MAAM,CAAA;AAAA,EAC5B,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,GAAA,KAAgB;AACnC,IAAA,IAAI,MAAA,CAAO,UAAU,CAAA,EAAG;AACxB,IAAA,SAAA,CAAU,CAAA,IAAA,KAAQ,KAAK,MAAA,CAAO,CAAC,GAAG,CAAA,KAAM,CAAA,KAAM,GAAG,CAAC,CAAA;AAClD,IAAA,IAAI,aAAa,GAAA,IAAO,SAAA,GAAY,CAAA,EAAG,YAAA,CAAa,YAAY,CAAC,CAAA;AAAA,EACnE,CAAA;AAEA,EAAA,MAAM,WAAA,GAAc,CAAC,GAAA,EAAa,IAAA,KAAiB;AACjD,IAAA,SAAA,CAAU,CAAA,IAAA,KAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM,CAAA,KAAM,GAAA,GAAM,EAAE,GAAG,CAAA,EAAG,IAAA,EAAK,GAAI,CAAC,CAAC,CAAA;AACpE,IAAA,aAAA,CAAc,IAAI,CAAA;AAAA,EACpB,CAAA;AAGA,EAAA,MAAM,UAAA,GAAa,CAAC,KAAA,KAAkB;AACpC,IAAA,MAAM,WAAA,GAAc,QAAQ,MAAA,GAAS,KAAA;AACrC,IAAA,iBAAA,CAAkB;AAAA,MAChB,OAAA,EAAS,YAAY,WAAW,CAAA;AAAA,MAChC,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,CAAA,GAAA,KAAO,CAAC,GAAG,GAAA,EAAK,GAAG,KAAA,CAAM,KAAK,CAAA,CAAE,IAAA,CAAK,EAAE,CAAC,CAAC;AAAA,KACzD,CAAA;AAAA,EACH,CAAA;AAGA,EAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAkB;AACjC,IAAA,iBAAA,CAAkB;AAAA,MAChB,MAAM,CAAC,GAAG,MAAM,GAAG,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,OAAM,EAAG,MAAM,MAAM,OAAA,CAAQ,MAAM,EAAE,IAAA,CAAK,EAAE,CAAC,CAAC;AAAA,KACvF,CAAA;AAAA,EACH,CAAA;AAGA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,iBAAA,CAAkB;AAAA,MAChB,OAAA,EAAS,YAAY,YAAY,CAAA;AAAA,MACjC,IAAA,EAAM,aAAA,CAAc,YAAA,EAAc,YAAY;AAAA,KAC/C,CAAA;AAAA,EACH,CAAA;AAGA,EAAA,MAAM,YAAY,MAAM;AACtB,IAAA,MAAM,MAAM,IAAA,CACT,MAAA,CAAO,CAAA,GAAA,KAAO,GAAA,CAAI,KAAK,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,EAAM,CAAC,CAAA,CACrC,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAI,CAAA,IAAA,KAAQ;AAC1B,MAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,IAAK,IAAA,CAAK,SAAS,GAAG,CAAA,IAAK,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA;AAChE,QAAA,OAAO,CAAA,CAAA,EAAI,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAM,IAAI,CAAC,CAAA,CAAA,CAAA;AACrC,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA,CACX,KAAK,IAAI,CAAA;AACZ,IAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,UAAA,EAAY,CAAA;AACjD,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,eAAA,CAAgB,IAAI,CAAA;AACpC,IAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,GAAG,CAAA;AACpC,IAAA,CAAA,CAAE,IAAA,GAAO,GAAA;AACT,IAAA,CAAA,CAAE,QAAA,GAAW,CAAA,EAAG,KAAA,IAAS,aAAa,CAAA,IAAA,CAAA;AACtC,IAAA,CAAA,CAAE,KAAA,EAAM;AACR,IAAA,GAAA,CAAI,gBAAgB,GAAG,CAAA;AAAA,EACzB,CAAA;AAGA,EAAA,MAAM,UAAA,GAAa,OAAO,CAAA,KAA2C;AACnE,IAAA,MAAM,IAAA,GAAO,CAAA,CAAE,MAAA,CAAO,KAAA,GAAQ,CAAC,CAAA;AAC/B,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,kCAAkC,EAAE,CAAA;AAEnE,IAAA,IAAI,oBAAA,CAAqB,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA,EAAG;AAExC,MAAA,MAAM,UAAU,IAAI,UAAA,CAAW,MAAM,IAAA,CAAK,aAAa,CAAA;AACvD,MAAA,MAAM,IAAA,GAAO,MAAM,OAAO,MAAM,CAAA;AAChC,MAAA,MAAM,KAAK,IAAA,CAAK,IAAA,CAAK,SAAS,EAAE,IAAA,EAAM,SAAS,CAAA;AAC/C,MAAA,MAAM,SAAA,GAAqB,EAAA,CAAG,UAAA,CAAW,GAAA,CAAI,CAAA,EAAA,KAAM;AACjD,QAAA,MAAM,IAAA,GAAmB,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,EAAA,CAAG,MAAA,CAAO,EAAE,CAAA,EAAG,EAAE,MAAA,EAAQ,CAAA,EAAG,MAAA,EAAQ,IAAI,CAAA;AAC1F,QAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,YAAA,EAAc,IAAA,CAAK,OAAO,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,IAAI,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,EAAG,CAAC,CAAC,CAAA;AACtF,QAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,CAAA,KAAK;AAAE,UAAA,MAAM,KAAK,CAAA,CAAE,GAAA,CAAI,OAAK,MAAA,CAAO,CAAA,IAAK,EAAE,CAAC,CAAA;AAAG,UAAA,OAAO,EAAA,CAAG,MAAA,GAAS,OAAA,EAAS,EAAA,CAAG,KAAK,EAAE,CAAA;AAAG,UAAA,OAAO,EAAA;AAAA,QAAI,CAAC,CAAA;AAC5H,QAAA,OAAO,MAAA,CAAO,MAAA,GAAS,YAAA,EAAc,MAAA,CAAO,IAAA,CAAK,MAAM,OAAO,CAAA,CAAE,IAAA,CAAK,EAAE,CAAC,CAAA;AACxE,QAAA,OAAO,EAAE,EAAA,EAAI,MAAA,CAAO,UAAA,EAAW,EAAG,IAAA,EAAM,EAAA,EAAI,OAAA,EAAS,WAAA,CAAY,OAAO,CAAA,EAAG,IAAA,EAAM,MAAA,EAAO;AAAA,MAC1F,CAAC,CAAA;AACD,MAAA,SAAA,CAAU,SAAS,CAAA;AACnB,MAAA,YAAA,CAAa,CAAC,CAAA;AACd,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf,CAAA,MAAO;AAEL,MAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,IAAA,EAAK;AAC7B,MAAA,MAAM,MAAA,GAAS,SAAS,IAAI,CAAA;AAC5B,MAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACzB,MAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,YAAA,EAAc,MAAA,CAAO,OAAO,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,IAAI,CAAA,EAAG,CAAA,CAAE,MAAM,CAAA,EAAG,CAAC,CAAC,CAAA;AACxF,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,GAAA,CAAI,CAAA,CAAA,KAAK;AAAE,QAAA,OAAO,CAAA,CAAE,MAAA,GAAS,OAAA,EAAS,CAAA,CAAE,KAAK,EAAE,CAAA;AAAG,QAAA,OAAO,CAAA;AAAA,MAAG,CAAC,CAAA;AACnF,MAAA,OAAO,MAAA,CAAO,MAAA,GAAS,YAAA,EAAc,MAAA,CAAO,IAAA,CAAK,MAAM,OAAO,CAAA,CAAE,IAAA,CAAK,EAAE,CAAC,CAAA;AACxE,MAAA,iBAAA,CAAkB,EAAE,OAAA,EAAS,WAAA,CAAY,OAAO,CAAA,EAAG,IAAA,EAAM,QAAQ,CAAA;AACjE,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf;AAEA,IAAA,IAAI,OAAA,CAAQ,OAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,KAAA,GAAQ,EAAA;AAAA,EAC/C,CAAA;AAGA,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAA,CAAK,OAAA,CAAQ,CAAA,GAAA,KAAO,GAAA,CAAI,OAAA,CAAQ,CAAA,IAAA,KAAQ;AACtC,IAAA,MAAM,CAAA,GAAI,WAAW,IAAI,CAAA;AACzB,IAAA,IAAI,CAAC,MAAM,CAAC,CAAA,IAAK,KAAK,IAAA,EAAK,EAAG,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA;AAAA,EAC9C,CAAC,CAAC,CAAA;AACF,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,MAAA,CAAO,CAAC,GAAG,GAAA,KAAQ,CAAA,GAAI,GAAA,CAAI,MAAA,CAAO,UAAQ,IAAA,CAAK,IAAA,EAAM,CAAA,CAAE,QAAQ,CAAC,CAAA;AAEzF,EAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,YAAA,EAAc,WAAU,sBAAA,EAChC,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,eAAY,KAAA,EAAO,CAAA,EAAG,iBAAiB,KAAA,IAAS,UAAU,CAAC,CAAA,eAAA,CAAA,EAAmB,CAAA;AAAA,oBAE/E,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gFAAA,EACZ,QAAA,EAAA;AAAA,MAAA,YAAA,mBACC,GAAA;AAAA,QAAC,OAAA;AAAA,QAAA;AAAA,UAAM,IAAA,EAAK,MAAA;AAAA,UAAO,KAAA,EAAO,KAAA;AAAA,UAAO,QAAA,EAAU,CAAA,CAAA,KAAK,QAAA,CAAS,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,UACrE,MAAA,EAAQ,MAAM,eAAA,CAAgB,KAAK,CAAA;AAAA,UACnC,WAAW,CAAA,CAAA,KAAK;AAAE,YAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,OAAA,EAAS,eAAA,CAAgB,KAAK,CAAA;AAAA,UAAG,CAAA;AAAA,UACjE,SAAA,EAAS,IAAA;AAAA,UACT,SAAA,EAAU;AAAA;AAAA,OAA8H,mBAE1I,GAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,eAAA,CAAgB,IAAI,CAAA,EAAG,SAAA,EAAU,8EAAA,EAA+E,KAAA,EAAM,iBAAA,EAC1I,mBAAS,UAAA,EACZ,CAAA;AAAA,sBAGF,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAA,EAAuB,CAAA;AAAA,sBAEtC,GAAA,CAAC,OAAA,EAAA,EAAM,GAAA,EAAK,OAAA,EAAS,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,gCAAA,EAAiC,QAAA,EAAU,UAAA,EAAY,SAAA,EAAU,QAAA,EAAS,CAAA;AAAA,sBAClH,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UAAO,OAAA,EAAS,MAAM,OAAA,CAAQ,OAAA,EAAS,KAAA,EAAM;AAAA,UAC5C,SAAA,EAAU,iGAAA;AAAA,UAAkG,QAAA,EAAA;AAAA;AAAA,OAE9G;AAAA,sBACA,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UAAO,OAAA,EAAS,SAAA;AAAA,UACf,SAAA,EAAU,iGAAA;AAAA,UAAkG,QAAA,EAAA;AAAA;AAAA,OAE9G;AAAA,sBAEA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAA,EAAuB,CAAA;AAAA,sBAEtC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YAAM,IAAA,EAAK,QAAA;AAAA,YAAS,GAAA,EAAI,GAAA;AAAA,YAAI,GAAA,EAAI,IAAA;AAAA,YAAK,KAAA,EAAO,WAAA;AAAA,YAAa,QAAA,EAAU,CAAA,CAAA,KAAK,cAAA,CAAe,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACpG,SAAA,EAAU;AAAA;AAAA,SAAgH;AAAA,wBAC5H,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,SAAS,MAAM,UAAA,CAAW,QAAA,CAAS,WAAW,KAAK,CAAC,CAAA;AAAA,YAC1D,SAAA,EAAU,iGAAA;AAAA,YAAkG,QAAA,EAAA;AAAA;AAAA;AAE9G,OAAA,EACF,CAAA;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YAAM,IAAA,EAAK,QAAA;AAAA,YAAS,GAAA,EAAI,GAAA;AAAA,YAAI,GAAA,EAAI,KAAA;AAAA,YAAM,KAAA,EAAO,WAAA;AAAA,YAAa,QAAA,EAAU,CAAA,CAAA,KAAK,cAAA,CAAe,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACrG,SAAA,EAAU;AAAA;AAAA,SAAgH;AAAA,wBAC5H,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,SAAS,MAAM,OAAA,CAAQ,QAAA,CAAS,WAAW,KAAK,EAAE,CAAA;AAAA,YACxD,SAAA,EAAU,iGAAA;AAAA,YAAkG,QAAA,EAAA;AAAA;AAAA;AAE9G,OAAA,EACF,CAAA;AAAA,sBAEA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAA,EAAuB,CAAA;AAAA,sBAEtC,GAAA;AAAA,QAAC,QAAA;AAAA,QAAA;AAAA,UAAO,OAAA,EAAS,WAAA;AAAA,UACf,SAAA,EAAU,iGAAA;AAAA,UAAkG,QAAA,EAAA;AAAA;AAAA,OAE9G;AAAA,sBAEA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAA,EAAuB,CAAA;AAAA,2BAGrC,KAAA,EAAA,EAAI,SAAA,EAAU,6BAA4B,KAAA,EAAO,SAAA,GAAY,KAAK,oBAAA,EACjE,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,OAAA,EAAS,MAAM,eAAA,CAAgB,MAAM,CAAA;AAAA,YAAG,UAAU,CAAC,SAAA;AAAA,YACzD,SAAA,EAAW,CAAA,sDAAA,EAAyD,YAAA,CAAa,IAAA,GAAO,8BAA8B,iCAAiC,CAAA,gDAAA,CAAA;AAAA,YAAoD,QAAA,EAAA;AAAA;AAAA,SAE7M;AAAA,wBACA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,OAAA,EAAS,MAAM,eAAA,CAAgB,QAAQ,CAAA;AAAA,YAAG,UAAU,CAAC,SAAA;AAAA,YAC3D,SAAA,EAAW,CAAA,mDAAA,EAAsD,YAAA,CAAa,MAAA,GAAS,8BAA8B,iCAAiC,CAAA,gDAAA,CAAA;AAAA,YAAoD,QAAA,EAAA;AAAA;AAAA,SAE5M;AAAA,wBACA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAAO,OAAA,EAAS,MAAM,eAAA,CAAgB,WAAW,CAAA;AAAA,YAAG,UAAU,CAAC,SAAA;AAAA,YAC9D,SAAA,EAAW,CAAA,sDAAA,EAAyD,YAAA,CAAa,SAAA,GAAY,8BAA8B,iCAAiC,CAAA,gDAAA,CAAA;AAAA,YAAoD,QAAA,EAAA;AAAA;AAAA,SAElN;AAAA,wBACA,IAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO,aAAa,QAAA,IAAY,MAAA;AAAA,YAChC,UAAU,CAAA,CAAA,KAAK,eAAA,CAAgB,UAAA,EAAY,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,YACzD,UAAU,CAAC,SAAA;AAAA,YACX,SAAA,EAAU,kHAAA;AAAA,YACV,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,IAAA,EAAK,QAAA,EAAA,IAAA,EAAE,CAAA;AAAA,8BACrB,GAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,MAAA,EAAO,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,8BACtB,GAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,IAAA,EAAK,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,8BACpB,GAAA,CAAC,QAAA,EAAA,EAAO,KAAA,EAAM,IAAA,EAAK,QAAA,EAAA,GAAA,EAAC;AAAA;AAAA;AAAA;AACtB,OAAA,EACF;AAAA,KAAA,EACF,CAAA;AAAA,oBAGA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gBAAA,EACb,QAAA,kBAAA,GAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACC,OAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA,EAAU,YAAA;AAAA,QACV,iBAAiB,CAAC,OAAA,KAAY,kBAAkB,EAAE,OAAA,EAAS,SAAS,CAAA;AAAA,QACpE,UAAA;AAAA,QACA,aAAA,EAAe,cAAA;AAAA,QACf,iBAAA,EAAmB,YAAA;AAAA,QACnB,OAAA,EAAS,YAAA;AAAA,QACT,SAAA,EAAU;AAAA;AAAA,KACZ,EACF,CAAA;AAAA,oBAGA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gEAAA,EACb,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,oEAAA,EACZ,QAAA,EAAA;AAAA,QAAA,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,EAAO,GAAA,qBAClB,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAS,MAAM,YAAA,CAAa,GAAG,CAAA;AAAA,YAC/B,eAAe,MAAM;AAAE,cAAA,aAAA,CAAc,GAAG,CAAA;AAAG,cAAA,UAAA,CAAW,MAAM,IAAI,CAAA;AAAA,YAAG,CAAA;AAAA,YACnE,eAAe,CAAA,CAAA,KAAK;AAAE,cAAA,CAAA,CAAE,cAAA,EAAe;AAAG,cAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,WAAA,CAAY,GAAG,CAAA;AAAA,YAAG,CAAA;AAAA,YACnF,SAAA,EAAW,CAAA,4EAAA,EACT,GAAA,KAAQ,SAAA,GACJ,kFACA,qDACN,CAAA,CAAA;AAAA,YACC,yBAAe,GAAA,mBACd,GAAA;AAAA,cAAC,OAAA;AAAA,cAAA;AAAA,gBAAM,IAAA,EAAK,MAAA;AAAA,gBAAO,KAAA,EAAO,OAAA;AAAA,gBAAS,QAAA,EAAU,CAAA,CAAA,KAAK,UAAA,CAAW,CAAA,CAAE,OAAO,KAAK,CAAA;AAAA,gBACzE,QAAQ,MAAM,WAAA,CAAY,GAAA,EAAK,OAAA,IAAW,MAAM,IAAI,CAAA;AAAA,gBACpD,WAAW,CAAA,CAAA,KAAK;AAAE,kBAAA,IAAI,EAAE,GAAA,KAAQ,OAAA,cAAqB,GAAA,EAAK,OAAA,IAAW,MAAM,IAAI,CAAA;AAAG,kBAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,aAAA,CAAc,IAAI,CAAA;AAAA,gBAAG,CAAA;AAAA,gBAC/H,OAAA,EAAS,CAAA,CAAA,KAAK,CAAA,CAAE,eAAA,EAAgB;AAAA,gBAChC,SAAA,EAAS,IAAA;AAAA,gBACT,SAAA,EAAU;AAAA;AAAA,gBACV,KAAA,CAAM;AAAA,WAAA;AAAA,UAhBC,KAAA,CAAM;AAAA,SAkBpB,CAAA;AAAA,wBACD,GAAA,CAAC,YAAO,OAAA,EAAS,QAAA,EAAU,WAAU,+EAAA,EAAgF,KAAA,EAAM,aAAY,QAAA,EAAA,GAAA,EAAC;AAAA,OAAA,EAC1I,CAAA;AAAA,sBAEA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2FAAA,EACb,QAAA,EAAA;AAAA,wBAAA,IAAA,CAAC,MAAA,EAAA,EAAM,QAAA,EAAA;AAAA,UAAA,WAAA;AAAA,UAAY;AAAA,SAAA,EAAM,CAAA;AAAA,QACxB,OAAA,CAAQ,MAAA,GAAS,CAAA,oBAChB,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,MAAA,EAAA,EAAK,QAAA,EAAA;AAAA,YAAA,OAAA;AAAA,YAAM,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,EAAG,MAAM,CAAA,GAAI,CAAA,EAAG,CAAC,CAAA,CAAE,cAAA,CAAe,MAAA,EAAW,EAAE,qBAAA,EAAuB,GAAG;AAAA,WAAA,EAAE,CAAA;AAAA,+BACtG,MAAA,EAAA,EAAK,QAAA,EAAA;AAAA,YAAA,OAAA;AAAA,YAAA,CAAO,QAAQ,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAA,EAAG,CAAC,CAAA,GAAI,OAAA,CAAQ,QAAQ,cAAA,CAAe,MAAA,EAAW,EAAE,qBAAA,EAAuB,GAAG;AAAA,WAAA,EAAE;AAAA,SAAA,EAC5H,CAAA;AAAA,6BAED,MAAA,EAAA,EAAM,QAAA,EAAA;AAAA,UAAA,IAAA,CAAK,MAAA;AAAA,UAAO,QAAA;AAAA,UAAI,OAAA,CAAQ;AAAA,SAAA,EAAO;AAAA,OAAA,EACxC;AAAA,KAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ","file":"Spreadsheet-IOKEDNS6.js","sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\nimport EditableGrid from '../shell/EditableGrid';\nimport type { GridColumn, CellStyle } from '../shell/EditableGrid';\nimport { WindowTitle } from '../shell/Modal';\n\nconst TITLE_DISPLAY_MAX = 24;\nfunction truncateForTitle(s: string) {\n return s.length > TITLE_DISPLAY_MAX ? `${s.slice(0, TITLE_DISPLAY_MAX - 1)}…` : s;\n}\n\nconst ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';\nconst DEFAULT_COLS = 10;\nconst DEFAULT_ROWS = 30;\n\nfunction colLabel(i: number): string {\n if (i < 26) return ALPHA[i];\n return ALPHA[Math.floor(i / 26) - 1] + ALPHA[i % 26];\n}\n\nfunction makeColumns(count: number): GridColumn[] {\n return Array.from({ length: count }, (_, i) => ({\n key: `col_${i}`,\n title: colLabel(i),\n width: 100,\n }));\n}\n\nfunction makeEmptyData(rows: number, cols: number): string[][] {\n return Array.from({ length: rows }, () => Array(cols).fill(''));\n}\n\ninterface Sheet {\n id: string;\n name: string;\n columns: GridColumn[];\n data: string[][];\n /** Per-cell text styling. Key: `${row}:${col}`. */\n cellStyles?: Record<string, CellStyle>;\n}\n\nfunction newSheet(name: string): Sheet {\n return {\n id: crypto.randomUUID(),\n name,\n columns: makeColumns(DEFAULT_COLS),\n data: makeEmptyData(DEFAULT_ROWS, DEFAULT_COLS),\n cellStyles: {},\n };\n}\n\nfunction parseCSV(text: string): string[][] {\n return text.split('\\n').map(line => {\n if (line.includes('\\t')) return line.split('\\t').map(s => s.trim());\n const parts: string[] = [];\n let current = '';\n let inQuotes = false;\n for (const ch of line) {\n if (ch === '\"') { inQuotes = !inQuotes; continue; }\n if (ch === ',' && !inQuotes) { parts.push(current.trim()); current = ''; continue; }\n current += ch;\n }\n parts.push(current.trim());\n return parts;\n }).filter(r => r.some(c => c.trim()));\n}\n\nexport default function Spreadsheet() {\n const containerRef = useRef<HTMLDivElement>(null);\n const [sheets, setSheets] = useState<Sheet[]>([newSheet('Sheet 1')]);\n const [activeIdx, setActiveIdx] = useState(0);\n\n // Undo history — push a snapshot whenever sheets change, except when the\n // change came from undo itself.\n const undoStackRef = useRef<Sheet[][]>([]);\n const lastCommittedRef = useRef<Sheet[]>(sheets);\n const skipRecordRef = useRef(false);\n useEffect(() => {\n if (skipRecordRef.current) {\n skipRecordRef.current = false;\n } else if (lastCommittedRef.current !== sheets) {\n undoStackRef.current.push(lastCommittedRef.current);\n if (undoStackRef.current.length > 50) undoStackRef.current.shift();\n }\n lastCommittedRef.current = sheets;\n }, [sheets]);\n const undo = useCallback(() => {\n const prev = undoStackRef.current.pop();\n if (!prev) return;\n skipRecordRef.current = true;\n setSheets(prev);\n }, []);\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if (!containerRef.current?.contains(document.activeElement) && !containerRef.current?.matches(':focus-within')) return;\n const isUndo = (e.ctrlKey || e.metaKey) && !e.shiftKey && (e.key === 'z' || e.key === 'Z');\n if (isUndo) {\n e.preventDefault();\n undo();\n }\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [undo]);\n const [title, setTitle] = useState('Untitled');\n const [editingTitle, setEditingTitle] = useState(false);\n const [editingTab, setEditingTab] = useState<number | null>(null);\n const [tabName, setTabName] = useState('');\n const fileRef = useRef<HTMLInputElement>(null);\n const [addColCount, setAddColCount] = useState('1');\n const [addRowCount, setAddRowCount] = useState('10');\n\n const active = sheets[activeIdx] || sheets[0];\n const data = active.data;\n const columns = active.columns;\n const cellStyles = active.cellStyles ?? {};\n const [focusedCell, setFocusedCell] = useState<{ row: number; col: number } | null>(null);\n const [selection, setSelection] = useState<{ anchor: { row: number; col: number }; end: { row: number; col: number } } | null>(null);\n\n // Cells affected by toolbar actions: full selection rectangle if non-empty,\n // otherwise the single focused cell.\n const targetCells = useCallback((): { row: number; col: number }[] => {\n if (selection) {\n const r1 = Math.min(selection.anchor.row, selection.end.row);\n const r2 = Math.max(selection.anchor.row, selection.end.row);\n const c1 = Math.min(selection.anchor.col, selection.end.col);\n const c2 = Math.max(selection.anchor.col, selection.end.col);\n const cells: { row: number; col: number }[] = [];\n for (let r = r1; r <= r2; r++) for (let c = c1; c <= c2; c++) cells.push({ row: r, col: c });\n return cells;\n }\n return focusedCell ? [focusedCell] : [];\n }, [selection, focusedCell]);\n\n const toggleCellStyle = useCallback((key: keyof CellStyle, value?: any) => {\n const cells = targetCells();\n if (cells.length === 0) return;\n setSheets(prev => prev.map((s, i) => {\n if (i !== activeIdx) return s;\n const styles = { ...(s.cellStyles ?? {}) };\n // For toggles, derive the new state from the FIRST cell so the whole\n // selection ends up consistent (rather than each cell flipping\n // independently).\n const firstKey = `${cells[0].row}:${cells[0].col}`;\n const desired = value !== undefined ? value : !((styles[firstKey] ?? {})[key] as any);\n for (const { row, col } of cells) {\n const k = `${row}:${col}`;\n styles[k] = { ...(styles[k] ?? {}), [key]: desired };\n }\n return { ...s, cellStyles: styles };\n }));\n }, [targetCells, activeIdx]);\n\n const headCell = selection ? { row: selection.anchor.row, col: selection.anchor.col } : focusedCell;\n const focusedStyle: CellStyle = headCell ? (cellStyles[`${headCell.row}:${headCell.col}`] ?? {}) : {};\n const hasTarget = !!headCell;\n\n const updateActiveSheet = useCallback((update: Partial<Sheet>) => {\n setSheets(prev => prev.map((s, i) => i === activeIdx ? { ...s, ...update } : s));\n }, [activeIdx]);\n\n const handleChange = useCallback((newData: string[][]) => {\n // Sync column count if rows were inserted/deleted with different column counts\n const maxCols = newData.reduce((m, r) => Math.max(m, r.length), 0);\n if (maxCols !== columns.length) {\n updateActiveSheet({ data: newData, columns: makeColumns(maxCols) });\n } else {\n updateActiveSheet({ data: newData });\n }\n }, [updateActiveSheet, columns.length]);\n\n // Tab management\n const addSheet = () => {\n const name = `Sheet ${sheets.length + 1}`;\n setSheets(prev => [...prev, newSheet(name)]);\n setActiveIdx(sheets.length);\n };\n\n const removeSheet = (idx: number) => {\n if (sheets.length <= 1) return;\n setSheets(prev => prev.filter((_, i) => i !== idx));\n if (activeIdx >= idx && activeIdx > 0) setActiveIdx(activeIdx - 1);\n };\n\n const renameSheet = (idx: number, name: string) => {\n setSheets(prev => prev.map((s, i) => i === idx ? { ...s, name } : s));\n setEditingTab(null);\n };\n\n // Add columns to active sheet\n const addColumns = (count: number) => {\n const newColCount = columns.length + count;\n updateActiveSheet({\n columns: makeColumns(newColCount),\n data: data.map(row => [...row, ...Array(count).fill('')]),\n });\n };\n\n // Add rows to active sheet\n const addRows = (count: number) => {\n updateActiveSheet({\n data: [...data, ...Array.from({ length: count }, () => Array(columns.length).fill(''))],\n });\n };\n\n // Clear active sheet\n const handleClear = () => {\n updateActiveSheet({\n columns: makeColumns(DEFAULT_COLS),\n data: makeEmptyData(DEFAULT_ROWS, DEFAULT_COLS),\n });\n };\n\n // Export as CSV\n const exportCSV = () => {\n const csv = data\n .filter(row => row.some(c => c.trim()))\n .map(row => row.map(cell => {\n if (cell.includes(',') || cell.includes('\"') || cell.includes('\\n'))\n return `\"${cell.replace(/\"/g, '\"\"')}\"`;\n return cell;\n }).join(','))\n .join('\\n');\n const blob = new Blob([csv], { type: 'text/csv' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `${title || 'spreadsheet'}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n };\n\n // Import CSV / XLSX\n const importFile = async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n const name = file.name.replace(/\\.(csv|tsv|txt|xlsx|xls|ods)$/i, '');\n\n if (/\\.(xlsx|xls|ods)$/i.test(file.name)) {\n // XLSX — dynamic import\n const byteArr = new Uint8Array(await file.arrayBuffer());\n const XLSX = await import('xlsx');\n const wb = XLSX.read(byteArr, { type: 'array' });\n const newSheets: Sheet[] = wb.SheetNames.map(sn => {\n const rows: string[][] = XLSX.utils.sheet_to_json(wb.Sheets[sn], { header: 1, defval: '' });\n const maxCols = Math.max(DEFAULT_COLS, rows.reduce((m, r) => Math.max(m, r.length), 0));\n const padded = rows.map(r => { const nr = r.map(c => String(c ?? '')); while (nr.length < maxCols) nr.push(''); return nr; });\n while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(''));\n return { id: crypto.randomUUID(), name: sn, columns: makeColumns(maxCols), data: padded };\n });\n setSheets(newSheets);\n setActiveIdx(0);\n setTitle(name);\n } else {\n // CSV/TSV\n const text = await file.text();\n const parsed = parseCSV(text);\n if (parsed.length === 0) return;\n const maxCols = Math.max(DEFAULT_COLS, parsed.reduce((m, r) => Math.max(m, r.length), 0));\n const padded = parsed.map(r => { while (r.length < maxCols) r.push(''); return r; });\n while (padded.length < DEFAULT_ROWS) padded.push(Array(maxCols).fill(''));\n updateActiveSheet({ columns: makeColumns(maxCols), data: padded });\n setTitle(name);\n }\n\n if (fileRef.current) fileRef.current.value = '';\n };\n\n // Stats\n const allNums: number[] = [];\n data.forEach(row => row.forEach(cell => {\n const v = parseFloat(cell);\n if (!isNaN(v) && cell.trim()) allNums.push(v);\n }));\n const filledCount = data.reduce((c, row) => c + row.filter(cell => cell.trim()).length, 0);\n\n return (\n <div ref={containerRef} className=\"flex flex-col h-full\">\n <WindowTitle title={`${truncateForTitle(title || 'Untitled')} - Spreadsheets`} />\n {/* Toolbar */}\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0\">\n {editingTitle ? (\n <input type=\"text\" value={title} onChange={e => setTitle(e.target.value)}\n onBlur={() => setEditingTitle(false)}\n onKeyDown={e => { if (e.key === 'Enter') setEditingTitle(false); }}\n autoFocus\n className=\"text-sm font-medium text-gray-900 border border-gray-300 rounded px-2 py-0.5 w-40 focus:border-blue-500 focus:ring-blue-500\" />\n ) : (\n <button onClick={() => setEditingTitle(true)} className=\"text-sm font-medium text-gray-900 hover:text-blue-600 truncate max-w-[200px]\" title=\"Click to rename\">\n {title || 'Untitled'}\n </button>\n )}\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n <input ref={fileRef} type=\"file\" accept=\".csv,.tsv,.txt,.xlsx,.xls,.ods\" onChange={importFile} className=\"hidden\" />\n <button onClick={() => fileRef.current?.click()}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Open\n </button>\n <button onClick={exportCSV}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Save CSV\n </button>\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n <div className=\"flex items-center gap-1\">\n <input type=\"number\" min=\"1\" max=\"50\" value={addColCount} onChange={e => setAddColCount(e.target.value)}\n className=\"w-10 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-blue-500\" />\n <button onClick={() => addColumns(parseInt(addColCount) || 1)}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n + Col\n </button>\n </div>\n <div className=\"flex items-center gap-1\">\n <input type=\"number\" min=\"1\" max=\"500\" value={addRowCount} onChange={e => setAddRowCount(e.target.value)}\n className=\"w-10 text-xs text-center border border-gray-300 rounded px-1 py-0.5 focus:border-blue-500 focus:ring-blue-500\" />\n <button onClick={() => addRows(parseInt(addRowCount) || 10)}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n + Row\n </button>\n </div>\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n <button onClick={handleClear}\n className=\"text-xs text-gray-600 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors\">\n Clear\n </button>\n\n <div className=\"h-4 w-px bg-gray-300\" />\n\n {/* Font style panel — applies to the focused cell. */}\n <div className=\"flex items-center gap-0.5\" title={hasTarget ? '' : 'Click a cell first'}>\n <button onClick={() => toggleCellStyle('bold')} disabled={!hasTarget}\n className={`px-2 py-1 text-xs rounded transition-colors font-bold ${focusedStyle.bold ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-200'} disabled:opacity-40 disabled:cursor-not-allowed`}>\n B\n </button>\n <button onClick={() => toggleCellStyle('italic')} disabled={!hasTarget}\n className={`px-2 py-1 text-xs rounded transition-colors italic ${focusedStyle.italic ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-200'} disabled:opacity-40 disabled:cursor-not-allowed`}>\n I\n </button>\n <button onClick={() => toggleCellStyle('underline')} disabled={!hasTarget}\n className={`px-2 py-1 text-xs rounded transition-colors underline ${focusedStyle.underline ? 'bg-blue-100 text-blue-700' : 'text-gray-600 hover:bg-gray-200'} disabled:opacity-40 disabled:cursor-not-allowed`}>\n U\n </button>\n <select\n value={focusedStyle.fontSize ?? 'base'}\n onChange={e => toggleCellStyle('fontSize', e.target.value)}\n disabled={!hasTarget}\n className=\"ml-1 text-xs border border-gray-300 rounded px-1 py-0.5 bg-white disabled:opacity-40 disabled:cursor-not-allowed\">\n <option value=\"sm\">XS</option>\n <option value=\"base\">S</option>\n <option value=\"lg\">M</option>\n <option value=\"xl\">L</option>\n </select>\n </div>\n </div>\n\n {/* Grid */}\n <div className=\"flex-1 min-h-0\">\n <EditableGrid\n columns={columns}\n data={data}\n onChange={handleChange}\n onColumnsChange={(newCols) => updateActiveSheet({ columns: newCols })}\n cellStyles={cellStyles}\n onFocusChange={setFocusedCell}\n onSelectionChange={setSelection}\n minRows={DEFAULT_ROWS}\n maxHeight=\"100%\"\n />\n </div>\n\n {/* Sheet tabs + status bar */}\n <div className=\"flex items-center border-t border-gray-200 bg-gray-50 shrink-0\">\n <div className=\"flex items-center gap-0.5 px-1 py-1 overflow-x-auto flex-1 min-w-0\">\n {sheets.map((sheet, idx) => (\n <button key={sheet.id}\n onClick={() => setActiveIdx(idx)}\n onDoubleClick={() => { setEditingTab(idx); setTabName(sheet.name); }}\n onContextMenu={e => { e.preventDefault(); if (sheets.length > 1) removeSheet(idx); }}\n className={`px-3 py-1 text-xs font-medium rounded-b whitespace-nowrap transition-colors ${\n idx === activeIdx\n ? 'bg-white text-blue-700 border border-t-0 border-gray-300 -mt-px relative z-10'\n : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'\n }`}>\n {editingTab === idx ? (\n <input type=\"text\" value={tabName} onChange={e => setTabName(e.target.value)}\n onBlur={() => renameSheet(idx, tabName || sheet.name)}\n onKeyDown={e => { if (e.key === 'Enter') renameSheet(idx, tabName || sheet.name); if (e.key === 'Escape') setEditingTab(null); }}\n onClick={e => e.stopPropagation()}\n autoFocus\n className=\"w-20 text-xs border border-blue-400 rounded px-1 py-0 focus:ring-0 focus:outline-none\" />\n ) : sheet.name}\n </button>\n ))}\n <button onClick={addSheet} className=\"px-2 py-1 text-xs text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded\" title=\"Add sheet\">+</button>\n </div>\n\n <div className=\"flex items-center gap-4 px-3 py-1 text-xs text-gray-500 shrink-0 border-l border-gray-200\">\n <span>{filledCount} cells</span>\n {allNums.length > 0 && (\n <>\n <span>Sum: {allNums.reduce((s, v) => s + v, 0).toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>\n <span>Avg: {(allNums.reduce((s, v) => s + v, 0) / allNums.length).toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>\n </>\n )}\n <span>{data.length} × {columns.length}</span>\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,197 @@
1
+ import { toast_default } from './chunk-WIJ45SYD.js';
2
+ import { useState, useRef, useCallback, useEffect } from 'react';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ function shuffle(arr) {
6
+ for (let i = arr.length - 1; i > 0; i--) {
7
+ const j = Math.floor(Math.random() * (i + 1));
8
+ [arr[i], arr[j]] = [arr[j], arr[i]];
9
+ }
10
+ return arr;
11
+ }
12
+ function emptyBoard() {
13
+ return Array.from({ length: 9 }, () => Array(9).fill(null));
14
+ }
15
+ function isValid(board, r, c, num) {
16
+ for (let i = 0; i < 9; i++) {
17
+ if (board[r][i] === num || board[i][c] === num) return false;
18
+ }
19
+ const br = Math.floor(r / 3) * 3, bc = Math.floor(c / 3) * 3;
20
+ for (let i = br; i < br + 3; i++)
21
+ for (let j = bc; j < bc + 3; j++)
22
+ if (board[i][j] === num) return false;
23
+ return true;
24
+ }
25
+ function solve(board) {
26
+ for (let r = 0; r < 9; r++)
27
+ for (let c = 0; c < 9; c++) {
28
+ if (board[r][c] !== null) continue;
29
+ const nums = shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9]);
30
+ for (const n of nums) {
31
+ if (isValid(board, r, c, n)) {
32
+ board[r][c] = n;
33
+ if (solve(board)) return true;
34
+ board[r][c] = null;
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ function generatePuzzle() {
42
+ const board = emptyBoard();
43
+ solve(board);
44
+ const givens = Array.from({ length: 9 }, () => Array(9).fill(true));
45
+ const cells = shuffle(
46
+ Array.from({ length: 81 }, (_, i) => [Math.floor(i / 9), i % 9])
47
+ );
48
+ let removed = 0;
49
+ for (const [r, c] of cells) {
50
+ if (removed >= 51) break;
51
+ board[r][c] = null;
52
+ givens[r][c] = false;
53
+ removed++;
54
+ }
55
+ return { puzzle: board, givens };
56
+ }
57
+ function hasConflict(board, r, c) {
58
+ const val = board[r][c];
59
+ if (val === null) return false;
60
+ for (let i = 0; i < 9; i++) {
61
+ if (i !== c && board[r][i] === val) return true;
62
+ if (i !== r && board[i][c] === val) return true;
63
+ }
64
+ const br = Math.floor(r / 3) * 3, bc = Math.floor(c / 3) * 3;
65
+ for (let i = br; i < br + 3; i++)
66
+ for (let j = bc; j < bc + 3; j++)
67
+ if ((i !== r || j !== c) && board[i][j] === val) return true;
68
+ return false;
69
+ }
70
+ function formatTime(s) {
71
+ const m = Math.floor(s / 60);
72
+ const sec = s % 60;
73
+ return `${m}:${sec.toString().padStart(2, "0")}`;
74
+ }
75
+ function Sudoku() {
76
+ const [board, setBoard] = useState(emptyBoard());
77
+ const [givens, setGivens] = useState(Array.from({ length: 9 }, () => Array(9).fill(false)));
78
+ const [selected, setSelected] = useState(null);
79
+ const [seconds, setSeconds] = useState(0);
80
+ const [running, setRunning] = useState(false);
81
+ const timerRef = useRef(null);
82
+ const startGame = useCallback(() => {
83
+ const { puzzle, givens: g } = generatePuzzle();
84
+ setBoard(puzzle);
85
+ setGivens(g);
86
+ setSelected(null);
87
+ setSeconds(0);
88
+ setRunning(true);
89
+ }, []);
90
+ useEffect(() => {
91
+ startGame();
92
+ }, [startGame]);
93
+ useEffect(() => {
94
+ if (running) {
95
+ timerRef.current = setInterval(() => setSeconds((s) => s + 1), 1e3);
96
+ } else if (timerRef.current) {
97
+ clearInterval(timerRef.current);
98
+ }
99
+ return () => {
100
+ if (timerRef.current) clearInterval(timerRef.current);
101
+ };
102
+ }, [running]);
103
+ const handleKey = useCallback((e) => {
104
+ if (!selected) return;
105
+ const [r, c] = selected;
106
+ if (givens[r][c]) return;
107
+ if (e.key >= "1" && e.key <= "9") {
108
+ setBoard((b) => {
109
+ const nb = b.map((row) => [...row]);
110
+ nb[r][c] = parseInt(e.key);
111
+ return nb;
112
+ });
113
+ } else if (e.key === "Backspace" || e.key === "Delete") {
114
+ setBoard((b) => {
115
+ const nb = b.map((row) => [...row]);
116
+ nb[r][c] = null;
117
+ return nb;
118
+ });
119
+ } else if (e.key === "ArrowUp" && r > 0) setSelected([r - 1, c]);
120
+ else if (e.key === "ArrowDown" && r < 8) setSelected([r + 1, c]);
121
+ else if (e.key === "ArrowLeft" && c > 0) setSelected([r, c - 1]);
122
+ else if (e.key === "ArrowRight" && c < 8) setSelected([r, c + 1]);
123
+ }, [selected, givens]);
124
+ const checkSolution = useCallback(() => {
125
+ let hasEmpty = false, hasError = false;
126
+ for (let r = 0; r < 9; r++)
127
+ for (let c = 0; c < 9; c++) {
128
+ if (board[r][c] === null) hasEmpty = true;
129
+ else if (hasConflict(board, r, c)) hasError = true;
130
+ }
131
+ if (hasError) toast_default.error("There are conflicts on the board.");
132
+ else if (hasEmpty) toast_default.info("Board is not yet complete.");
133
+ else {
134
+ toast_default.success("Congratulations! Puzzle solved!");
135
+ setRunning(false);
136
+ }
137
+ }, [board]);
138
+ const inSameGroup = (r, c) => {
139
+ if (!selected) return false;
140
+ const [sr, sc] = selected;
141
+ return r === sr || c === sc || Math.floor(r / 3) === Math.floor(sr / 3) && Math.floor(c / 3) === Math.floor(sc / 3);
142
+ };
143
+ const cellBg = (r, c) => {
144
+ const isSelected = selected && selected[0] === r && selected[1] === c;
145
+ const val = board[r][c];
146
+ const conflict = val !== null && hasConflict(board, r, c);
147
+ if (conflict) return "bg-red-100";
148
+ if (isSelected) return "bg-blue-200";
149
+ const sameNum = selected && val !== null && val === board[selected[0]][selected[1]];
150
+ if (sameNum) return "bg-blue-100";
151
+ if (inSameGroup(r, c)) return "bg-gray-100";
152
+ return "bg-white";
153
+ };
154
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4 p-6 select-none", onKeyDown: handleKey, tabIndex: 0, children: [
155
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-6", children: [
156
+ /* @__PURE__ */ jsx("span", { className: "text-lg font-mono font-semibold text-gray-700", children: formatTime(seconds) }),
157
+ /* @__PURE__ */ jsx(
158
+ "button",
159
+ {
160
+ onClick: startGame,
161
+ className: "px-3 py-1.5 text-sm font-medium bg-blue-600 text-white rounded hover:bg-blue-700",
162
+ children: "New Game"
163
+ }
164
+ ),
165
+ /* @__PURE__ */ jsx(
166
+ "button",
167
+ {
168
+ onClick: checkSolution,
169
+ className: "px-3 py-1.5 text-sm font-medium bg-green-600 text-white rounded hover:bg-green-700",
170
+ children: "Check"
171
+ }
172
+ )
173
+ ] }),
174
+ /* @__PURE__ */ jsx("div", { className: "border-2 border-gray-800 inline-grid grid-cols-9", style: { lineHeight: 0 }, children: board.map(
175
+ (row, r) => row.map((val, c) => {
176
+ const borderR = c % 3 === 2 && c < 8 ? "border-r-2 border-r-gray-800" : "border-r border-r-gray-300";
177
+ const borderB = r % 3 === 2 && r < 8 ? "border-b-2 border-b-gray-800" : "border-b border-b-gray-300";
178
+ return /* @__PURE__ */ jsx(
179
+ "div",
180
+ {
181
+ className: `w-10 h-10 flex items-center justify-center text-lg cursor-pointer
182
+ ${borderR} ${borderB} ${cellBg(r, c)}
183
+ ${givens[r][c] ? "font-bold text-gray-900" : "text-blue-600"}`,
184
+ onClick: () => setSelected([r, c]),
185
+ children: val ?? ""
186
+ },
187
+ `${r}-${c}`
188
+ );
189
+ })
190
+ ) }),
191
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400", children: "Click a cell, type 1-9 to fill, Backspace to clear. Arrow keys to navigate." })
192
+ ] });
193
+ }
194
+
195
+ export { Sudoku as default };
196
+ //# sourceMappingURL=Sudoku-XHLYCEVT.js.map
197
+ //# sourceMappingURL=Sudoku-XHLYCEVT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/apps/Sudoku.tsx"],"names":[],"mappings":"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","file":"Sudoku-XHLYCEVT.js","sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\nimport toast from '../shell/toast';\n\ntype Board = (number | null)[][];\ntype Givens = boolean[][];\n\nfunction shuffle<T>(arr: T[]): T[] {\n for (let i = arr.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [arr[i], arr[j]] = [arr[j], arr[i]];\n }\n return arr;\n}\n\nfunction emptyBoard(): Board {\n return Array.from({ length: 9 }, () => Array(9).fill(null));\n}\n\nfunction isValid(board: Board, r: number, c: number, num: number): boolean {\n for (let i = 0; i < 9; i++) {\n if (board[r][i] === num || board[i][c] === num) return false;\n }\n const br = Math.floor(r / 3) * 3, bc = Math.floor(c / 3) * 3;\n for (let i = br; i < br + 3; i++)\n for (let j = bc; j < bc + 3; j++)\n if (board[i][j] === num) return false;\n return true;\n}\n\nfunction solve(board: Board): boolean {\n for (let r = 0; r < 9; r++)\n for (let c = 0; c < 9; c++) {\n if (board[r][c] !== null) continue;\n const nums = shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9]);\n for (const n of nums) {\n if (isValid(board, r, c, n)) {\n board[r][c] = n;\n if (solve(board)) return true;\n board[r][c] = null;\n }\n }\n return false;\n }\n return true;\n}\n\nfunction generatePuzzle(): { puzzle: Board; givens: Givens } {\n const board = emptyBoard();\n solve(board);\n const givens: Givens = Array.from({ length: 9 }, () => Array(9).fill(true));\n const cells = shuffle(\n Array.from({ length: 81 }, (_, i) => [Math.floor(i / 9), i % 9] as [number, number])\n );\n let removed = 0;\n for (const [r, c] of cells) {\n if (removed >= 51) break; // 81 - 51 = 30 givens\n board[r][c] = null;\n givens[r][c] = false;\n removed++;\n }\n return { puzzle: board, givens };\n}\n\nfunction hasConflict(board: Board, r: number, c: number): boolean {\n const val = board[r][c];\n if (val === null) return false;\n for (let i = 0; i < 9; i++) {\n if (i !== c && board[r][i] === val) return true;\n if (i !== r && board[i][c] === val) return true;\n }\n const br = Math.floor(r / 3) * 3, bc = Math.floor(c / 3) * 3;\n for (let i = br; i < br + 3; i++)\n for (let j = bc; j < bc + 3; j++)\n if ((i !== r || j !== c) && board[i][j] === val) return true;\n return false;\n}\n\nfunction formatTime(s: number): string {\n const m = Math.floor(s / 60);\n const sec = s % 60;\n return `${m}:${sec.toString().padStart(2, '0')}`;\n}\n\nexport default function Sudoku() {\n const [board, setBoard] = useState<Board>(emptyBoard());\n const [givens, setGivens] = useState<Givens>(Array.from({ length: 9 }, () => Array(9).fill(false)));\n const [selected, setSelected] = useState<[number, number] | null>(null);\n const [seconds, setSeconds] = useState(0);\n const [running, setRunning] = useState(false);\n const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const startGame = useCallback(() => {\n const { puzzle, givens: g } = generatePuzzle();\n setBoard(puzzle);\n setGivens(g);\n setSelected(null);\n setSeconds(0);\n setRunning(true);\n }, []);\n\n useEffect(() => { startGame(); }, [startGame]);\n\n useEffect(() => {\n if (running) {\n timerRef.current = setInterval(() => setSeconds(s => s + 1), 1000);\n } else if (timerRef.current) {\n clearInterval(timerRef.current);\n }\n return () => { if (timerRef.current) clearInterval(timerRef.current); };\n }, [running]);\n\n const handleKey = useCallback((e: React.KeyboardEvent) => {\n if (!selected) return;\n const [r, c] = selected;\n if (givens[r][c]) return;\n if (e.key >= '1' && e.key <= '9') {\n setBoard(b => { const nb = b.map(row => [...row]); nb[r][c] = parseInt(e.key); return nb; });\n } else if (e.key === 'Backspace' || e.key === 'Delete') {\n setBoard(b => { const nb = b.map(row => [...row]); nb[r][c] = null; return nb; });\n } else if (e.key === 'ArrowUp' && r > 0) setSelected([r - 1, c]);\n else if (e.key === 'ArrowDown' && r < 8) setSelected([r + 1, c]);\n else if (e.key === 'ArrowLeft' && c > 0) setSelected([r, c - 1]);\n else if (e.key === 'ArrowRight' && c < 8) setSelected([r, c + 1]);\n }, [selected, givens]);\n\n const checkSolution = useCallback(() => {\n let hasEmpty = false, hasError = false;\n for (let r = 0; r < 9; r++)\n for (let c = 0; c < 9; c++) {\n if (board[r][c] === null) hasEmpty = true;\n else if (hasConflict(board, r, c)) hasError = true;\n }\n if (hasError) toast.error('There are conflicts on the board.');\n else if (hasEmpty) toast.info('Board is not yet complete.');\n else { toast.success('Congratulations! Puzzle solved!'); setRunning(false); }\n }, [board]);\n\n const inSameGroup = (r: number, c: number): boolean => {\n if (!selected) return false;\n const [sr, sc] = selected;\n return r === sr || c === sc ||\n (Math.floor(r / 3) === Math.floor(sr / 3) && Math.floor(c / 3) === Math.floor(sc / 3));\n };\n\n const cellBg = (r: number, c: number): string => {\n const isSelected = selected && selected[0] === r && selected[1] === c;\n const val = board[r][c];\n const conflict = val !== null && hasConflict(board, r, c);\n if (conflict) return 'bg-red-100';\n if (isSelected) return 'bg-blue-200';\n const sameNum = selected && val !== null && val === board[selected[0]][selected[1]];\n if (sameNum) return 'bg-blue-100';\n if (inSameGroup(r, c)) return 'bg-gray-100';\n return 'bg-white';\n };\n\n return (\n <div className=\"flex flex-col items-center gap-4 p-6 select-none\" onKeyDown={handleKey} tabIndex={0}>\n <div className=\"flex items-center gap-6\">\n <span className=\"text-lg font-mono font-semibold text-gray-700\">{formatTime(seconds)}</span>\n <button onClick={startGame}\n className=\"px-3 py-1.5 text-sm font-medium bg-blue-600 text-white rounded hover:bg-blue-700\">\n New Game\n </button>\n <button onClick={checkSolution}\n className=\"px-3 py-1.5 text-sm font-medium bg-green-600 text-white rounded hover:bg-green-700\">\n Check\n </button>\n </div>\n <div className=\"border-2 border-gray-800 inline-grid grid-cols-9\" style={{ lineHeight: 0 }}>\n {board.map((row, r) =>\n row.map((val, c) => {\n const borderR = c % 3 === 2 && c < 8 ? 'border-r-2 border-r-gray-800' : 'border-r border-r-gray-300';\n const borderB = r % 3 === 2 && r < 8 ? 'border-b-2 border-b-gray-800' : 'border-b border-b-gray-300';\n return (\n <div key={`${r}-${c}`}\n className={`w-10 h-10 flex items-center justify-center text-lg cursor-pointer\n ${borderR} ${borderB} ${cellBg(r, c)}\n ${givens[r][c] ? 'font-bold text-gray-900' : 'text-blue-600'}`}\n onClick={() => setSelected([r, c])}>\n {val ?? ''}\n </div>\n );\n })\n )}\n </div>\n <p className=\"text-xs text-gray-400\">Click a cell, type 1-9 to fill, Backspace to clear. Arrow keys to navigate.</p>\n </div>\n );\n}\n"]}
@@ -0,0 +1,243 @@
1
+ import { useState, useRef, useCallback, useEffect } from 'react';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+
4
+ // src/apps/Tetris.tsx
5
+ var COLS = 10;
6
+ var ROWS = 20;
7
+ var CELL = 28;
8
+ var SHAPES = [
9
+ [[1, 1, 1, 1]],
10
+ // I
11
+ [[1, 1], [1, 1]],
12
+ // O
13
+ [[0, 1, 0], [1, 1, 1]],
14
+ // T
15
+ [[0, 1, 1], [1, 1, 0]],
16
+ // S
17
+ [[1, 1, 0], [0, 1, 1]],
18
+ // Z
19
+ [[1, 0, 0], [1, 1, 1]],
20
+ // J
21
+ [[0, 0, 1], [1, 1, 1]]
22
+ // L
23
+ ];
24
+ var COLORS = ["#00f0f0", "#f0f000", "#a000f0", "#00f000", "#f00000", "#0000f0", "#f0a000"];
25
+ var rotate = (m) => m[0].map((_, i) => m.map((r) => r[i]).reverse());
26
+ var newPiece = (idx) => ({
27
+ shape: SHAPES[idx],
28
+ color: COLORS[idx],
29
+ idx,
30
+ x: Math.floor((COLS - SHAPES[idx][0].length) / 2),
31
+ y: 0
32
+ });
33
+ var randIdx = () => Math.floor(Math.random() * 7);
34
+ var valid = (board, shape, x, y) => shape.every(
35
+ (row, dy) => row.every(
36
+ (v, dx) => !v || x + dx >= 0 && x + dx < COLS && y + dy < ROWS && (y + dy < 0 || !board[y + dy][x + dx])
37
+ )
38
+ );
39
+ var merge = (board, piece) => board.map(
40
+ (row, r) => row.map((cell, c) => {
41
+ const dy = r - piece.y, dx = c - piece.x;
42
+ if (dy >= 0 && dy < piece.shape.length && dx >= 0 && dx < piece.shape[0].length && piece.shape[dy][dx])
43
+ return piece.color;
44
+ return cell;
45
+ })
46
+ );
47
+ var emptyBoard = () => Array.from({ length: ROWS }, () => Array(COLS).fill(""));
48
+ function Tetris() {
49
+ const [board, setBoard] = useState(emptyBoard);
50
+ const [piece, setPiece] = useState(() => newPiece(randIdx()));
51
+ const [nextIdx, setNextIdx] = useState(randIdx);
52
+ const [score, setScore] = useState(0);
53
+ const [lines, setLines] = useState(0);
54
+ const [level, setLevel] = useState(0);
55
+ const [gameOver, setGameOver] = useState(false);
56
+ const [paused, setPaused] = useState(false);
57
+ const [started, setStarted] = useState(false);
58
+ const tickRef = useRef();
59
+ const pieceRef = useRef(piece);
60
+ const boardRef = useRef(board);
61
+ pieceRef.current = piece;
62
+ boardRef.current = board;
63
+ const lockPiece = useCallback(() => {
64
+ const p = pieceRef.current;
65
+ let b = merge(boardRef.current, p);
66
+ const full = b.reduce((acc, row, i) => row.every((c) => c) ? [...acc, i] : acc, []);
67
+ if (full.length) {
68
+ full.forEach((i) => {
69
+ b.splice(i, 1);
70
+ b.unshift(Array(COLS).fill(""));
71
+ });
72
+ const pts = [0, 100, 300, 500, 800][full.length] || 800;
73
+ setScore((s) => s + pts * (level + 1));
74
+ setLines((l) => {
75
+ const nl = l + full.length;
76
+ setLevel(Math.floor(nl / 10));
77
+ return nl;
78
+ });
79
+ }
80
+ setBoard(b);
81
+ const np = newPiece(nextIdx);
82
+ if (!valid(b, np.shape, np.x, np.y)) {
83
+ setGameOver(true);
84
+ return;
85
+ }
86
+ setPiece(np);
87
+ setNextIdx(randIdx());
88
+ }, [nextIdx, level]);
89
+ const drop = useCallback(() => {
90
+ const p = pieceRef.current;
91
+ if (valid(boardRef.current, p.shape, p.x, p.y + 1)) {
92
+ setPiece({ ...p, y: p.y + 1 });
93
+ } else {
94
+ lockPiece();
95
+ }
96
+ }, [lockPiece]);
97
+ useEffect(() => {
98
+ if (gameOver || paused || !started) return;
99
+ clearInterval(tickRef.current);
100
+ tickRef.current = setInterval(drop, Math.max(100, 800 - level * 70));
101
+ return () => clearInterval(tickRef.current);
102
+ }, [drop, level, gameOver, paused, started]);
103
+ const move = useCallback((dx, dy) => {
104
+ const p = pieceRef.current;
105
+ if (valid(boardRef.current, p.shape, p.x + dx, p.y + dy))
106
+ setPiece({ ...p, x: p.x + dx, y: p.y + dy });
107
+ }, []);
108
+ const rotatePiece = useCallback(() => {
109
+ const p = pieceRef.current;
110
+ const r = rotate(p.shape);
111
+ for (const dx of [0, -1, 1, -2, 2]) {
112
+ if (valid(boardRef.current, r, p.x + dx, p.y)) {
113
+ setPiece({ ...p, shape: r, x: p.x + dx });
114
+ return;
115
+ }
116
+ }
117
+ }, []);
118
+ const hardDrop = useCallback(() => {
119
+ const p = pieceRef.current;
120
+ let ny = p.y;
121
+ while (valid(boardRef.current, p.shape, p.x, ny + 1)) ny++;
122
+ setPiece({ ...p, y: ny });
123
+ setTimeout(() => lockPiece(), 0);
124
+ }, [lockPiece]);
125
+ const restart = useCallback(() => {
126
+ setBoard(emptyBoard());
127
+ const idx = randIdx();
128
+ setPiece(newPiece(idx));
129
+ setNextIdx(randIdx());
130
+ setScore(0);
131
+ setLines(0);
132
+ setLevel(0);
133
+ setGameOver(false);
134
+ setPaused(false);
135
+ setStarted(true);
136
+ }, []);
137
+ useEffect(() => {
138
+ const handler = (e) => {
139
+ if (e.key === "p" || e.key === "P") {
140
+ if (started && !gameOver) setPaused((v) => !v);
141
+ return;
142
+ }
143
+ if (gameOver || paused || !started) return;
144
+ switch (e.key) {
145
+ case "ArrowLeft":
146
+ e.preventDefault();
147
+ move(-1, 0);
148
+ break;
149
+ case "ArrowRight":
150
+ e.preventDefault();
151
+ move(1, 0);
152
+ break;
153
+ case "ArrowDown":
154
+ e.preventDefault();
155
+ move(0, 1);
156
+ break;
157
+ case "ArrowUp":
158
+ e.preventDefault();
159
+ rotatePiece();
160
+ break;
161
+ case " ":
162
+ e.preventDefault();
163
+ hardDrop();
164
+ break;
165
+ }
166
+ };
167
+ window.addEventListener("keydown", handler);
168
+ return () => window.removeEventListener("keydown", handler);
169
+ }, [move, rotatePiece, hardDrop, gameOver, paused, started]);
170
+ let ghostY = piece.y;
171
+ while (valid(board, piece.shape, piece.x, ghostY + 1)) ghostY++;
172
+ const display = board.map(
173
+ (row, r) => row.map((cell, c) => {
174
+ const dy = r - piece.y, dx = c - piece.x;
175
+ const inPiece = dy >= 0 && dy < piece.shape.length && dx >= 0 && dx < piece.shape[0].length && piece.shape[dy][dx];
176
+ if (inPiece) return piece.color;
177
+ const gdy = r - ghostY, gdx = c - piece.x;
178
+ const inGhost = gdy >= 0 && gdy < piece.shape.length && gdx >= 0 && gdx < piece.shape[0].length && piece.shape[gdy][gdx];
179
+ if (inGhost && !cell) return "ghost";
180
+ return cell;
181
+ })
182
+ );
183
+ const nextShape = SHAPES[nextIdx];
184
+ const nextColor = COLORS[nextIdx];
185
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center select-none", tabIndex: 0, children: [
186
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
187
+ /* @__PURE__ */ jsx("div", { className: "border-2 border-gray-600", style: { width: COLS * CELL, height: ROWS * CELL }, children: display.map((row, r) => /* @__PURE__ */ jsx("div", { className: "flex", children: row.map((cell, c) => /* @__PURE__ */ jsx(
188
+ "div",
189
+ {
190
+ style: {
191
+ width: CELL,
192
+ height: CELL,
193
+ backgroundColor: cell === "ghost" ? "rgba(255,255,255,0.08)" : cell || "#111",
194
+ border: "1px solid #222",
195
+ opacity: cell && cell !== "ghost" ? 0.9 : 1
196
+ }
197
+ },
198
+ c
199
+ )) }, r)) }),
200
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 text-white w-28", children: [
201
+ /* @__PURE__ */ jsxs("div", { children: [
202
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-400 uppercase", children: "Next" }),
203
+ /* @__PURE__ */ jsx("div", { className: "mt-1 p-1 bg-gray-800 rounded flex flex-col items-center justify-center", style: { minHeight: 5 * CELL }, children: nextShape.map((row, r) => /* @__PURE__ */ jsx("div", { className: "flex", children: row.map((v, c) => /* @__PURE__ */ jsx("div", { style: {
204
+ width: CELL,
205
+ height: CELL,
206
+ backgroundColor: v ? nextColor : "transparent",
207
+ border: v ? "1px solid rgba(0,0,0,0.3)" : "none"
208
+ } }, c)) }, r)) })
209
+ ] }),
210
+ /* @__PURE__ */ jsxs("div", { children: [
211
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-400 uppercase", children: "Score" }),
212
+ /* @__PURE__ */ jsx("div", { className: "text-lg font-bold", children: score })
213
+ ] }),
214
+ /* @__PURE__ */ jsxs("div", { children: [
215
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-400 uppercase", children: "Level" }),
216
+ /* @__PURE__ */ jsx("div", { className: "text-lg font-bold", children: level })
217
+ ] }),
218
+ /* @__PURE__ */ jsxs("div", { children: [
219
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-400 uppercase", children: "Lines" }),
220
+ /* @__PURE__ */ jsx("div", { className: "text-lg font-bold", children: lines })
221
+ ] }),
222
+ /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 mt-2 space-y-0.5", children: [
223
+ /* @__PURE__ */ jsx("div", { children: "Arrows: Move/Rotate" }),
224
+ /* @__PURE__ */ jsx("div", { children: "Space: Hard Drop" }),
225
+ /* @__PURE__ */ jsx("div", { children: "P: Pause" })
226
+ ] }),
227
+ (!started || gameOver) && /* @__PURE__ */ jsx("button", { onClick: restart, className: "mt-2 px-3 py-1.5 bg-blue-600 hover:bg-blue-500 rounded text-sm font-medium", children: gameOver ? "Play Again" : "Start" })
228
+ ] })
229
+ ] }),
230
+ paused && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx("div", { className: "text-white text-3xl font-bold", children: "PAUSED" }) }),
231
+ gameOver && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
232
+ /* @__PURE__ */ jsx("div", { className: "text-white text-3xl font-bold", children: "GAME OVER" }),
233
+ /* @__PURE__ */ jsxs("div", { className: "text-gray-300 mt-2", children: [
234
+ "Score: ",
235
+ score
236
+ ] })
237
+ ] }) })
238
+ ] });
239
+ }
240
+
241
+ export { Tetris as default };
242
+ //# sourceMappingURL=Tetris-ZHCZYL24.js.map
243
+ //# sourceMappingURL=Tetris-ZHCZYL24.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/apps/Tetris.tsx"],"names":[],"mappings":"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","file":"Tetris-ZHCZYL24.js","sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\n\nconst COLS = 10, ROWS = 20, CELL = 28;\nconst SHAPES: number[][][] = [\n [[1,1,1,1]], // I\n [[1,1],[1,1]], // O\n [[0,1,0],[1,1,1]], // T\n [[0,1,1],[1,1,0]], // S\n [[1,1,0],[0,1,1]], // Z\n [[1,0,0],[1,1,1]], // J\n [[0,0,1],[1,1,1]], // L\n];\nconst COLORS = ['#00f0f0','#f0f000','#a000f0','#00f000','#f00000','#0000f0','#f0a000'];\n\ntype Piece = { shape: number[][]; color: string; x: number; y: number; idx: number };\n\nconst rotate = (m: number[][]): number[][] =>\n m[0].map((_, i) => m.map(r => r[i]).reverse());\n\nconst newPiece = (idx: number): Piece => ({\n shape: SHAPES[idx], color: COLORS[idx], idx,\n x: Math.floor((COLS - SHAPES[idx][0].length) / 2), y: 0,\n});\n\nconst randIdx = () => Math.floor(Math.random() * 7);\n\nconst valid = (board: string[][], shape: number[][], x: number, y: number) =>\n shape.every((row, dy) =>\n row.every((v, dx) =>\n !v || (x + dx >= 0 && x + dx < COLS && y + dy < ROWS && (y + dy < 0 || !board[y + dy][x + dx]))\n )\n );\n\nconst merge = (board: string[][], piece: Piece): string[][] =>\n board.map((row, r) =>\n row.map((cell, c) => {\n const dy = r - piece.y, dx = c - piece.x;\n if (dy >= 0 && dy < piece.shape.length && dx >= 0 && dx < piece.shape[0].length && piece.shape[dy][dx])\n return piece.color;\n return cell;\n })\n );\n\nconst emptyBoard = (): string[][] => Array.from({ length: ROWS }, () => Array(COLS).fill(''));\n\nexport default function Tetris() {\n const [board, setBoard] = useState(emptyBoard);\n const [piece, setPiece] = useState<Piece>(() => newPiece(randIdx()));\n const [nextIdx, setNextIdx] = useState(randIdx);\n const [score, setScore] = useState(0);\n const [lines, setLines] = useState(0);\n const [level, setLevel] = useState(0);\n const [gameOver, setGameOver] = useState(false);\n const [paused, setPaused] = useState(false);\n const [started, setStarted] = useState(false);\n const tickRef = useRef<ReturnType<typeof setInterval>>();\n const pieceRef = useRef(piece);\n const boardRef = useRef(board);\n pieceRef.current = piece;\n boardRef.current = board;\n\n const lockPiece = useCallback(() => {\n const p = pieceRef.current;\n let b = merge(boardRef.current, p);\n // Clear lines\n const full = b.reduce<number[]>((acc, row, i) => (row.every(c => c) ? [...acc, i] : acc), []);\n if (full.length) {\n full.forEach(i => { b.splice(i, 1); b.unshift(Array(COLS).fill('')); });\n const pts = [0, 100, 300, 500, 800][full.length] || 800;\n setScore(s => s + pts * (level + 1));\n setLines(l => {\n const nl = l + full.length;\n setLevel(Math.floor(nl / 10));\n return nl;\n });\n }\n setBoard(b);\n // Spawn next\n const np = newPiece(nextIdx);\n if (!valid(b, np.shape, np.x, np.y)) {\n setGameOver(true);\n return;\n }\n setPiece(np);\n setNextIdx(randIdx());\n }, [nextIdx, level]);\n\n const drop = useCallback(() => {\n const p = pieceRef.current;\n if (valid(boardRef.current, p.shape, p.x, p.y + 1)) {\n setPiece({ ...p, y: p.y + 1 });\n } else {\n lockPiece();\n }\n }, [lockPiece]);\n\n // Tick\n useEffect(() => {\n if (gameOver || paused || !started) return;\n clearInterval(tickRef.current);\n tickRef.current = setInterval(drop, Math.max(100, 800 - level * 70));\n return () => clearInterval(tickRef.current);\n }, [drop, level, gameOver, paused, started]);\n\n const move = useCallback((dx: number, dy: number) => {\n const p = pieceRef.current;\n if (valid(boardRef.current, p.shape, p.x + dx, p.y + dy))\n setPiece({ ...p, x: p.x + dx, y: p.y + dy });\n }, []);\n\n const rotatePiece = useCallback(() => {\n const p = pieceRef.current;\n const r = rotate(p.shape);\n // Wall kick offsets\n for (const dx of [0, -1, 1, -2, 2]) {\n if (valid(boardRef.current, r, p.x + dx, p.y)) {\n setPiece({ ...p, shape: r, x: p.x + dx });\n return;\n }\n }\n }, []);\n\n const hardDrop = useCallback(() => {\n const p = pieceRef.current;\n let ny = p.y;\n while (valid(boardRef.current, p.shape, p.x, ny + 1)) ny++;\n setPiece({ ...p, y: ny });\n // Use setTimeout to ensure state is set before lock\n setTimeout(() => lockPiece(), 0);\n }, [lockPiece]);\n\n const restart = useCallback(() => {\n setBoard(emptyBoard());\n const idx = randIdx();\n setPiece(newPiece(idx));\n setNextIdx(randIdx());\n setScore(0);\n setLines(0);\n setLevel(0);\n setGameOver(false);\n setPaused(false);\n setStarted(true);\n }, []);\n\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'p' || e.key === 'P') { if (started && !gameOver) setPaused(v => !v); return; }\n if (gameOver || paused || !started) return;\n switch (e.key) {\n case 'ArrowLeft': e.preventDefault(); move(-1, 0); break;\n case 'ArrowRight': e.preventDefault(); move(1, 0); break;\n case 'ArrowDown': e.preventDefault(); move(0, 1); break;\n case 'ArrowUp': e.preventDefault(); rotatePiece(); break;\n case ' ': e.preventDefault(); hardDrop(); break;\n }\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [move, rotatePiece, hardDrop, gameOver, paused, started]);\n\n // Ghost piece\n let ghostY = piece.y;\n while (valid(board, piece.shape, piece.x, ghostY + 1)) ghostY++;\n\n // Render board with piece & ghost\n const display = board.map((row, r) =>\n row.map((cell, c) => {\n const dy = r - piece.y, dx = c - piece.x;\n const inPiece = dy >= 0 && dy < piece.shape.length && dx >= 0 && dx < piece.shape[0].length && piece.shape[dy][dx];\n if (inPiece) return piece.color;\n const gdy = r - ghostY, gdx = c - piece.x;\n const inGhost = gdy >= 0 && gdy < piece.shape.length && gdx >= 0 && gdx < piece.shape[0].length && piece.shape[gdy][gdx];\n if (inGhost && !cell) return 'ghost';\n return cell;\n })\n );\n\n const nextShape = SHAPES[nextIdx];\n const nextColor = COLORS[nextIdx];\n\n return (\n <div className=\"flex items-center justify-center select-none\" tabIndex={0}>\n <div className=\"flex gap-4\">\n {/* Board */}\n <div className=\"border-2 border-gray-600\" style={{ width: COLS * CELL, height: ROWS * CELL }}>\n {display.map((row, r) => (\n <div key={r} className=\"flex\">\n {row.map((cell, c) => (\n <div\n key={c}\n style={{\n width: CELL, height: CELL,\n backgroundColor: cell === 'ghost' ? 'rgba(255,255,255,0.08)' : cell || '#111',\n border: '1px solid #222',\n opacity: cell && cell !== 'ghost' ? 0.9 : 1,\n }}\n />\n ))}\n </div>\n ))}\n </div>\n {/* Side panel */}\n <div className=\"flex flex-col gap-3 text-white w-28\">\n <div>\n <div className=\"text-xs text-gray-400 uppercase\">Next</div>\n <div className=\"mt-1 p-1 bg-gray-800 rounded flex flex-col items-center justify-center\" style={{ minHeight: 5 * CELL }}>\n {nextShape.map((row, r) => (\n <div key={r} className=\"flex\">\n {row.map((v, c) => (\n <div key={c} style={{\n width: CELL, height: CELL,\n backgroundColor: v ? nextColor : 'transparent',\n border: v ? '1px solid rgba(0,0,0,0.3)' : 'none',\n }} />\n ))}\n </div>\n ))}\n </div>\n </div>\n <div>\n <div className=\"text-xs text-gray-400 uppercase\">Score</div>\n <div className=\"text-lg font-bold\">{score}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-400 uppercase\">Level</div>\n <div className=\"text-lg font-bold\">{level}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-400 uppercase\">Lines</div>\n <div className=\"text-lg font-bold\">{lines}</div>\n </div>\n <div className=\"text-xs text-gray-500 mt-2 space-y-0.5\">\n <div>Arrows: Move/Rotate</div>\n <div>Space: Hard Drop</div>\n <div>P: Pause</div>\n </div>\n {(!started || gameOver) && (\n <button onClick={restart} className=\"mt-2 px-3 py-1.5 bg-blue-600 hover:bg-blue-500 rounded text-sm font-medium\">\n {gameOver ? 'Play Again' : 'Start'}\n </button>\n )}\n </div>\n </div>\n {/* Overlays */}\n {paused && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/60\">\n <div className=\"text-white text-3xl font-bold\">PAUSED</div>\n </div>\n )}\n {gameOver && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/60\">\n <div className=\"text-center\">\n <div className=\"text-white text-3xl font-bold\">GAME OVER</div>\n <div className=\"text-gray-300 mt-2\">Score: {score}</div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"]}