@particle-academy/react-fancy 5.33.0 → 5.35.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 (70) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/dist/activity-light.cjs +13 -0
  3. package/dist/activity-light.cjs.map +1 -0
  4. package/dist/activity-light.d.cts +64 -0
  5. package/dist/activity-light.d.ts +64 -0
  6. package/dist/activity-light.js +4 -0
  7. package/dist/activity-light.js.map +1 -0
  8. package/dist/button.cjs +4 -4
  9. package/dist/button.js +2 -2
  10. package/dist/{chunk-ZYVWKYPO.js → chunk-2OMB3OOR.js} +3 -3
  11. package/dist/{chunk-ZYVWKYPO.js.map → chunk-2OMB3OOR.js.map} +1 -1
  12. package/dist/{chunk-G6VMNSCM.js → chunk-46IPQEUP.js} +3 -3
  13. package/dist/{chunk-G6VMNSCM.js.map → chunk-46IPQEUP.js.map} +1 -1
  14. package/dist/{chunk-H6PXIB35.cjs → chunk-5Q7EQQTT.cjs} +4 -4
  15. package/dist/{chunk-H6PXIB35.cjs.map → chunk-5Q7EQQTT.cjs.map} +1 -1
  16. package/dist/{chunk-BQ77ZWFM.js → chunk-6ASZUE4T.js} +4 -4
  17. package/dist/{chunk-BQ77ZWFM.js.map → chunk-6ASZUE4T.js.map} +1 -1
  18. package/dist/{chunk-WEKQD4BQ.js → chunk-DCDW3LW6.js} +3 -3
  19. package/dist/{chunk-WEKQD4BQ.js.map → chunk-DCDW3LW6.js.map} +1 -1
  20. package/dist/{chunk-VRJTPXJ7.cjs → chunk-DNV7MUEE.cjs} +81 -20
  21. package/dist/chunk-DNV7MUEE.cjs.map +1 -0
  22. package/dist/{chunk-YMU3SS6D.cjs → chunk-E2SZBIAV.cjs} +16 -16
  23. package/dist/{chunk-YMU3SS6D.cjs.map → chunk-E2SZBIAV.cjs.map} +1 -1
  24. package/dist/chunk-EHNMKQ4O.cjs +71 -0
  25. package/dist/chunk-EHNMKQ4O.cjs.map +1 -0
  26. package/dist/chunk-IR6JSQ4O.cjs +233 -0
  27. package/dist/chunk-IR6JSQ4O.cjs.map +1 -0
  28. package/dist/{chunk-5TUZENNJ.cjs → chunk-MTUH2X3W.cjs} +4 -4
  29. package/dist/{chunk-5TUZENNJ.cjs.map → chunk-MTUH2X3W.cjs.map} +1 -1
  30. package/dist/{chunk-B4PFNSX7.js → chunk-O3WEV562.js} +79 -21
  31. package/dist/chunk-O3WEV562.js.map +1 -0
  32. package/dist/chunk-PZTA6NCD.js +69 -0
  33. package/dist/chunk-PZTA6NCD.js.map +1 -0
  34. package/dist/{chunk-UWL6U3L7.cjs → chunk-RMJGF7MN.cjs} +3 -3
  35. package/dist/{chunk-UWL6U3L7.cjs.map → chunk-RMJGF7MN.cjs.map} +1 -1
  36. package/dist/{chunk-EIUS2K72.js → chunk-TX6VUYBC.js} +3 -3
  37. package/dist/{chunk-EIUS2K72.js.map → chunk-TX6VUYBC.js.map} +1 -1
  38. package/dist/{chunk-LQFQ2UKU.cjs → chunk-WWUKTXPX.cjs} +3 -3
  39. package/dist/{chunk-LQFQ2UKU.cjs.map → chunk-WWUKTXPX.cjs.map} +1 -1
  40. package/dist/chunk-Z4CW3U6C.js +231 -0
  41. package/dist/chunk-Z4CW3U6C.js.map +1 -0
  42. package/dist/editor.cjs +4 -4
  43. package/dist/editor.js +2 -2
  44. package/dist/index-DXUAAZzE.d.cts +157 -0
  45. package/dist/index-DXUAAZzE.d.ts +157 -0
  46. package/dist/index.cjs +160 -138
  47. package/dist/index.d.cts +3 -1
  48. package/dist/index.d.ts +3 -1
  49. package/dist/index.js +27 -25
  50. package/dist/inputs.cjs +9 -9
  51. package/dist/inputs.js +3 -3
  52. package/dist/json-editor.cjs +15 -15
  53. package/dist/json-editor.js +4 -4
  54. package/dist/magic-wand.cjs +4 -4
  55. package/dist/magic-wand.js +3 -3
  56. package/dist/prompt-input.cjs +5 -4
  57. package/dist/prompt-input.d.cts +2 -77
  58. package/dist/prompt-input.d.ts +2 -77
  59. package/dist/prompt-input.js +4 -3
  60. package/dist/reason-tag.cjs +4 -4
  61. package/dist/reason-tag.js +3 -3
  62. package/dist/virtual-list.cjs +13 -0
  63. package/dist/virtual-list.cjs.map +1 -0
  64. package/dist/virtual-list.d.cts +125 -0
  65. package/dist/virtual-list.d.ts +125 -0
  66. package/dist/virtual-list.js +4 -0
  67. package/dist/virtual-list.js.map +1 -0
  68. package/package.json +23 -2
  69. package/dist/chunk-B4PFNSX7.js.map +0 -1
  70. package/dist/chunk-VRJTPXJ7.cjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","detectFormat","marked","sanitizeHtml","useControllableState","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","ContentRenderer"],"mappings":";;;;;;;;;;;;AA4BO,IAAM,aAAA,GAAgBA,oBAAyC,IAAI,CAAA;AAEnE,SAAS,SAAA,GAAgC;AAC9C,EAAA,MAAM,GAAA,GAAMC,iBAAW,aAAa,CAAA;AACpC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,GAAA;AACT;AC3BO,SAAS,kBAAA,CAAmB;AAAA,EACjC,SAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,KAAA,GAAQ,QAAA;AAAA,EACR,WAAA,GAAc;AAChB,CAAA,EAA4B;AAC1B,EAAA,MAAM,EAAE,UAAA,EAAY,aAAA,EAAc,GAAI,SAAA,EAAU;AAEhD,EAAA,uBACEC,cAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAK,QAAA;AAAA,MACL,OAAA,EAAS,MAAM,aAAA,CAAc,CAAC,UAAU,CAAA;AAAA,MACxC,KAAA,EAAO,aAAa,WAAA,GAAc,KAAA;AAAA,MAClC,cAAA,EAAc,UAAA;AAAA,MACd,uCAAA,EAAsC,EAAA;AAAA,MACtC,SAAA,EAAWC,oBAAA;AAAA,QACT,iGAAA;AAAA,QACA,aACI,8BAAA,GACA,0CAAA;AAAA,QACJ;AAAA,OACF;AAAA,MAEC,QAAA,EAAA;AAAA;AAAA,GACH;AAEJ;AAEA,kBAAA,CAAmB,WAAA,GAAc,oBAAA;AChCjC,IAAM,eAAA,GAAkB;AAAA,EACtB,EAAE,IAAA,EAAM,GAAA,EAAK,KAAA,EAAO,MAAA,EAAQ,SAAS,MAAA,EAAO;AAAA,EAC5C,EAAE,IAAA,EAAM,GAAA,EAAK,KAAA,EAAO,QAAA,EAAU,SAAS,QAAA,EAAS;AAAA,EAChD,EAAE,IAAA,EAAM,GAAA,EAAK,KAAA,EAAO,WAAA,EAAa,SAAS,WAAA,EAAY;AAAA,EACtD,EAAE,IAAA,EAAM,GAAA,EAAK,KAAA,EAAO,eAAA,EAAiB,SAAS,eAAA;AAChD,CAAA;AAEO,SAAS,aAAA,CAAc;AAAA,EAC5B,OAAA,GAAU,eAAA;AAAA,EACV,QAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,gBAAA,GAAmB;AACrB,CAAA,EAAuB;AACrB,EAAA,MAAM,EAAE,IAAA,EAAM,UAAA,EAAW,GAAI,SAAA,EAAU;AAEvC,EAAA,uBACED,cAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,iCAAA,EAAgC,EAAA;AAAA,MAChC,SAAA,EAAWC,oBAAA;AAAA,QACT,8FAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEC,sCACCC,eAAA,CAAAC,mBAAA,EAAA,EACG,QAAA,EAAA;AAAA,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,qBACZH,cAAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAEC,IAAA,EAAK,QAAA;AAAA,YAEL,QAAA,EAAU,UAAA;AAAA,YACV,SAAS,MAAM;AACb,cAAA,IAAI,QAAA,EAAU;AACZ,gBAAA,QAAA,CAAS,OAAO,OAAO,CAAA;AAAA,cACzB,CAAA,MAAO;AACL,gBAAA,IAAA,CAAK,MAAA,CAAO,OAAA,EAAS,MAAA,CAAO,UAAU,CAAA;AAAA,cACxC;AAAA,YACF,CAAA;AAAA,YACA,OAAO,MAAA,CAAO,KAAA;AAAA,YACd,SAAA,EAAWC,oBAAA;AAAA,cACT,sFAAA;AAAA,cACA,MAAA,CAAO,SACH,8BAAA,GACA,0CAAA;AAAA,cACJ,UAAA,IAAc;AAAA,aAChB;AAAA,YAEC,QAAA,EAAA,MAAA,CAAO;AAAA,WAAA;AAAA,UApBH,GAAG,MAAA,CAAO,OAAO,CAAA,CAAA,EAAI,MAAA,CAAO,cAAc,EAAE,CAAA;AAAA,SAsBpD,CAAA;AAAA,QACA,gBAAA,oBAAoBD,cAAAA,CAAC,kBAAA,EAAA,EAAmB,WAAU,SAAA,EAAU;AAAA,OAAA,EAC/D;AAAA;AAAA,GAEJ;AAEJ;AAEA,aAAA,CAAc,WAAA,GAAc,eAAA;AC/DrB,SAAS,sBAAA,GAAyB;AACvC,EAAA,uBACEA,cAAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,2CAAA,EAA0C,EAAA;AAAA,MAC1C,SAAA,EAAU;AAAA;AAAA,GACZ;AAEJ;AAEA,sBAAA,CAAuB,WAAA,GAAc,wBAAA;ACF9B,SAAS,aAAA,CAAc,EAAE,SAAA,EAAW,SAAA,EAAW,iBAAgB,EAAuB;AAC3F,EAAA,MAAM;AAAA,IACJ,UAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,YAAA;AAAA,IACA,UAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,MACE,SAAA,EAAU;AAKd,EAAA,MAAM,UAAA,GAAaI,aAAsB,IAAI,CAAA;AAE7C,EAAAC,eAAA,CAAU,MAAM;AAId,IAAA,IAAI,UAAA,EAAY;AACd,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AACrB,MAAA;AAAA,IACF;AACA,IAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,IAAA,IAAI,EAAA,IAAM,UAAA,CAAW,OAAA,KAAY,YAAA,EAAc;AAC7C,MAAA,EAAA,CAAG,SAAA,GAAY,YAAA;AACf,MAAA,UAAA,CAAW,OAAA,GAAU,YAAA;AAAA,IACvB;AAAA,EACF,CAAA,EAAG,CAAC,UAAA,EAAY,YAAA,EAAc,UAAU,CAAC,CAAA;AAEzC,EAAA,MAAM,UAAA,GAAaC,aAAA;AAAA,IACjB,MAAMC,yCAAuB,UAAU,CAAA;AAAA,IACvC,CAAC,UAAU;AAAA,GACb;AAEA,EAAA,IAAI,UAAA,EAAY;AAOd,IAAA,uBACEP,cAAAA;AAAA,MAACQ,0BAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO,YAAA;AAAA,QACP,QAAA,EAAU,cAAA;AAAA,QACV,QAAA,EAAU,YAAA,KAAiB,MAAA,GAAS,MAAA,GAAS,WAAA;AAAA,QAC7C,WAAA;AAAA,QACA,SAAA;AAAA,QACA,SAAA,EAAWP,oBAAA,CAAG,mBAAA,EAAqB,eAAe;AAAA;AAAA,KACpD;AAAA,EAEJ;AAEA,EAAA,uBACED,cAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,UAAA;AAAA,MACL,eAAA,EAAe,IAAA;AAAA,MACf,8BAAA,EAA8B,IAAA;AAAA,MAC9B,iCAAA,EAAgC,EAAA;AAAA,MAChC,kBAAA,EAAkB,WAAA;AAAA,MAClB,OAAA,EAAS,QAAA;AAAA,MACT,KAAA,EAAO;AAAA,QACL,UAAA,EAAY,WAAA;AAAA,QACZ,SAAA,EAAW,SAAA,GAAY,CAAA,EAAG,SAAS,CAAA,EAAA,CAAA,GAAO;AAAA,OAC5C;AAAA,MACA,SAAA,EAAWC,oBAAA;AAAA,QACT,wEAAA;AAAA,QACA,oBAAA;AAAA,QACAQ,8BAAA;AAAA,QACA,UAAA;AAAA,QACA,2GAAA;AAAA,QACA;AAAA;AACF;AAAA,GACF;AAEJ;AAEA,aAAA,CAAc,WAAA,GAAc,eAAA;ACzE5B,SAAS,MAAA,CACP,KAAA,EACA,YAAA,EACA,MAAA,EACA,cAA4C,MAAA,EACpC;AACR,EAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AACnB,EAAA,MAAM,OAAO,MAAM;AAIjB,IAAA,MAAM,MAAA,GACJ,gBAAgB,MAAA,GACZ,WAAA,GACA,iBAAiB,MAAA,GACf,MAAA,GACAC,+BAAa,KAAK,CAAA;AAC1B,IAAA,IAAI,MAAA,KAAW,QAAQ,OAAO,KAAA;AAC9B,IAAA,OAAQC,aAAA,CAAO,MAAM,KAAA,EAAO,EAAE,OAAO,KAAA,EAAO,EAAa,IAAA,EAAK;AAAA,EAChE,CAAA,GAAG;AACH,EAAA,OAAO,MAAA,GAAS,GAAA,GAAMC,8BAAA,CAAa,GAAG,CAAA;AACxC;AAEA,SAAS,UAAA,CAAW;AAAA,EAClB,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA,EAAO,eAAA;AAAA,EACP,YAAA,GAAe,EAAA;AAAA,EACf,QAAA;AAAA,EACA,YAAA,GAAe,MAAA;AAAA,EACf,WAAA,GAAc,MAAA;AAAA,EACd,WAAA,GAAc,GAAA;AAAA,EACd,WAAA;AAAA,EACA,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY,kBAAA;AAAA,EACZ,MAAA,GAAS,KAAA;AAAA,EACT,UAAA,EAAY,cAAA;AAAA,EACZ,iBAAA,GAAoB,KAAA;AAAA,EACpB;AACF,CAAA,EAAgB;AACd,EAAA,MAAM,UAAA,GAAaR,aAA8B,IAAI,CAAA;AACrD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,IAAIS,sCAAA,CAAqB,eAAA,EAAiB,cAAc,QAAQ,CAAA;AACtF,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,sCAAA;AAAA,IAClC,cAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,IAAA,GAAOC,+BAAa,QAAQ,CAAA;AAClC,EAAA,MAAM,SAAS,IAAA,KAAS,MAAA;AAKxB,EAAA,MAAM,WAAA,GAAcV,aAAsB,IAAI,CAAA;AAO9C,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAIW,eAAS,CAAC,CAAA;AAClD,EAAAV,gBAAU,MAAM;AACd,IAAA,IAAI,UAAU,WAAA,CAAY,OAAA,kBAAyB,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA;AAAA,EACjE,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,EAAA,MAAM,WAAA,GAAcC,aAAAA;AAAA,IAClB,MAAM,MAAA,CAAO,KAAA,EAAO,YAAA,EAAc,QAAQ,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOrD,CAAC,MAAA,EAAQ,YAAA,EAAc,MAAA,EAAQ,WAAA,EAAa,YAAY,YAAY;AAAA,GACtE;AAEA,EAAA,MAAM,UAAA,GAAaA,aAAAA;AAAA,IACjB,MAAMU,kCAAgB,kBAAkB,CAAA;AAAA,IACxC,CAAC,kBAAkB;AAAA,GACrB;AAEA,EAAA,MAAM,cAAA,GAAiBC,iBAAA;AAAA,IACrB,CAAC,IAAA,KAAyB;AACxB,MAAA,OAAO,YAAA,KAAiB,UAAA,GAAaC,gCAAA,CAAe,IAAI,CAAA,GAAI,IAAA;AAAA,IAC9D,CAAA;AAAA,IACA,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,MAAM,WAAA,GAAcD,kBAAY,MAAM;AACpC,IAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,IAAA,IAAI,CAAC,EAAA,EAAI;AACT,IAAA,MAAM,IAAA,GAAO,cAAA,CAAe,EAAA,CAAG,SAAS,CAAA;AAGxC,IAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,IAAA,QAAA,CAAS,IAAI,CAAA;AAAA,EACf,CAAA,EAAG,CAAC,QAAA,EAAU,cAAc,CAAC,CAAA;AAE7B,EAAA,MAAM,IAAA,GAAOA,iBAAA;AAAA,IACX,CAAC,SAAiB,GAAA,KAAiB;AACjC,MAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,EAAA,CAAG,KAAA,EAAM;AACT,MAAA,QAAA,CAAS,WAAA,CAAY,OAAA,EAAS,KAAA,EAAO,GAAG,CAAA;AACxC,MAAA,WAAA,EAAY;AAAA,IACd,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,UAAA,GAAaA,iBAAA;AAAA,IACjB,CAAC,IAAA,KAAiB;AAChB,MAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,EAAA,CAAG,KAAA,EAAM;AACT,MAAA,QAAA,CAAS,WAAA,CAAY,YAAA,EAAc,KAAA,EAAO,IAAI,CAAA;AAC9C,MAAA,WAAA,EAAY;AAAA,IACd,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAIA,EAAA,MAAM,iBAAA,GAAoBA,iBAAA;AAAA,IACxB,CAAC,IAAA,KAAiB;AAGhB,MAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,MAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACf,CAAA;AAAA,IACA,CAAC,QAAQ;AAAA,GACX;AAEA,EAAA,MAAM,aAAA,GAAgBA,iBAAA;AAAA,IACpB,CAAC,QAAgB,KAAA,KAAkB;AACjC,MAAA,MAAM,GAAA,GAAM,OAAO,YAAA,EAAa;AAChC,MAAA,IAAI,CAAC,GAAA,IAAO,GAAA,CAAI,UAAA,KAAe,CAAA,EAAG;AAClC,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,UAAA,CAAW,CAAC,CAAA;AAC9B,MAAA,MAAM,YAAA,GAAe,MAAM,QAAA,EAAS;AACpC,MAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AACtB,MAAA,IAAI,CAAC,EAAA,EAAI;AACT,MAAA,EAAA,CAAG,KAAA,EAAM;AACT,MAAA,QAAA,CAAS,WAAA,CAAY,cAAc,KAAA,EAAO,CAAA,EAAG,MAAM,CAAA,EAAG,YAAY,CAAA,EAAG,KAAK,CAAA,CAAE,CAAA;AAC5E,MAAA,WAAA,EAAY;AAAA,IACd,CAAA;AAAA,IACA,CAAC,WAAW;AAAA,GACd;AAEA,EAAA,MAAM,YAAA,GAAe;AAAA,IACnB,UAAA;AAAA,IACA,IAAA;AAAA,IACA,UAAA;AAAA,IACA,aAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,UAAA;AAAA,IACA,aAAA;AAAA,IACA,YAAA,EAAc,WAAA;AAAA,IACd,QAAA,EAAU,WAAA;AAAA,IACV,YAAA,EAAc,KAAA;AAAA,IACd,cAAA,EAAgB;AAAA,GAClB;AAKA,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,uBACEjB,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,yBAAA,EAAwB,EAAA;AAAA,QACxB,WAAA,EAAU,MAAA;AAAA,QACV,SAAA,EAAWC,oBAAA;AAAA,UACT,kGAAA;AAAA,UACA;AAAA,SACF;AAAA,QAEA,QAAA,kBAAAD,cAAAA;AAAA,UAACmB,iCAAA;AAAA,UAAA;AAAA,YACC,KAAA;AAAA,YACA,MAAA,EAAQ,WAAA,KAAgB,MAAA,GAAS,WAAA,GAAc,YAAA;AAAA,YAC/C,WAAA;AAAA,YACA,UAAA,EAAY,kBAAA;AAAA,YACZ,MAAA;AAAA,YACA,SAAA,EAAU;AAAA;AAAA;AACZ;AAAA,KACF;AAAA,EAEJ;AAEA,EAAA,uBACEnB,cAAAA,CAAC,aAAA,CAAc,UAAd,EAAuB,KAAA,EAAO,cAC7B,QAAA,kBAAAA,cAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,yBAAA,EAAwB,EAAA;AAAA,MACxB,WAAA,EAAU,MAAA;AAAA,MACV,SAAA,EAAWC,oBAAA;AAAA,QACT,gHAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH,EACF,CAAA;AAEJ;AAEA,IAAM,oBAAA,GAAuB,MAAA,CAAO,MAAA,CAAO,aAAA,EAAe;AAAA,EACxD,SAAA,EAAW;AACb,CAAC,CAAA;AAEM,IAAM,MAAA,GAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY;AAAA,EAC9C,OAAA,EAAS,oBAAA;AAAA,EACT,OAAA,EAAS,aAAA;AAAA,EACT,YAAA,EAAc;AAChB,CAAC","file":"chunk-LQFQ2UKU.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-zinc-200 dark:bg-zinc-700\"\n : \"hover:bg-zinc-100 dark:hover:bg-zinc-800\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-zinc-200 px-2 py-1.5 dark:border-zinc-700\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-zinc-200 dark:bg-zinc-700\"\n : \"hover:bg-zinc-100 dark:hover:bg-zinc-800\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-zinc-200 dark:bg-zinc-700\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n } = useEditor();\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { htmlToMarkdown, detectFormat } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): string {\n if (!value) return \"\";\n const raw = (() => {\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return value;\n return (marked.parse(value, { async: false }) as string).trim();\n })();\n return unsafe ? raw : sanitizeHtml(raw);\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const initialHtml = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html) : html;\n },\n [outputFormat],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n className,\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n className=\"px-4 py-3\"\n />\n </div>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n className,\n )}\n >\n {children}\n </div>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n});\n"]}
1
+ {"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","detectFormat","marked","sanitizeHtml","useControllableState","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","ContentRenderer"],"mappings":"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","file":"chunk-WWUKTXPX.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-zinc-200 dark:bg-zinc-700\"\n : \"hover:bg-zinc-100 dark:hover:bg-zinc-800\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-zinc-200 px-2 py-1.5 dark:border-zinc-700\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-zinc-200 dark:bg-zinc-700\"\n : \"hover:bg-zinc-100 dark:hover:bg-zinc-800\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-zinc-200 dark:bg-zinc-700\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n } = useEditor();\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { htmlToMarkdown, detectFormat } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): string {\n if (!value) return \"\";\n const raw = (() => {\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return value;\n return (marked.parse(value, { async: false }) as string).trim();\n })();\n return unsafe ? raw : sanitizeHtml(raw);\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const initialHtml = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html) : html;\n },\n [outputFormat],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n className,\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n className=\"px-4 py-3\"\n />\n </div>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900\",\n className,\n )}\n >\n {children}\n </div>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n});\n"]}
@@ -0,0 +1,231 @@
1
+ import { cn } from './chunk-MMW2JGKZ.js';
2
+ import { useRef, useState, useCallback, useMemo, useEffect, useLayoutEffect } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ function VirtualList({
6
+ items,
7
+ renderRow,
8
+ height = "100%",
9
+ estimateRowHeight = 32,
10
+ overscan = 6,
11
+ followTail = false,
12
+ onFollowChange,
13
+ onUnseenChange,
14
+ scrollToId = null,
15
+ onScrollToIdResolved,
16
+ className,
17
+ ref,
18
+ ...props
19
+ }) {
20
+ const viewportRef = useRef(null);
21
+ const windowRef = useRef(null);
22
+ const heights = useRef(/* @__PURE__ */ new Map());
23
+ const [measured, setMeasured] = useState(0);
24
+ const [scrollTop, setScrollTop] = useState(0);
25
+ const [clientHeight, setClientHeight] = useState(0);
26
+ const attachViewport = useCallback(
27
+ (el) => {
28
+ viewportRef.current = el;
29
+ if (typeof ref === "function") ref(el);
30
+ else if (ref) ref.current = el;
31
+ },
32
+ [ref]
33
+ );
34
+ const viewport = clientHeight > 0 ? clientHeight : typeof height === "number" ? height : estimateRowHeight * 20;
35
+ const { offsets, total } = useMemo(() => {
36
+ const offs = new Float64Array(items.length + 1);
37
+ for (let i = 0; i < items.length; i += 1) {
38
+ offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);
39
+ }
40
+ return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };
41
+ }, [items, measured, estimateRowHeight]);
42
+ const indexAt = useCallback(
43
+ (y) => {
44
+ let lo = 0;
45
+ let hi = items.length - 1;
46
+ while (lo < hi) {
47
+ const mid = lo + hi + 1 >> 1;
48
+ if (offsets[mid] <= y) lo = mid;
49
+ else hi = mid - 1;
50
+ }
51
+ return Math.max(0, lo);
52
+ },
53
+ [items.length, offsets]
54
+ );
55
+ const start = Math.max(0, indexAt(scrollTop) - overscan);
56
+ const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);
57
+ const visible = useMemo(() => items.slice(start, end), [items, start, end]);
58
+ const observer = useRef(null);
59
+ useEffect(() => {
60
+ if (typeof ResizeObserver === "undefined") return;
61
+ const ro = new ResizeObserver(() => setMeasured((v) => v + 1));
62
+ observer.current = ro;
63
+ return () => {
64
+ ro.disconnect();
65
+ observer.current = null;
66
+ };
67
+ }, []);
68
+ useLayoutEffect(() => {
69
+ const win = windowRef.current;
70
+ if (!win) return;
71
+ let changed = false;
72
+ for (const el of Array.from(win.children)) {
73
+ const id = el.getAttribute(ROW_ID_ATTR);
74
+ if (id === null) continue;
75
+ observer.current?.observe(el);
76
+ const h = el.getBoundingClientRect().height;
77
+ if (h > 0 && heights.current.get(id) !== h) {
78
+ heights.current.set(id, h);
79
+ changed = true;
80
+ }
81
+ }
82
+ const vp = viewportRef.current;
83
+ if (vp && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) setClientHeight(vp.clientHeight);
84
+ if (changed) setMeasured((v) => v + 1);
85
+ });
86
+ const anchored = useRef(null);
87
+ const resolved = useRef(null);
88
+ useEffect(() => {
89
+ anchored.current = null;
90
+ resolved.current = null;
91
+ }, [scrollToId]);
92
+ useLayoutEffect(() => {
93
+ if (scrollToId === null || resolved.current === scrollToId) return;
94
+ const index = items.findIndex((item) => item.id === scrollToId);
95
+ if (index < 0) {
96
+ resolved.current = scrollToId;
97
+ onScrollToIdResolved?.(scrollToId, false);
98
+ return;
99
+ }
100
+ const vp = viewportRef.current;
101
+ if (!vp) return;
102
+ const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));
103
+ if (anchored.current !== target) {
104
+ anchored.current = target;
105
+ vp.scrollTop = target;
106
+ setScrollTop(target);
107
+ return;
108
+ }
109
+ if (Math.abs(vp.scrollTop - target) <= 1) {
110
+ resolved.current = scrollToId;
111
+ onScrollToIdResolved?.(scrollToId, true);
112
+ }
113
+ });
114
+ const reportedFollow = useRef(followTail);
115
+ const programmatic = useRef(false);
116
+ useEffect(() => {
117
+ reportedFollow.current = followTail;
118
+ }, [followTail]);
119
+ const askFollow = useCallback(
120
+ (next) => {
121
+ if (reportedFollow.current === next) return;
122
+ reportedFollow.current = next;
123
+ onFollowChange?.(next);
124
+ },
125
+ [onFollowChange]
126
+ );
127
+ useLayoutEffect(() => {
128
+ if (!followTail) return;
129
+ if (scrollToId !== null && resolved.current !== scrollToId) return;
130
+ const vp = viewportRef.current;
131
+ if (!vp) return;
132
+ const bottom = Math.max(0, total - viewport);
133
+ if (Math.abs(vp.scrollTop - bottom) > 0.5) {
134
+ programmatic.current = true;
135
+ vp.scrollTop = bottom;
136
+ setScrollTop(bottom);
137
+ }
138
+ });
139
+ const onScroll = useCallback(() => {
140
+ const vp = viewportRef.current;
141
+ if (!vp) return;
142
+ const next = vp.scrollTop;
143
+ const previous = scrollTop;
144
+ setScrollTop(next);
145
+ const bottom = Math.max(0, total - viewport);
146
+ const wasProgrammatic = programmatic.current;
147
+ programmatic.current = false;
148
+ if (next <= bottom - BOTTOM_SLACK) {
149
+ if (!wasProgrammatic && next < previous) askFollow(false);
150
+ } else {
151
+ askFollow(true);
152
+ }
153
+ }, [scrollTop, total, viewport, askFollow]);
154
+ const onWheel = useCallback(
155
+ (e) => {
156
+ if (e.deltaY < 0) askFollow(false);
157
+ },
158
+ [askFollow]
159
+ );
160
+ const onKeyDown = useCallback(
161
+ (e) => {
162
+ if (UPWARD_KEYS.has(e.key)) askFollow(false);
163
+ },
164
+ [askFollow]
165
+ );
166
+ const touchStart = useRef(null);
167
+ const onTouchStart = useCallback((e) => {
168
+ touchStart.current = e.touches[0]?.clientY ?? null;
169
+ }, []);
170
+ const onTouchMove = useCallback(
171
+ (e) => {
172
+ const from = touchStart.current;
173
+ const y = e.touches[0]?.clientY;
174
+ if (from !== null && y !== void 0 && y - from > TOUCH_SLOP) askFollow(false);
175
+ },
176
+ [askFollow]
177
+ );
178
+ const seen = useRef(items.length);
179
+ const reportedUnseen = useRef(null);
180
+ useEffect(() => {
181
+ const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);
182
+ if (followTail) seen.current = items.length;
183
+ if (reportedUnseen.current === unseen) return;
184
+ reportedUnseen.current = unseen;
185
+ onUnseenChange?.(unseen);
186
+ }, [items.length, followTail, onUnseenChange]);
187
+ return /* @__PURE__ */ jsx(
188
+ "div",
189
+ {
190
+ ref: attachViewport,
191
+ "data-react-fancy-virtual-list": "",
192
+ "data-react-fancy-virtual-list-following": followTail ? "true" : "false",
193
+ "data-react-fancy-virtual-list-rows": items.length,
194
+ role: "log",
195
+ tabIndex: 0,
196
+ className: cn("relative overflow-y-auto overflow-x-hidden outline-none", className),
197
+ style: { height },
198
+ onScroll,
199
+ onWheel,
200
+ onKeyDown,
201
+ onTouchStart,
202
+ onTouchMove,
203
+ ...props,
204
+ children: /* @__PURE__ */ jsx(
205
+ "div",
206
+ {
207
+ "data-react-fancy-virtual-list-sizer": "",
208
+ style: { height: `${total}px`, position: "relative" },
209
+ children: /* @__PURE__ */ jsx(
210
+ "div",
211
+ {
212
+ ref: windowRef,
213
+ "data-react-fancy-virtual-list-window": "",
214
+ "data-react-fancy-virtual-list-start": start,
215
+ style: { transform: `translateY(${offsets[start] ?? 0}px)` },
216
+ children: visible.map((item, i) => /* @__PURE__ */ jsx("div", { "data-react-fancy-virtual-row-id": item.id, children: renderRow(item, start + i) }, item.id))
217
+ }
218
+ )
219
+ }
220
+ )
221
+ }
222
+ );
223
+ }
224
+ var ROW_ID_ATTR = "data-react-fancy-virtual-row-id";
225
+ var BOTTOM_SLACK = 4;
226
+ var TOUCH_SLOP = 4;
227
+ var UPWARD_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "PageUp", "Home"]);
228
+
229
+ export { VirtualList };
230
+ //# sourceMappingURL=chunk-Z4CW3U6C.js.map
231
+ //# sourceMappingURL=chunk-Z4CW3U6C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/VirtualList/VirtualList.tsx"],"names":[],"mappings":";;;;AA4DO,SAAS,WAAA,CAAsC;AAAA,EACpD,KAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,iBAAA,GAAoB,EAAA;AAAA,EACpB,QAAA,GAAW,CAAA;AAAA,EACX,UAAA,GAAa,KAAA;AAAA,EACb,cAAA;AAAA,EACA,cAAA;AAAA,EACA,UAAA,GAAa,IAAA;AAAA,EACb,oBAAA;AAAA,EACA,SAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAwB;AACtB,EAAA,MAAM,WAAA,GAAc,OAA8B,IAAI,CAAA;AACtD,EAAA,MAAM,SAAA,GAAY,OAA8B,IAAI,CAAA;AAGpD,EAAA,MAAM,OAAA,GAAU,MAAA,iBAAO,IAAI,GAAA,EAAqB,CAAA;AAEhD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,CAAC,CAAA;AAE1C,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,CAAC,CAAA;AAC5C,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,CAAC,CAAA;AAElD,EAAA,MAAM,cAAA,GAAiB,WAAA;AAAA,IACrB,CAAC,EAAA,KAA8B;AAC7B,MAAA,WAAA,CAAY,OAAA,GAAU,EAAA;AACtB,MAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,GAAA,CAAI,EAAE,CAAA;AAAA,WAAA,IAC5B,GAAA,EAAM,GAAA,CAA2C,OAAA,GAAU,EAAA;AAAA,IACtE,CAAA;AAAA,IACA,CAAC,GAAG;AAAA,GACN;AAKA,EAAA,MAAM,QAAA,GACJ,eAAe,CAAA,GAAI,YAAA,GAAe,OAAO,MAAA,KAAW,QAAA,GAAW,SAAS,iBAAA,GAAoB,EAAA;AAQ9F,EAAA,MAAM,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,QAAQ,MAAM;AACvC,IAAA,MAAM,IAAA,GAAO,IAAI,YAAA,CAAa,KAAA,CAAM,SAAS,CAAC,CAAA;AAC9C,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,CAAA,EAAG;AACxC,MAAA,IAAA,CAAK,CAAA,GAAI,CAAC,CAAA,GAAI,IAAA,CAAK,CAAC,CAAA,IAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,CAAC,CAAA,CAAE,EAAE,CAAA,IAAK,iBAAA,CAAA;AAAA,IAC/D;AAEA,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,KAAA,EAAO,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA,EAAE;AAAA,EAC7E,CAAA,EAAG,CAAC,KAAA,EAAO,QAAA,EAAU,iBAAiB,CAAC,CAAA;AAGvC,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,CAAA,KAAsB;AACrB,MAAA,IAAI,EAAA,GAAK,CAAA;AACT,MAAA,IAAI,EAAA,GAAK,MAAM,MAAA,GAAS,CAAA;AACxB,MAAA,OAAO,KAAK,EAAA,EAAI;AACd,QAAA,MAAM,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,CAAA,IAAM,CAAA;AAC7B,QAAA,IAAI,OAAA,CAAQ,GAAG,CAAA,IAAK,CAAA,EAAG,EAAA,GAAK,GAAA;AAAA,kBAClB,GAAA,GAAM,CAAA;AAAA,MAClB;AAEA,MAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAE,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAC,KAAA,CAAM,MAAA,EAAQ,OAAO;AAAA,GACxB;AAEA,EAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,OAAA,CAAQ,SAAS,IAAI,QAAQ,CAAA;AACvD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,MAAA,EAAQ,QAAQ,SAAA,GAAY,QAAQ,CAAA,GAAI,CAAA,GAAI,QAAQ,CAAA;AAC/E,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,MAAM,KAAA,CAAM,KAAA,CAAM,KAAA,EAAO,GAAG,CAAA,EAAG,CAAC,KAAA,EAAO,KAAA,EAAO,GAAG,CAAC,CAAA;AAI1E,EAAA,MAAM,QAAA,GAAW,OAA8B,IAAI,CAAA;AAEnD,EAAA,SAAA,CAAU,MAAM;AAGd,IAAA,IAAI,OAAO,mBAAmB,WAAA,EAAa;AAE3C,IAAA,MAAM,EAAA,GAAK,IAAI,cAAA,CAAe,MAAM,YAAY,CAAC,CAAA,KAAM,CAAA,GAAI,CAAC,CAAC,CAAA;AAC7D,IAAA,QAAA,CAAS,OAAA,GAAU,EAAA;AAEnB,IAAA,OAAO,MAAM;AACX,MAAA,EAAA,CAAG,UAAA,EAAW;AACd,MAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,IACrB,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAKL,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,MAAM,SAAA,CAAU,OAAA;AACtB,IAAA,IAAI,CAAC,GAAA,EAAK;AAEV,IAAA,IAAI,OAAA,GAAU,KAAA;AACd,IAAA,KAAA,MAAW,EAAA,IAAM,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA,EAAoB;AAC1D,MAAA,MAAM,EAAA,GAAK,EAAA,CAAG,YAAA,CAAa,WAAW,CAAA;AACtC,MAAA,IAAI,OAAO,IAAA,EAAM;AAEjB,MAAA,QAAA,CAAS,OAAA,EAAS,QAAQ,EAAE,CAAA;AAE5B,MAAA,MAAM,CAAA,GAAI,EAAA,CAAG,qBAAA,EAAsB,CAAE,MAAA;AAGrC,MAAA,IAAI,IAAI,CAAA,IAAK,OAAA,CAAQ,QAAQ,GAAA,CAAI,EAAE,MAAM,CAAA,EAAG;AAC1C,QAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,CAAC,CAAA;AACzB,QAAA,OAAA,GAAU,IAAA;AAAA,MACZ;AAAA,IACF;AAEA,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,EAAA,IAAM,GAAG,YAAA,GAAe,CAAA,IAAK,GAAG,YAAA,KAAiB,YAAA,EAAc,eAAA,CAAgB,EAAA,CAAG,YAAY,CAAA;AAElG,IAAA,IAAI,OAAA,EAAS,WAAA,CAAY,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA;AAAA,EACvC,CAAC,CAAA;AAKD,EAAA,MAAM,QAAA,GAAW,OAAsB,IAAI,CAAA;AAC3C,EAAA,MAAM,QAAA,GAAW,OAAsB,IAAI,CAAA;AAE3C,EAAA,SAAA,CAAU,MAAM;AAEd,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AACnB,IAAA,QAAA,CAAS,OAAA,GAAU,IAAA;AAAA,EACrB,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,QAAA,CAAS,OAAA,KAAY,UAAA,EAAY;AAE5D,IAAA,MAAM,QAAQ,KAAA,CAAM,SAAA,CAAU,CAAC,IAAA,KAAS,IAAA,CAAK,OAAO,UAAU,CAAA;AAC9D,IAAA,IAAI,QAAQ,CAAA,EAAG;AAIb,MAAA,QAAA,CAAS,OAAA,GAAU,UAAA;AACnB,MAAA,oBAAA,GAAuB,YAAY,KAAK,CAAA;AAExC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AAKT,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,QAAQ,CAAC,CAAC,CAAA;AAElF,IAAA,IAAI,QAAA,CAAS,YAAY,MAAA,EAAQ;AAC/B,MAAA,QAAA,CAAS,OAAA,GAAU,MAAA;AACnB,MAAA,EAAA,CAAG,SAAA,GAAY,MAAA;AACf,MAAA,YAAA,CAAa,MAAM,CAAA;AAEnB,MAAA;AAAA,IACF;AAIA,IAAA,IAAI,KAAK,GAAA,CAAI,EAAA,CAAG,SAAA,GAAY,MAAM,KAAK,CAAA,EAAG;AACxC,MAAA,QAAA,CAAS,OAAA,GAAU,UAAA;AACnB,MAAA,oBAAA,GAAuB,YAAY,IAAI,CAAA;AAAA,IACzC;AAAA,EACF,CAAC,CAAA;AAID,EAAA,MAAM,cAAA,GAAiB,OAAO,UAAU,CAAA;AACxC,EAAA,MAAM,YAAA,GAAe,OAAO,KAAK,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AAId,IAAA,cAAA,CAAe,OAAA,GAAU,UAAA;AAAA,EAC3B,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,IAAA,KAAkB;AACjB,MAAA,IAAI,cAAA,CAAe,YAAY,IAAA,EAAM;AACrC,MAAA,cAAA,CAAe,OAAA,GAAU,IAAA;AACzB,MAAA,cAAA,GAAiB,IAAI,CAAA;AAAA,IACvB,CAAA;AAAA,IACA,CAAC,cAAc;AAAA,GACjB;AAEA,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,UAAA,EAAY;AAGjB,IAAA,IAAI,UAAA,KAAe,IAAA,IAAQ,QAAA,CAAS,OAAA,KAAY,UAAA,EAAY;AAE5D,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AAET,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,QAAQ,CAAA;AAC3C,IAAA,IAAI,KAAK,GAAA,CAAI,EAAA,CAAG,SAAA,GAAY,MAAM,IAAI,GAAA,EAAK;AACzC,MAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,MAAA,EAAA,CAAG,SAAA,GAAY,MAAA;AACf,MAAA,YAAA,CAAa,MAAM,CAAA;AAAA,IACrB;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,QAAA,GAAW,YAAY,MAAM;AACjC,IAAA,MAAM,KAAK,WAAA,CAAY,OAAA;AACvB,IAAA,IAAI,CAAC,EAAA,EAAI;AAET,IAAA,MAAM,OAAO,EAAA,CAAG,SAAA;AAChB,IAAA,MAAM,QAAA,GAAW,SAAA;AACjB,IAAA,YAAA,CAAa,IAAI,CAAA;AAEjB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,QAAQ,QAAQ,CAAA;AAC3C,IAAA,MAAM,kBAAkB,YAAA,CAAa,OAAA;AACrC,IAAA,YAAA,CAAa,OAAA,GAAU,KAAA;AAEvB,IAAA,IAAI,IAAA,IAAQ,SAAS,YAAA,EAAc;AAEjC,MAAA,IAAI,CAAC,eAAA,IAAmB,IAAA,GAAO,QAAA,YAAoB,KAAK,CAAA;AAAA,IAC1D,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,SAAA,EAAW,KAAA,EAAO,QAAA,EAAU,SAAS,CAAC,CAAA;AAG1C,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,CAAA,KAAkC;AACjC,MAAA,IAAI,CAAA,CAAE,MAAA,GAAS,CAAA,EAAG,SAAA,CAAU,KAAK,CAAA;AAAA,IACnC,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,SAAA,GAAY,WAAA;AAAA,IAChB,CAAC,CAAA,KAAqC;AACpC,MAAA,IAAI,YAAY,GAAA,CAAI,CAAA,CAAE,GAAG,CAAA,YAAa,KAAK,CAAA;AAAA,IAC7C,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAEA,EAAA,MAAM,UAAA,GAAa,OAAsB,IAAI,CAAA;AAE7C,EAAA,MAAM,YAAA,GAAe,WAAA,CAAY,CAAC,CAAA,KAAkC;AAClE,IAAA,UAAA,CAAW,OAAA,GAAU,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAG,OAAA,IAAW,IAAA;AAAA,EAChD,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,CAAA,KAAkC;AACjC,MAAA,MAAM,OAAO,UAAA,CAAW,OAAA;AACxB,MAAA,MAAM,CAAA,GAAI,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA,EAAG,OAAA;AAExB,MAAA,IAAI,IAAA,KAAS,QAAQ,CAAA,KAAM,MAAA,IAAa,IAAI,IAAA,GAAO,UAAA,YAAsB,KAAK,CAAA;AAAA,IAChF,CAAA;AAAA,IACA,CAAC,SAAS;AAAA,GACZ;AAIA,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,KAAA,CAAM,MAAM,CAAA;AAChC,EAAA,MAAM,cAAA,GAAiB,OAAsB,IAAI,CAAA;AAEjD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,MAAA,GAAS,aAAa,CAAA,GAAI,IAAA,CAAK,IAAI,CAAA,EAAG,KAAA,CAAM,MAAA,GAAS,IAAA,CAAK,OAAO,CAAA;AACvE,IAAA,IAAI,UAAA,EAAY,IAAA,CAAK,OAAA,GAAU,KAAA,CAAM,MAAA;AAErC,IAAA,IAAI,cAAA,CAAe,YAAY,MAAA,EAAQ;AACvC,IAAA,cAAA,CAAe,OAAA,GAAU,MAAA;AACzB,IAAA,cAAA,GAAiB,MAAM,CAAA;AAAA,EACzB,GAAG,CAAC,KAAA,CAAM,MAAA,EAAQ,UAAA,EAAY,cAAc,CAAC,CAAA;AAI7C,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,cAAA;AAAA,MACL,+BAAA,EAA8B,EAAA;AAAA,MAC9B,yCAAA,EAAyC,aAAa,MAAA,GAAS,OAAA;AAAA,MAC/D,sCAAoC,KAAA,CAAM,MAAA;AAAA,MAC1C,IAAA,EAAK,KAAA;AAAA,MACL,QAAA,EAAU,CAAA;AAAA,MACV,SAAA,EAAW,EAAA,CAAG,yDAAA,EAA2D,SAAS,CAAA;AAAA,MAClF,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,MAChB,QAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,YAAA;AAAA,MACA,WAAA;AAAA,MACC,GAAG,KAAA;AAAA,MAIJ,QAAA,kBAAA,GAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,qCAAA,EAAoC,EAAA;AAAA,UACpC,OAAO,EAAE,MAAA,EAAQ,GAAG,KAAK,CAAA,EAAA,CAAA,EAAM,UAAU,UAAA,EAAW;AAAA,UAEpD,QAAA,kBAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,SAAA;AAAA,cACL,sCAAA,EAAqC,EAAA;AAAA,cACrC,qCAAA,EAAqC,KAAA;AAAA,cACrC,KAAA,EAAO,EAAE,SAAA,EAAW,CAAA,WAAA,EAAc,QAAQ,KAAK,CAAA,IAAK,CAAC,CAAA,GAAA,CAAA,EAAM;AAAA,cAE1D,kBAAQ,GAAA,CAAI,CAAC,IAAA,EAAM,CAAA,yBACjB,KAAA,EAAA,EAAkB,iCAAA,EAAiC,IAAA,CAAK,EAAA,EACtD,oBAAU,IAAA,EAAM,KAAA,GAAQ,CAAC,CAAA,EAAA,EADlB,IAAA,CAAK,EAEf,CACD;AAAA;AAAA;AACH;AAAA;AACF;AAAA,GACF;AAEJ;AAEA,IAAM,WAAA,GAAc,iCAAA;AAEpB,IAAM,YAAA,GAAe,CAAA;AAErB,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,8BAAc,IAAI,GAAA,CAAI,CAAC,SAAA,EAAW,QAAA,EAAU,MAAM,CAAC,CAAA","file":"chunk-Z4CW3U6C.js","sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from \"react\";\nimport type { KeyboardEvent, TouchEvent, WheelEvent } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { VirtualListProps, VirtualListRow } from \"./VirtualList.types\";\n\n/**\n * A windowed list for ledgers that run to tens of thousands of rows — only the\n * visible slice is in the DOM, with **measured** variable row heights, deep\n * links by row id, and sticky-bottom tail-following.\n *\n * Built for an agent event stream, where every one of those three is load\n * bearing at once: message rows wrap and event rows do not, a link carries\n * `?at=<rowId>` to the exact row under discussion, and new rows arrive while\n * the human is reading older ones.\n *\n * ## Heights are measured, never predicted\n *\n * `estimateRowHeight` only seeds rows that have never been on screen. The\n * moment a row renders, its real height is measured and replaces the estimate,\n * and every offset after it moves. The alternative — a `rowHeight(item)`\n * callback — is a prediction about text wrapping, font loading and container\n * width, and when it is wrong the error accumulates down the list: rows draw in\n * the wrong place and `scrollToId` lands on a neighbour. Measuring is slower to\n * settle and correct when it does.\n *\n * ## Tail-follow breaks on INTENT, not on position\n *\n * Any upward gesture — wheel, drag, PageUp, Home — stops following\n * immediately, even when the list is still pinned to the bottom and\n * `scrollTop` has not changed yet. Position cannot tell \"the human scrolled\n * up\" from \"content grew downward\", so a position-only implementation keeps\n * following through a wheel-up and yanks the reader back on the next append.\n *\n * ## It is controlled\n *\n * `followTail` is the consumer's state. This component only ever *asks* via\n * `onFollowChange`, so the follow state is readable and writable by an agent\n * over a bridge, as the Human+ component contract requires. The `↓ n new` pill\n * is the consumer's to render, from `onUnseenChange`.\n *\n * @example\n * ```tsx\n * const [following, setFollowing] = useState(true);\n * const [unseen, setUnseen] = useState(0);\n *\n * <VirtualList\n * items={events}\n * renderRow={(e) => <StreamRow event={e} />}\n * height=\"100%\"\n * estimateRowHeight={28}\n * followTail={following}\n * onFollowChange={setFollowing}\n * onUnseenChange={setUnseen}\n * scrollToId={params.at}\n * />\n * {!following && unseen > 0 && (\n * <Button onClick={() => setFollowing(true)}>↓ {unseen} new</Button>\n * )}\n * ```\n */\nexport function VirtualList<T extends VirtualListRow>({\n items,\n renderRow,\n height = \"100%\",\n estimateRowHeight = 32,\n overscan = 6,\n followTail = false,\n onFollowChange,\n onUnseenChange,\n scrollToId = null,\n onScrollToIdResolved,\n className,\n ref,\n ...props\n}: VirtualListProps<T>) {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const windowRef = useRef<HTMLDivElement | null>(null);\n\n /** Measured heights by row id, so a height follows its row across a reindex. */\n const heights = useRef(new Map<string, number>());\n /** Bumped when a measurement actually changed, to recompute offsets. */\n const [measured, setMeasured] = useState(0);\n\n const [scrollTop, setScrollTop] = useState(0);\n const [clientHeight, setClientHeight] = useState(0);\n\n const attachViewport = useCallback(\n (el: HTMLDivElement | null) => {\n viewportRef.current = el;\n if (typeof ref === \"function\") ref(el);\n else if (ref) (ref as { current: HTMLDivElement | null }).current = el;\n },\n [ref],\n );\n\n // A number `height` IS the viewport height, and is usable before the element\n // has been measured. Without one, render a conservative first window so there\n // is something on screen for the measurement pass to work from.\n const viewport =\n clientHeight > 0 ? clientHeight : typeof height === \"number\" ? height : estimateRowHeight * 20;\n\n /**\n * Prefix sums of row heights: `offsets[i]` is where row `i` starts, and the\n * last entry is the total. Rebuilt whenever rows or measurements change —\n * O(n) once per change, against O(log n) per scroll from the binary search,\n * rather than O(n) on every scroll frame.\n */\n const { offsets, total } = useMemo(() => {\n const offs = new Float64Array(items.length + 1);\n for (let i = 0; i < items.length; i += 1) {\n offs[i + 1] = offs[i] + (heights.current.get(items[i].id) ?? estimateRowHeight);\n }\n\n return { offsets: offs, total: items.length === 0 ? 0 : offs[items.length] };\n }, [items, measured, estimateRowHeight]);\n\n /** First row whose span contains `y`. */\n const indexAt = useCallback(\n (y: number): number => {\n let lo = 0;\n let hi = items.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (offsets[mid] <= y) lo = mid;\n else hi = mid - 1;\n }\n\n return Math.max(0, lo);\n },\n [items.length, offsets],\n );\n\n const start = Math.max(0, indexAt(scrollTop) - overscan);\n const end = Math.min(items.length, indexAt(scrollTop + viewport) + 1 + overscan);\n const visible = useMemo(() => items.slice(start, end), [items, start, end]);\n\n // ---------------------------------------------------------------- measuring\n\n const observer = useRef<ResizeObserver | null>(null);\n\n useEffect(() => {\n // Absent in jsdom and in older runtimes. Without it the list still measures\n // on every render; it just does not notice a row reflowing in place.\n if (typeof ResizeObserver === \"undefined\") return;\n\n const ro = new ResizeObserver(() => setMeasured((v) => v + 1));\n observer.current = ro;\n\n return () => {\n ro.disconnect();\n observer.current = null;\n };\n }, []);\n\n // No dependency array on purpose: every render re-measures what is on screen,\n // and the `changed` guard is what stops it looping. A render that learns\n // nothing new sets no state.\n useLayoutEffect(() => {\n const win = windowRef.current;\n if (!win) return;\n\n let changed = false;\n for (const el of Array.from(win.children) as HTMLElement[]) {\n const id = el.getAttribute(ROW_ID_ATTR);\n if (id === null) continue;\n\n observer.current?.observe(el);\n\n const h = el.getBoundingClientRect().height;\n // A zero height is \"not laid out yet\", not a 0px row. Recording it would\n // collapse the list and make every offset after it wrong.\n if (h > 0 && heights.current.get(id) !== h) {\n heights.current.set(id, h);\n changed = true;\n }\n }\n\n const vp = viewportRef.current;\n if (vp && vp.clientHeight > 0 && vp.clientHeight !== clientHeight) setClientHeight(vp.clientHeight);\n\n if (changed) setMeasured((v) => v + 1);\n });\n\n // ------------------------------------------------------------- deep linking\n\n /** The target this component last moved to, to detect that it has settled. */\n const anchored = useRef<number | null>(null);\n const resolved = useRef<string | null>(null);\n\n useEffect(() => {\n // A new id is a new request, even if the old one had resolved.\n anchored.current = null;\n resolved.current = null;\n }, [scrollToId]);\n\n useLayoutEffect(() => {\n if (scrollToId === null || resolved.current === scrollToId) return;\n\n const index = items.findIndex((item) => item.id === scrollToId);\n if (index < 0) {\n // A link to a row that is no longer in the list. Leaving the viewport\n // where it is and SAYING SO is the only honest outcome — scrolling to the\n // top would be indistinguishable from a successful landing.\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, false);\n\n return;\n }\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n // Clamped the way the browser clamps: a row inside the final viewport-height\n // of the list cannot be brought to the top, and pretending otherwise would\n // leave this waiting for a position that can never be reached.\n const target = Math.max(0, Math.min(offsets[index], Math.max(0, total - viewport)));\n\n if (anchored.current !== target) {\n anchored.current = target;\n vp.scrollTop = target;\n setScrollTop(target);\n\n return;\n }\n\n // The target stopped moving as rows above it were measured, and we are on\n // it. Only now is the deep link actually honoured.\n if (Math.abs(vp.scrollTop - target) <= 1) {\n resolved.current = scrollToId;\n onScrollToIdResolved?.(scrollToId, true);\n }\n });\n\n // ------------------------------------------------------------- tail-follow\n\n const reportedFollow = useRef(followTail);\n const programmatic = useRef(false);\n\n useEffect(() => {\n // The prop is the truth, so adopt it rather than remembering what we asked\n // for. Otherwise a consumer that declines an `onFollowChange(false)` would\n // never be asked again.\n reportedFollow.current = followTail;\n }, [followTail]);\n\n const askFollow = useCallback(\n (next: boolean) => {\n if (reportedFollow.current === next) return;\n reportedFollow.current = next;\n onFollowChange?.(next);\n },\n [onFollowChange],\n );\n\n useLayoutEffect(() => {\n if (!followTail) return;\n // A pending deep link wins while it resolves; otherwise the two would fight\n // over the same scrollTop and the link would lose.\n if (scrollToId !== null && resolved.current !== scrollToId) return;\n\n const vp = viewportRef.current;\n if (!vp) return;\n\n const bottom = Math.max(0, total - viewport);\n if (Math.abs(vp.scrollTop - bottom) > 0.5) {\n programmatic.current = true;\n vp.scrollTop = bottom;\n setScrollTop(bottom);\n }\n });\n\n const onScroll = useCallback(() => {\n const vp = viewportRef.current;\n if (!vp) return;\n\n const next = vp.scrollTop;\n const previous = scrollTop;\n setScrollTop(next);\n\n const bottom = Math.max(0, total - viewport);\n const wasProgrammatic = programmatic.current;\n programmatic.current = false;\n\n if (next <= bottom - BOTTOM_SLACK) {\n // Moved up, and not because we moved it.\n if (!wasProgrammatic && next < previous) askFollow(false);\n } else {\n askFollow(true);\n }\n }, [scrollTop, total, viewport, askFollow]);\n\n /** Any upward gesture is a statement of intent, whatever scrollTop says. */\n const onWheel = useCallback(\n (e: WheelEvent<HTMLDivElement>) => {\n if (e.deltaY < 0) askFollow(false);\n },\n [askFollow],\n );\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (UPWARD_KEYS.has(e.key)) askFollow(false);\n },\n [askFollow],\n );\n\n const touchStart = useRef<number | null>(null);\n\n const onTouchStart = useCallback((e: TouchEvent<HTMLDivElement>) => {\n touchStart.current = e.touches[0]?.clientY ?? null;\n }, []);\n\n const onTouchMove = useCallback(\n (e: TouchEvent<HTMLDivElement>) => {\n const from = touchStart.current;\n const y = e.touches[0]?.clientY;\n // Finger travelling DOWN drags the content down, which scrolls up.\n if (from !== null && y !== undefined && y - from > TOUCH_SLOP) askFollow(false);\n },\n [askFollow],\n );\n\n // ------------------------------------------------------------ unseen count\n\n const seen = useRef(items.length);\n const reportedUnseen = useRef<number | null>(null);\n\n useEffect(() => {\n const unseen = followTail ? 0 : Math.max(0, items.length - seen.current);\n if (followTail) seen.current = items.length;\n\n if (reportedUnseen.current === unseen) return;\n reportedUnseen.current = unseen;\n onUnseenChange?.(unseen);\n }, [items.length, followTail, onUnseenChange]);\n\n // ------------------------------------------------------------------ render\n\n return (\n <div\n ref={attachViewport}\n data-react-fancy-virtual-list=\"\"\n data-react-fancy-virtual-list-following={followTail ? \"true\" : \"false\"}\n data-react-fancy-virtual-list-rows={items.length}\n role=\"log\"\n tabIndex={0}\n className={cn(\"relative overflow-y-auto overflow-x-hidden outline-none\", className)}\n style={{ height }}\n onScroll={onScroll}\n onWheel={onWheel}\n onKeyDown={onKeyDown}\n onTouchStart={onTouchStart}\n onTouchMove={onTouchMove}\n {...props}\n >\n {/* Holds the full scroll range so the scrollbar is honest from the first\n paint, instead of growing as rows are discovered. */}\n <div\n data-react-fancy-virtual-list-sizer=\"\"\n style={{ height: `${total}px`, position: \"relative\" }}\n >\n <div\n ref={windowRef}\n data-react-fancy-virtual-list-window=\"\"\n data-react-fancy-virtual-list-start={start}\n style={{ transform: `translateY(${offsets[start] ?? 0}px)` }}\n >\n {visible.map((item, i) => (\n <div key={item.id} data-react-fancy-virtual-row-id={item.id}>\n {renderRow(item, start + i)}\n </div>\n ))}\n </div>\n </div>\n </div>\n );\n}\n\nconst ROW_ID_ATTR = \"data-react-fancy-virtual-row-id\";\n/** Within this many px of the bottom counts as \"at the bottom\". */\nconst BOTTOM_SLACK = 4;\n/** Ignore a touch jitter smaller than this before calling it an upward drag. */\nconst TOUCH_SLOP = 4;\nconst UPWARD_KEYS = new Set([\"ArrowUp\", \"PageUp\", \"Home\"]);\n"]}
package/dist/editor.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var chunkLQFQ2UKU_cjs = require('./chunk-LQFQ2UKU.cjs');
3
+ var chunkWWUKTXPX_cjs = require('./chunk-WWUKTXPX.cjs');
4
4
  require('./chunk-HPNIEYSJ.cjs');
5
5
  require('./chunk-T6YELZPJ.cjs');
6
+ require('./chunk-ND5D7GJB.cjs');
6
7
  require('./chunk-G7227RTY.cjs');
7
8
  require('./chunk-TXTRTTG7.cjs');
8
9
  require('./chunk-3WGRVDQR.cjs');
9
- require('./chunk-ND5D7GJB.cjs');
10
10
  require('./chunk-BFKCVSNK.cjs');
11
11
  require('./chunk-J2DI6RMR.cjs');
12
12
  require('./chunk-L3V3BYVS.cjs');
@@ -19,11 +19,11 @@ require('./chunk-ZU5Q77OT.cjs');
19
19
 
20
20
  Object.defineProperty(exports, "Editor", {
21
21
  enumerable: true,
22
- get: function () { return chunkLQFQ2UKU_cjs.Editor; }
22
+ get: function () { return chunkWWUKTXPX_cjs.Editor; }
23
23
  });
24
24
  Object.defineProperty(exports, "useEditor", {
25
25
  enumerable: true,
26
- get: function () { return chunkLQFQ2UKU_cjs.useEditor; }
26
+ get: function () { return chunkWWUKTXPX_cjs.useEditor; }
27
27
  });
28
28
  //# sourceMappingURL=editor.cjs.map
29
29
  //# sourceMappingURL=editor.cjs.map
package/dist/editor.js CHANGED
@@ -1,10 +1,10 @@
1
- export { Editor, useEditor } from './chunk-EIUS2K72.js';
1
+ export { Editor, useEditor } from './chunk-TX6VUYBC.js';
2
2
  import './chunk-FWCREAIT.js';
3
3
  import './chunk-CPY7NHXM.js';
4
+ import './chunk-25ZOOUFG.js';
4
5
  import './chunk-FBWUXSKH.js';
5
6
  import './chunk-5U2DZ7AV.js';
6
7
  import './chunk-CHADHN6M.js';
7
- import './chunk-25ZOOUFG.js';
8
8
  import './chunk-XJXQG34J.js';
9
9
  import './chunk-MQ6VGG62.js';
10
10
  import './chunk-NRXK42LO.js';
@@ -0,0 +1,157 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ /**
5
+ * Which modifier key the user actually has.
6
+ *
7
+ * This exists because a keyboard hint is a CLAIM about the reader's hardware.
8
+ * `⌘ + Enter to send` on Windows or Linux names a key that is not on the
9
+ * keyboard, and a reader who cannot act on a hint learns to ignore hints — so
10
+ * the cost is not one wrong glyph, it is the credibility of every hint beside
11
+ * it. (Reported from a real Electron app running on Linux, where the glyph had
12
+ * no font behind it either and rendered as tofu.)
13
+ *
14
+ * Two deliberate choices:
15
+ *
16
+ * 1. **The platform is an ARGUMENT, never a global read.** `detectPlatform`
17
+ * takes the hints; `modifierKeyLabel` takes the verdict. Both branches are
18
+ * therefore reachable from one machine, which is the only way a test can
19
+ * cover the platform you are not sitting at. A function that reads
20
+ * `navigator` internally can only ever be tested on the developer's own OS.
21
+ * 2. **`generic` is the fallback, and that is not arbitrary.** Every Fancy
22
+ * surface that submits on a modifier accepts `metaKey || ctrlKey`, so
23
+ * `Ctrl` is a TRUE label on an Apple keyboard while `⌘` is a FALSE one
24
+ * everywhere else. The honest default is the one that is pressable on all
25
+ * of them.
26
+ */
27
+ type FancyPlatform = "apple" | "generic";
28
+ /**
29
+ * The parts of `navigator` the detection reads — nothing more, so a caller can
30
+ * hand it a literal (a test, an Electron main process forwarding `process.platform`,
31
+ * a server with a stored preference).
32
+ */
33
+ interface PlatformHints {
34
+ /** `navigator.userAgentData` — the modern, non-spoofed answer where it exists. */
35
+ userAgentData?: {
36
+ platform?: string;
37
+ } | null;
38
+ /** `navigator.platform` — deprecated, still the most reliable legacy signal. */
39
+ platform?: string | null;
40
+ /** `navigator.userAgent` — last resort. */
41
+ userAgent?: string | null;
42
+ }
43
+ /** Resolve a platform from navigator-shaped hints. Unknown or absent ⇒ `generic`. */
44
+ declare function detectPlatform(hints?: PlatformHints | null): FancyPlatform;
45
+ /**
46
+ * The modifier key to NAME for a platform.
47
+ *
48
+ * `⌘` is emitted for `apple` and nothing else. A test enumerates the platform
49
+ * values and counts that no other one can produce it, because a hint that names
50
+ * the wrong key is worse than no hint.
51
+ */
52
+ declare function modifierKeyLabel(platform: FancyPlatform): "⌘" | "Ctrl";
53
+ /** SSR-safe read of the live browser hints. `null` off a browser. */
54
+ declare function browserPlatformHints(): PlatformHints | null;
55
+
56
+ /**
57
+ * PromptInput — the chat composer every AI app rebuilds. Auto-growing
58
+ * multi-line input with:
59
+ *
60
+ * • slash-command picker (`/` triggers a filtered command palette,
61
+ * ↑/↓ navigate, Enter inserts)
62
+ * • mention picker (`@` triggers, filtered against `mentions`)
63
+ * • drop-to-attach (drag files anywhere on the surface), paste-to-attach
64
+ * (a pasted screenshot becomes an attachment) + chip bar
65
+ * • submit on ⌘/Ctrl+Enter (plain Enter inserts a newline)
66
+ * • live token-budget meter (green → amber → red as headroom drops)
67
+ *
68
+ * Wire it up:
69
+ *
70
+ * <PromptInput
71
+ * budgetTokens={32000}
72
+ * commands={[{ name: "/rewrite", hint: "rewrite the selection" }]}
73
+ * mentions={[{ id: "ada", name: "Ada", kind: "person" }]}
74
+ * onSubmit={(text, attachments) => sendToAgent(text, attachments)}
75
+ * />
76
+ */
77
+ type PromptCmd = {
78
+ name: string;
79
+ hint: string;
80
+ };
81
+ type PromptMention = {
82
+ id: string;
83
+ name: string;
84
+ kind: "agent" | "file" | "person" | string;
85
+ };
86
+ type PromptAttachment = {
87
+ id: string;
88
+ name: string;
89
+ bytes: number;
90
+ /**
91
+ * The file the user actually attached.
92
+ *
93
+ * Optional because an attachment restored from a server has no File — but for
94
+ * anything the user dropped or picked it is always present. Without it a host
95
+ * can render a chip and nothing else: no POST, no FormData, no send-to-model.
96
+ */
97
+ file?: File;
98
+ /** MIME type. Hosts branch on this to decide whether a file can be sent as-is. */
99
+ type?: string;
100
+ };
101
+ /**
102
+ * `...rest` reaches the root surface, so a consumer or a bridge can tag the
103
+ * composer with its own `data-*` handle. The component named every prop it
104
+ * used and had no rest spread, which means a `data-handle` put on it was
105
+ * DROPPED — silently, since React discards unknown props and the component
106
+ * still renders perfectly. That is issue #22 all over again, and the contract
107
+ * calls it out: "Each interactive element has a stable identity. Agents never
108
+ * guess DOM."
109
+ */
110
+ interface PromptInputProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSubmit" | "className"> {
111
+ /** Extra classes on the root surface. MERGED, never replaced. */
112
+ className?: string;
113
+ /** Token budget for the meter. */
114
+ budgetTokens: number;
115
+ /** Slash-commands. Names must start with `/`. */
116
+ commands?: PromptCmd[];
117
+ /** @-mentions. */
118
+ mentions?: PromptMention[];
119
+ /** Show the keyboard hint ("Ctrl + Enter to send", "⌘ + Enter" on a Mac). */
120
+ showHint?: boolean;
121
+ /**
122
+ * Which modifier the hint and the default placeholder NAME. Omit it and the
123
+ * component reads the platform after mount, which is the right answer for an
124
+ * ordinary web app.
125
+ *
126
+ * Pass it when the host knows better than `navigator` does — an Electron
127
+ * renderer forwarding `process.platform`, a remote session where the
128
+ * keyboard is not the one running the browser, or a test that needs the
129
+ * branch it is not sitting on. The pressed keys are unaffected either way:
130
+ * submit is `⌘/Ctrl+Enter` on every platform, so this only decides which
131
+ * true key gets named.
132
+ */
133
+ platform?: FancyPlatform;
134
+ /** Called on ⌘/Ctrl+Enter or send button. */
135
+ onSubmit: (text: string, attachments: PromptAttachment[]) => void;
136
+ /**
137
+ * Placeholder text. Defaults to a hint naming the modifier THIS platform
138
+ * actually has, so leaving it unset is the better option — a hardcoded
139
+ * placeholder is the other place a `⌘` ends up in front of a Windows user.
140
+ */
141
+ placeholder?: string;
142
+ /** Rough estimator: chars-per-token. Defaults to 4. */
143
+ charsPerToken?: number;
144
+ /** Color → CSS chip mapping for mention kinds. */
145
+ mentionColor?: Record<string, string>;
146
+ /** Optional max textarea height in px. Defaults to 280. */
147
+ maxHeight?: number;
148
+ /**
149
+ * Rendered INSIDE the rounded shell, ABOVE the attachments bar and
150
+ * textarea. Use this slot for a drawer of tools/files/prompts/etc. so
151
+ * the drawer and composer share one visual panel. See {@link ChatDrawer}.
152
+ */
153
+ aboveInput?: ReactNode;
154
+ }
155
+ declare function PromptInput({ budgetTokens, commands, mentions, showHint, platform, onSubmit, placeholder, charsPerToken, mentionColor, maxHeight, aboveInput, className, ...rest }: PromptInputProps): react.JSX.Element;
156
+
157
+ export { type FancyPlatform as F, type PlatformHints as P, type PromptAttachment as a, type PromptCmd as b, PromptInput as c, type PromptInputProps as d, type PromptMention as e, browserPlatformHints as f, detectPlatform as g, modifierKeyLabel as m };