cubevis 1.0.19__tar.gz → 1.0.28__tar.gz

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 (164) hide show
  1. {cubevis-1.0.19 → cubevis-1.0.28}/PKG-INFO +1 -1
  2. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__js__/bokeh-3.8/cubevisjs.min.js +3 -3
  3. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/__init__.py +1 -0
  4. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_bokeh_app_context.py +48 -1
  5. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_showable.py +92 -60
  6. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/_initialize.py +7 -0
  7. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_createmask.py +3 -9
  8. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_createregion.py +3 -9
  9. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_interactiveclean.mustache +2 -1
  10. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_interactiveclean.py +2 -1
  11. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_interactivecleannotebook.mustache +24 -1
  12. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_interactivecleannotebook.py +24 -1
  13. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/__init__.py +0 -1
  14. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/_cube.py +17 -11
  15. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/_interactive_clean_ui.mustache +22 -7
  16. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/_interactive_clean_ui.py +22 -7
  17. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/__init__.py +2 -1
  18. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_jupyter.py +35 -12
  19. cubevis-1.0.28/cubevis/utils/_mutual_exclusion.py +117 -0
  20. {cubevis-1.0.19 → cubevis-1.0.28}/pyproject.toml +1 -1
  21. {cubevis-1.0.19 → cubevis-1.0.28}/LICENSE +0 -0
  22. {cubevis-1.0.19 → cubevis-1.0.28}/README.rst +0 -0
  23. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/LICENSE.rst +0 -0
  24. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/20px/fast-backward.svg +0 -0
  25. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/20px/fast-forward.svg +0 -0
  26. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/20px/step-backward.svg +0 -0
  27. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/20px/step-forward.svg +0 -0
  28. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/add-chan.png +0 -0
  29. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/add-chan.svg +0 -0
  30. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/add-cube.png +0 -0
  31. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/add-cube.svg +0 -0
  32. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/drag.png +0 -0
  33. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/drag.svg +0 -0
  34. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/mask-selected.png +0 -0
  35. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/mask.png +0 -0
  36. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/mask.svg +0 -0
  37. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/new-layer-sm-selected.png +0 -0
  38. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/new-layer-sm-selected.svg +0 -0
  39. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/new-layer-sm.png +0 -0
  40. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/new-layer-sm.svg +0 -0
  41. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/reset.png +0 -0
  42. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/reset.svg +0 -0
  43. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/sub-chan.png +0 -0
  44. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/sub-chan.svg +0 -0
  45. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/sub-cube.png +0 -0
  46. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/sub-cube.svg +0 -0
  47. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/trash.png +0 -0
  48. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/trash.svg +0 -0
  49. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/trash_full.png +0 -0
  50. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/trash_full.svg +0 -0
  51. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/trash_full_raw.png +0 -0
  52. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/zoom-to-fit.png +0 -0
  53. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__icons__/zoom-to-fit.svg +0 -0
  54. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__init__.py +0 -0
  55. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__js__/bokeh-3.6/cubevisjs.min.js +0 -0
  56. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__js__/bokeh-3.7/cubevisjs.min.js +0 -0
  57. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/__js__/casalib.min.js +0 -0
  58. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/annotations/__init__.py +0 -0
  59. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/annotations/_ev_poly_annotation.py +0 -0
  60. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/components/__init__.py +0 -0
  61. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/format/__init__.py +0 -0
  62. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/format/_time_ticks.py +0 -0
  63. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/format/_wcs_ticks.py +0 -0
  64. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/__init__.py +0 -0
  65. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_edit_span.py +0 -0
  66. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_ev_text_input.py +0 -0
  67. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_shared_dict.py +0 -0
  68. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_tip.py +0 -0
  69. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/models/_tip_button.py +0 -0
  70. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/sources/__init__.py +0 -0
  71. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/sources/_data_pipe.py +0 -0
  72. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/sources/_image_data_source.py +0 -0
  73. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/sources/_image_pipe.py +0 -0
  74. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/sources/_spectra_data_source.py +0 -0
  75. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/sources/_updatable_data_source.py +0 -0
  76. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/__init__.py +0 -0
  77. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/_current.py +0 -0
  78. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/_javascript.py +0 -0
  79. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/_palette.py +0 -0
  80. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/_session.py +0 -0
  81. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/bokeh-2.4.1.min.js +0 -0
  82. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/bokeh-gl-2.4.1.min.js +0 -0
  83. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/bokeh-tables-2.4.1.min.js +0 -0
  84. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/bokeh-widgets-2.4.1.min.js +0 -0
  85. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/casaguijs-v0.0.4.0-b2.4.min.js +0 -0
  86. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/casaguijs-v0.0.5.0-b2.4.min.js +0 -0
  87. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/casaguijs-v0.0.6.0-b2.4.min.js +0 -0
  88. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/state/js/casalib-v0.0.1.min.js +0 -0
  89. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/tools/__init__.py +0 -0
  90. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/tools/_cbreset_tool.py +0 -0
  91. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/tools/_drag_tool.py +0 -0
  92. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/utils/__init__.py +0 -0
  93. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/utils/_axes_labels.py +0 -0
  94. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/bokeh/utils/_svg_icon.py +0 -0
  95. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/data/__init__.py +0 -0
  96. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/data/casaimage/__init__.py +0 -0
  97. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/exe/__init__.py +0 -0
  98. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/exe/_context.py +0 -0
  99. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/exe/_mode.py +0 -0
  100. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/exe/_setting.py +0 -0
  101. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/exe/_task.py +0 -0
  102. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/_gclean.py +0 -0
  103. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/__init__.py +0 -0
  104. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_plotants.py +0 -0
  105. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/apps/_plotbandpass.py +0 -0
  106. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casashell/createmask.py +0 -0
  107. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casashell/iclean.py +0 -0
  108. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casatasks/__init__.py +0 -0
  109. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casatasks/createmask.py +0 -0
  110. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casatasks/createregion.py +0 -0
  111. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casatasks/iclean.py +0 -0
  112. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/private/casatasks/iclean_notebook.py +0 -0
  113. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/readme.rst +0 -0
  114. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/remote/__init__.py +0 -0
  115. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/remote/_gclean.py +0 -0
  116. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/remote/_local.py +0 -0
  117. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/remote/_remote_kernel.py +0 -0
  118. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/_app_context.py +0 -0
  119. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/_interactiveclean_wrappers.py +0 -0
  120. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/toolbox/_region_list.py +0 -0
  121. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_ResourceManager.py +0 -0
  122. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_browser.py +0 -0
  123. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_contextmgrchain.py +0 -0
  124. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_conversion.py +0 -0
  125. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_copydoc.py +0 -0
  126. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_docenum.py +0 -0
  127. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_git.py +0 -0
  128. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_import_protected_module.py +0 -0
  129. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_logging.py +0 -0
  130. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_pkgs.py +0 -0
  131. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_regions.py +0 -0
  132. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_static.py +0 -0
  133. {cubevis-1.0.19 → cubevis-1.0.28}/cubevis/utils/_tiles.py +0 -0
  134. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/alma-many-chan/alma-many-chan.py +0 -0
  135. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/basic-websockets-demo/client.html +0 -0
  136. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/basic-websockets-demo/client.py +0 -0
  137. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/basic-websockets-demo/server.py +0 -0
  138. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/createmask-demo/run-createmask.py +0 -0
  139. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/createregion-demo/run-createregion.py +0 -0
  140. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/cubemask-demo/image-slider-spectra-done-stats.py +0 -0
  141. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/cubemask-demo/image-slider-spectra-done.py +0 -0
  142. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/cubemask-demo/image-slider-spectra.py +0 -0
  143. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/cubemask-demo/image-slider.py +0 -0
  144. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/cubemask-demo/image.py +0 -0
  145. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/iclean-demo.ipynb +0 -0
  146. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/m100_interactive.py +0 -0
  147. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/mask0-iclean.py +0 -0
  148. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/run-gclean.py +0 -0
  149. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/run-iclean-obj.py +0 -0
  150. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/run-iclean.py +0 -0
  151. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-demo/vla-sim-jet-iclean.py +0 -0
  152. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-first-look/run-fl-cont.py +0 -0
  153. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-first-look/run-fl-line.py +0 -0
  154. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-outlier/run-iclean.py +0 -0
  155. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-outlier/test_outlier.txt +0 -0
  156. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/iclean-remote/iclean_remote_webserver.py +0 -0
  157. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/large-cube/run-largecube.py +0 -0
  158. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/svg-test.py +0 -0
  159. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/updatable-data-source/direct-plot.py +0 -0
  160. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/updatable-data-source/simple-update.py +0 -0
  161. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/updatable-data-source/updated-plot.py +0 -0
  162. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/uranus-demo/uranus-iclean.py +0 -0
  163. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/websocket-reconnect/client.html +0 -0
  164. {cubevis-1.0.19 → cubevis-1.0.28}/tests/manual/websocket-reconnect/server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cubevis
3
- Version: 1.0.19
3
+ Version: 1.0.28
4
4
  Summary: visualization toolkit and apps for casa
5
5
  License: LGPL
6
6
  Author-email: Darrell Schiebel <darrell@schiebel.us>,Pam Harris <pharris@nrao.edu>
@@ -42,7 +42,7 @@
42
42
  }
43
43
  })
44
44
  ({
45
- "07c9fde72b": function _(e,a,t,o,c){o();const i=e("tslib"),n=e("f8eca7a5dd");c("DataPipe",n.DataPipe);c("activeDataPipes",e("5179d11e71").activeDataPipes);const p=e("2889e0dd45");c("ImagePipe",p.ImagePipe);const s=e("de65005924");c("ImageDataSource",s.ImageDataSource);const r=e("02e3c3e46c");c("SpectraDataSource",r.SpectraDataSource);const d=e("64b16deff9");c("UpdatableDataSource",d.UpdatableDataSource);const S=e("b6ae454f0d");c("WcsTicks",S.WcsTicks);const l=e("cb7d28d6b3");c("DragTool",l.DragTool);const D=e("9d3c34ff8e");c("CBResetTool",D.CBResetTool);const T=e("e3901fa9f2");c("serialize",T.serialize),c("deserialize",T.deserialize);const b=e("9f961622ce");c("TipButton",b.TipButton);const u=e("ca4c845905");c("Tip",u.Tip);const f=e("a6e757a69a");c("Showable",f.Showable);const P=e("467d2716b0");c("BokehAppContext",P.BokehAppContext);const g=e("50a1e32f01");c("SharedDict",g.SharedDict);const h=e("b55081402e");c("EditSpan",h.EditSpan);const B=e("9144bfc7a5");c("EvTextInput",B.EvTextInput);const E=e("74e0abef8a");c("EvPolyAnnotation",E.EvPolyAnnotation);const I=i.__importStar(e("484bb85d20"));t.find=I;(0,e("@bokehjs/base").register_models)({DataPipe:n.DataPipe,ImagePipe:p.ImagePipe,ImageDataSource:s.ImageDataSource,SpectraDataSource:r.SpectraDataSource,UpdatableDataSource:d.UpdatableDataSource,WcsTicks:S.WcsTicks,DragTool:l.DragTool,CBResetTool:D.CBResetTool,Tip:u.Tip,TipButton:b.TipButton,SharedDict:g.SharedDict,Showable:f.Showable,BokehAppContext:P.BokehAppContext,EditSpan:h.EditSpan,EvTextInput:B.EvTextInput,EvPolyAnnotation:E.EvPolyAnnotation})},
45
+ "07c9fde72b": function _(e,t,a,o,c){o();const i=e("tslib"),n=e("f8eca7a5dd");c("DataPipe",n.DataPipe);c("activeDataPipes",e("5179d11e71").activeDataPipes);const p=e("2889e0dd45");c("ImagePipe",p.ImagePipe);const s=e("de65005924");c("ImageDataSource",s.ImageDataSource);const r=e("02e3c3e46c");c("SpectraDataSource",r.SpectraDataSource);const d=e("64b16deff9");c("UpdatableDataSource",d.UpdatableDataSource);const S=e("b6ae454f0d");c("WcsTicks",S.WcsTicks);const l=e("cb7d28d6b3");c("DragTool",l.DragTool);const D=e("9d3c34ff8e");c("CBResetTool",D.CBResetTool);const T=e("e3901fa9f2");c("serialize",T.serialize),c("deserialize",T.deserialize);const b=e("9f961622ce");c("TipButton",b.TipButton);const u=e("ca4c845905");c("Tip",u.Tip);const f=e("92e97078e3");c("Showable",f.Showable);const P=e("467d2716b0");c("BokehAppContext",P.BokehAppContext);const g=e("50a1e32f01");c("SharedDict",g.SharedDict);const h=e("b55081402e");c("EditSpan",h.EditSpan);const B=e("9144bfc7a5");c("EvTextInput",B.EvTextInput);const E=e("74e0abef8a");c("EvPolyAnnotation",E.EvPolyAnnotation);const I=i.__importStar(e("484bb85d20"));a.find=I;(0,e("@bokehjs/base").register_models)({DataPipe:n.DataPipe,ImagePipe:p.ImagePipe,ImageDataSource:s.ImageDataSource,SpectraDataSource:r.SpectraDataSource,UpdatableDataSource:d.UpdatableDataSource,WcsTicks:S.WcsTicks,DragTool:l.DragTool,CBResetTool:D.CBResetTool,Tip:u.Tip,TipButton:b.TipButton,SharedDict:g.SharedDict,Showable:f.Showable,BokehAppContext:P.BokehAppContext,EditSpan:h.EditSpan,EvTextInput:B.EvTextInput,EvPolyAnnotation:E.EvPolyAnnotation})},
46
46
  "f8eca7a5dd": function _(e,s,t,i,n){var o;i();const a=e("@bokehjs/models/sources/data_source"),c=e("e3901fa9f2"),r=e("@bokehjs/core/util/callbacks"),l=e("5179d11e71");class d extends a.DataSource{constructor(e){super(e),this.send_queue={},this.connection_queue=[],this.pending={},this.incoming_callbacks={},this.session_id=casalib.object_id(this)}checkSessionConflict(){try{if("undefined"==typeof Storage)return console.warn("localStorage not available, skipping session conflict detection"),!0;const e=localStorage.getItem(this.session_storage_key);if(e){const s=JSON.parse(e);if(s.sessionId!==this.session_id&&Date.now()-s.timestamp<12e4){if(this.conflict_check){const e=`CubeVis DataPipe (${this.instance_key}) is already running in another browser window or tab.\n\nPlease close other instances and refresh this page, or\nclose this window to continue using the other instance.`;return alert(e),window.opener||1===window.history.length?window.close():window.location.href="about:blank",!1}console.group(`DataPipe ${this.instance_key} conflict detected in Jupyter context`),console.log("Current session ID:",this.session_id),console.log("Existing session ID:",s.sessionId),console.log("Existing timestamp:",new Date(s.timestamp).toISOString()),console.log("Age of existing session (ms):",Date.now()-s.timestamp),console.log("Address:",this.address),console.log("Instance key:",this.instance_key),console.log("Storage key:",this.session_storage_key),console.log("Existing data:",s),console.log("All localStorage keys:",Object.keys(localStorage).filter((e=>e.startsWith("cubevis_datapipe_")))),console.groupEnd()}}return this.updateSessionHeartbeat(),!0}catch(e){return console.warn("Session conflict detection failed:",e),!0}}updateSessionHeartbeat(){try{"undefined"!=typeof Storage&&localStorage.setItem(this.session_storage_key,JSON.stringify({sessionId:this.session_id,timestamp:Date.now(),instanceKey:this.instance_key}))}catch(e){console.warn("Session heartbeat update failed:",e)}}startHeartbeat(){this.heartbeat_interval=window.setInterval((()=>{this.updateSessionHeartbeat()}),3e4)}stopHeartbeat(){this.heartbeat_interval&&(clearInterval(this.heartbeat_interval),this.heartbeat_interval=void 0)}cleanupSession(){try{if("undefined"!=typeof Storage){const e=localStorage.getItem(this.session_storage_key);if(e){JSON.parse(e).sessionId===this.session_id&&localStorage.removeItem(this.session_storage_key)}}}catch(e){console.warn("Session cleanup failed:",e)}this.stopHeartbeat()}handleSessionConflictMessage(e){console.error("Session conflict detected by server:",e);let s="Session conflict detected by server.";"session_conflict"===e.type?s=e.error||s:"session_corruption"===e.type&&(s=`Session corruption detected.\nExpected: ${e.expected}\nReceived: ${e.received}`),alert(s+"\n\nThis window will be closed to prevent data corruption."),this.cleanupSession();const t=new CustomEvent("cubevis_session_conflict",{detail:{message:e,sessionId:this.session_id}});window.dispatchEvent(t),setTimeout((()=>{window.opener||1===window.history.length?window.close():window.location.href="about:blank"}),2e3)}generateInstanceKey(){const e=`${this.address[0]}_${this.address[1]}`;return`${this.instance_id}_${e}`}initialize(){if(super.initialize(),l.activeDataPipes.register(this),this.instance_key=this.generateInstanceKey(),this.session_storage_key=`cubevis_datapipe_${this.instance_key}`,!this.checkSessionConflict())return;let e=`ws://${this.address[0]}:${this.address[1]}`;console.log("datapipe url:",e);var s=void 0;document.shutdown_in_progress_=!1;var t=()=>{void 0!==this.websocket&&this.websocket.close(),this.websocket=new WebSocket(e),this.websocket.binaryType="arraybuffer",this.websocket.addEventListener("error",(e=>{console.log("error encountered:",e)})),this.websocket.onmessage=e=>{if("string"==typeof e.data||e.data instanceof String){let s=(0,c.deserialize)(e.data);if("id"in s&&"direction"in s&&"message"in s){let{id:e,message:t,direction:i}=s;if("error"===i&&("session_conflict"===e||e===this.session_id)&&t&&("session_conflict"===t.type||"session_corruption"===t.type||"close_duplicate"===t.action))return void this.handleSessionConflictMessage(t);if(void 0===t&&console.log("Error, event failure",s),"j2p"==i)if(e in this.pending){let{cb:i}=this.pending[e];if(delete this.pending[e],e in this.send_queue&&this.send_queue[e].length>0){let{cb:s,msg:t}=this.send_queue[e].shift();this.pending[e]={cb:s},this.websocket.send((0,c.serialize)(t))}void 0===t?console.log("DROPPING ERROR FOR NOW (maybe need error callbacks)",s):i(t)}else console.log("message received but could not find id");else if(e in this.incoming_callbacks){let s=this.incoming_callbacks[e](t);this.websocket.send((0,c.serialize)({id:e,direction:i,message:s,session:this.session_id}))}}else console.log(`datapipe received message without one of 'id', 'message' or 'direction': ${s}`)}else console.log("datapipe received binary data",e.data.byteLength,"bytes")},this.websocket.onopen=()=>{for(s?0==s.connected&&console.log(`connection reestablished at ${new Date}`):(this.websocket.send((0,c.serialize)({id:"initialize",direction:"j2p",session:this.session_id})),this.startHeartbeat()),s=new casalib.ReconnectState;this.connection_queue.length>0;){let e=this.connection_queue.shift();this.send.apply(e[0],e[1])}},this.websocket.onclose=()=>{if(s&&1==s.connected&&(console.log(`connection lost at ${new Date}`),s.connected=!1,!document.shutdown_in_progress_)){console.log(`connection lost at ${new Date}`);var e=s;function i(n){0==s.connected&&(console.log(`${n+1}\treconnection attempt ${new Date}`),t(),e.backoff(),e.retries>0?setTimeout(i,e.timeout,n+1):0==s.connected&&console.log(`aborting reconnection after ${n} attempts ${new Date}`))}i(0)}}};window.addEventListener("beforeunload",(()=>{this.cleanupSession()})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState?this.stopHeartbeat():"visible"===document.visibilityState&&(this.updateSessionHeartbeat(),this.startHeartbeat())})),t();(()=>{null!=this.init_script&&(0,r.execute)(this.init_script,this)})()}destroy(){l.activeDataPipes.unregister(this),super.destroy()}register(e,s){this.incoming_callbacks[e]=s}send(e,s,t,i=!1){let n={id:e,message:s,direction:"j2p",session:this.session_id};if(!this.websocket||e in this.pending)if(e in this.send_queue)if("boolean"==typeof i&&i&&this.send_queue[e].length>0)this.send_queue[e][0].msg=n,this.send_queue[e][0].cb=t;else if("function"==typeof i&&this.send_queue[e].length>0){let o=!1;for(const a of this.send_queue[e])i(a.msg.message)&&(a.msg=n,a.cb=t,o=!0);o||this.send_queue[e].push({cb:t,msg:n})}else this.send_queue[e].push({cb:t,msg:n});else this.send_queue[e]=[{cb:t,msg:n}];else if(this.websocket.readyState===WebSocket.CONNECTING)this.connection_queue.push([this,[e,s,t]]);else if(e in this.send_queue&&this.send_queue[e].length>0){this.send_queue[e].push({cb:t,msg:n});{let{cb:r,msg:l}=this.send_queue[e].shift();if(this.pending[e]={cb:r},this.websocket.readyState===WebSocket.OPEN)this.websocket.send((0,c.serialize)(l));else{let d=20,h=this;function u(){h.websocket.readyState===WebSocket.OPEN?h.websocket.send((0,c.serialize)(l)):(d-=1,d>0&&setTimeout(u,3e3))}setTimeout(u,3e3)}}}else if(this.websocket.readyState===WebSocket.OPEN)this.pending[e]={cb:t},this.websocket.send((0,c.serialize)(n));else{let g=20,b=this;function _(){b.websocket.readyState===WebSocket.OPEN?(b.pending[e]={cb:t},b.websocket.send((0,c.serialize)(n))):(g-=1,g>0&&setTimeout(_,3e3))}setTimeout(_,3e3)}}}t.DataPipe=d,o=d,d.__name__="DataPipe",d.__module__="cubevis.bokeh.sources._data_pipe",o.define((({Any:e,Tuple:s,String:t,Number:i,Bool:n})=>({init_script:[e,null],address:[s(t,i)],instance_id:[t],conflict_check:[n,!0]})))},
47
47
  "e3901fa9f2": function _(e,r,s,i,o){i();const l=e("@bokehjs/base"),a=e("@bokehjs/core/resolvers"),t=e("@bokehjs/core/serialization/deserializer"),n=e("@bokehjs/core/serialization/serializer"),{deserialize:c}=new class{constructor(){this.resolver=new a.ModelResolver(l.default_resolver),this.deserializer=new t.Deserializer(this.resolver),this.deserialize=e=>{try{return this.deserializer.decode(JSON.parse(e))}catch(r){return console.group("deserialize error"),console.log(e),console.log(r),console.groupEnd(),{}}}}};s.deserialize=c;const{serialize:z}=new class{constructor(){this.serializer=new n.Serializer,this.serialize=e=>JSON.stringify(this.serializer.encode(e))}};s.serialize=z},
48
48
  "5179d11e71": function _(a,e,n,c,t){c();const i=a("30b45c52a1");n.activeDataPipes=new i.ModelManager},
@@ -58,10 +58,10 @@
58
58
  "9d3c34ff8e": function _(e,o,l,s,t){var c;s();const _=e("@bokehjs/models/tools/actions/reset_tool"),i=e("@bokehjs/styles/icons.css"),a=e("@bokehjs/core/util/callbacks");class n extends _.ResetToolView{doit(){const{precallback:e,postcallback:o}=this.model;null!=e&&(0,a.execute)(e,this.model),this.plot_view.reset(),null!=o&&(0,a.execute)(o,this.model)}}l.CBResetToolView=n,n.__name__="CBResetToolView";class r extends _.ResetTool{constructor(e){super(e),this.tool_name="CBReset",this.tool_icon=i.tool_icon_reset}}l.CBResetTool=r,c=r,r.__name__="CBResetTool",r.__module__="cubevis.bokeh.tools._cbreset_tool",c.prototype.default_view=n,c.define((({Any:e,Nullable:o})=>({precallback:[o(e),null],postcallback:[o(e),null]}))),c.register_alias("cbreset",(()=>new c))},
59
59
  "9f961622ce": function _(e,t,i,o,s){var n;o();const l=e("@bokehjs/models/widgets/abstract_button"),c=e("@bokehjs/models/ui/tooltip"),d=e("@bokehjs/models/ui/icons/builtin_icon"),u=e("@bokehjs/core/build_views"),h=e("@bokehjs/core/bokeh_events"),r=e("@bokehjs/core/util/object");class a extends l.AbstractButtonView{constructor(){super(...arguments),this.isMouseInside=!1}click(){this.model.trigger_event(new h.ButtonClick),super.click()}*children(){yield*super.children(),yield this.tooltip}Show(){this.tooltip.model.setv({visible:!0,closable:!1})}unShow(){this.tooltip.model.setv({visible:!1,closable:!1})}async lazy_initialize(){const{hover_wait:e}=this.model;this.debouncedShow=casalib.debounce((()=>{this.isMouseInside&&this.Show()}),1e3*e),await super.lazy_initialize();const{tooltip:t}=this.model;this.tooltip=await(0,u.build_view)(t,{parent:this})}remove(){this.tooltip.remove(),super.remove()}render(){super.render(),this.el.addEventListener("mouseenter",(()=>{this.isMouseInside=!0,this.debouncedShow()})),this.el.addEventListener("mouseleave",(()=>{this.isMouseInside=!1,this.debouncedShow.cancel(),this.unShow()})),document.addEventListener("mousedown",(e=>{if(void 0===(0,r.dict)(this.model.js_event_callbacks).get(h.ButtonClick.prototype.event_name)){if(e.composedPath().includes(this.tooltip.el))return;this.debouncedShow.cancel(),this.unShow()}else this.isMouseInside=!1,this.debouncedShow.cancel(),this.unShow()})),window.addEventListener("blur",(()=>{this.debouncedShow.cancel(),this.unShow()}))}}i.TipButtonView=a,a.__name__="TipButtonView";class b extends l.AbstractButton{constructor(e){super(e)}on_click(e){this.on_event(h.ButtonClick,e)}}i.TipButton=b,n=b,b.__name__="TipButton",b.__module__="cubevis.bokeh.models._tip_button",n.prototype.default_view=a,n.define((({Ref:e,Number:t})=>({tooltip:[e(c.Tooltip)],hover_wait:[t,1.5]}))),n.override({label:"",icon:new d.BuiltinIcon({icon_name:"help",size:18}),button_type:"default"})},
60
60
  "ca4c845905": function _(e,i,s,t,o){var l;t();const d=e("@bokehjs/models/layouts/layout_dom"),n=e("@bokehjs/models/ui/ui_element"),h=e("@bokehjs/models/ui/tooltip"),r=e("@bokehjs/core/dom"),a=e("@bokehjs/core/build_views");class c extends d.LayoutDOMView{constructor(){super(...arguments),this.isMouseInside=!1}stylesheets(){return[...super.stylesheets(),"*, *:before, *:after { box-sizing: border-box; } fieldset { border: 0px; margin: 0px; padding: 0px; }"]}Show(){this.tooltip.model.setv({visible:!0,closable:!1})}unShow(){this.tooltip.model.setv({visible:!1,closable:!1})}async lazy_initialize(){const{hover_wait:e}=this.model;this.debouncedShow=casalib.debounce((()=>{this.isMouseInside&&this.Show()}),1e3*e),await super.lazy_initialize(),await this.build_child_views();const{tooltip:i}=this.model;this.tooltip=await(0,a.build_view)(i,{parent:this})}remove(){this.tooltip.remove(),super.remove()}connect_signals(){super.connect_signals();const{child:e}=this.model.properties;this.on_change(e,(()=>this.update_children())),this.el.addEventListener("mouseenter",(e=>{this.mouseenter.emit(e)})),this.el.addEventListener("mouseleave",(e=>{this.mouseleave.emit(e)}))}*children(){yield*super.children(),yield this.tooltip}get child_models(){return[this.model.child]}render(){super.render(),this.el.addEventListener("mouseenter",(()=>{this.isMouseInside=!0,this.debouncedShow()})),this.el.addEventListener("mouseleave",(()=>{this.isMouseInside=!1,this.debouncedShow.cancel(),this.unShow()})),window.addEventListener("blur",(()=>{this.debouncedShow.cancel(),this.unShow()})),this.el.addEventListener("click",(()=>{this.debouncedShow.cancel(),this.unShow()}));const e=this.child_views.map((e=>e.el));this.fieldset_el=(0,r.fieldset)({},...e),this.shadow_el.appendChild(this.fieldset_el)}_update_children(){const e=this.child_views.map((e=>e.el));this.fieldset_el.append(...e)}}s.TipView=c,c.__name__="TipView";class u extends d.LayoutDOM{constructor(e){super(e)}}s.Tip=u,l=u,u.__name__="Tip",u.__module__="cubevis.bokeh.models._tip",l.prototype.default_view=c,l.define((({Ref:e,Number:i})=>({child:[e(n.UIElement)],tooltip:[e(h.Tooltip)],hover_wait:[i,1.5]})))},
61
- "a6e757a69a": function _(e,t,l,o,s){var i;o();const a=e("@bokehjs/models/layouts/layout_dom"),r=e("@bokehjs/models/ui/ui_element");class n extends a.LayoutDOMView{constructor(){super(...arguments),this._overlay_el=null}get child_models(){return null!=this.model.ui?[this.model.ui]:[]}initialize(){super.initialize(),this.el.setAttribute("data-lm-suppress-shortcuts","true")}async lazy_initialize(){await super.lazy_initialize()}connect_signals(){super.connect_signals(),this.connect(this.model.properties.disabled.change,(()=>{this._update_disabled_state()})),this.connect(this.model.properties.disabled_message.change,(()=>{this.model.disabled&&null!=this._overlay_el&&this._update_overlay_message()}))}_update_layout(){super._update_layout()}render(){super.render(),console.log("Showable render() - disabled:",this.model.disabled,"shadow_el:",null!=this.shadow_el),0===this.child_views.length&&null==this.model.ui&&(this.el.innerHTML='<div style="color: gray; padding: 10px; border: 1px dashed gray;">\n Showable: No UI element set\n </div>'),this._update_disabled_state()}after_layout(){super.after_layout(),console.log("Showable after_layout() - disabled:",this.model.disabled,"shadow_el:",null!=this.shadow_el),this.model.disabled&&this._update_disabled_state()}_intrinsic_display(){return super._intrinsic_display()}_update_disabled_state(){console.log("_update_disabled_state called, disabled:",this.model.disabled),this.model.disabled?(this._show_disabled_overlay(),this.el.style.filter="grayscale(50%)"):(this._hide_disabled_overlay(),this.el.style.filter="")}_disable_interactive_elements(){this.el.querySelectorAll(".bk-toolbar, .bk-toolbar-button").forEach((e=>{e.style.pointerEvents="none",e.style.opacity="0.5"}));this.el.querySelectorAll("canvas").forEach((e=>{e.style.pointerEvents="none"}));this.el.querySelectorAll("button, .bk-btn").forEach((e=>{e.disabled=!0}))}_enable_interactive_elements(){const e=this.shadow_el;if(!e)return;e.querySelectorAll(".bk-toolbar, .bk-toolbar-button").forEach((e=>{e.style.pointerEvents="",e.style.opacity=""}));e.querySelectorAll("canvas").forEach((e=>{e.style.pointerEvents=""}));e.querySelectorAll("button, .bk-btn").forEach((e=>{e.disabled=!1})),console.log("Interactive elements re-enabled")}_show_disabled_overlay(){if(console.log("_show_disabled_overlay called, _overlay_el exists:",null!=this._overlay_el),console.log("disabled_message:",this.model.disabled_message),null==this._overlay_el){console.log("Creating new overlay element");const e=this.shadow_el;if(console.log("Shadow root:",e),!e)return void console.error("No shadow root found!");const t=document.createElement("div");t.className="showable-disabled-message",t.style.cssText="\n background: white;\n padding: 30px 40px;\n border-radius: 10px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n text-align: center;\n border: 2px solid #4CAF50;\n max-width: 90%;\n word-wrap: break-word;\n pointer-events: auto;\n cursor: default;\n ",t.innerHTML=`\n <div style="font-size: 24px; font-weight: bold; color: #4CAF50; margin-bottom: 10px;">\n ${this.model.disabled_message}\n </div>\n `,console.log("Message box created"),this._overlay_el=document.createElement("div"),this._overlay_el.appendChild(t),this._overlay_el.style.setProperty("position","absolute","important"),this._overlay_el.style.setProperty("top","0","important"),this._overlay_el.style.setProperty("left","0","important"),this._overlay_el.style.setProperty("right","0","important"),this._overlay_el.style.setProperty("bottom","0","important"),this._overlay_el.style.setProperty("width","100%","important"),this._overlay_el.style.setProperty("height","100%","important"),this._overlay_el.style.setProperty("background-color","rgba(220, 220, 220, 0.85)","important"),this._overlay_el.style.setProperty("display","flex","important"),this._overlay_el.style.setProperty("justify-content","center","important"),this._overlay_el.style.setProperty("align-items","center","important"),this._overlay_el.style.setProperty("z-index","2147483647","important"),this._overlay_el.style.setProperty("pointer-events","auto","important"),this._overlay_el.style.setProperty("cursor","not-allowed","important"),console.log("Overlay element styles set");const l=e.firstElementChild;l&&(l.style.position="relative",console.log("Set content container to relative positioning")),e.appendChild(this._overlay_el),console.log("Overlay appended to shadow root"),setTimeout((()=>{const e=this._overlay_el.getBoundingClientRect(),l=t.getBoundingClientRect();console.log("Overlay bounding rect:",{width:e.width,height:e.height,top:e.top,left:e.left}),console.log("Message box bounding rect:",{width:l.width,height:l.height})}),100),this._disable_interactive_elements();["mousedown","mouseup","click","dblclick","contextmenu","touchstart","touchmove","touchend","keydown","keyup","keypress","pointerdown","pointerup","pointermove","dragstart","drag","dragend"].forEach((e=>{this._overlay_el.addEventListener(e,(e=>{if(e.target!==t&&!t.contains(e.target))return e.stopPropagation(),e.preventDefault(),e.stopImmediatePropagation(),!1}),{capture:!0,passive:!1})})),this._overlay_el.addEventListener("wheel",(t=>{let l=e.host.parentElement;for(;l;){const e=window.getComputedStyle(l).overflow,t=window.getComputedStyle(l).overflowY;if("auto"===e||"scroll"===e||"auto"===t||"scroll"===t)break;l=l.parentElement}l&&(l.scrollTop+=t.deltaY,l.scrollLeft+=t.deltaX),t.stopPropagation(),t.preventDefault()}),{capture:!0,passive:!1})}else console.log("Overlay already exists, showing it"),this._overlay_el.style.display="flex";console.log("_show_disabled_overlay complete")}_update_overlay_message(){if(null!=this._overlay_el){const e=this._overlay_el.querySelector(".showable-disabled-message");e&&(e.innerHTML=`\n <div style="font-size: 24px; font-weight: bold; color: #4CAF50; margin-bottom: 10px;">\n ${this.model.disabled_message}\n </div>\n <div style="font-size: 14px; color: #666;">\n You can now close this GUI or continue working in your notebook\n </div>\n `)}}_hide_disabled_overlay(){null!=this._overlay_el&&(this._overlay_el.style.display="none",this._enable_interactive_elements())}remove(){null!=this._overlay_el&&(this._overlay_el.remove(),this._overlay_el=null),super.remove()}}l.ShowableView=n,n.__name__="ShowableView";class d extends a.LayoutDOM{constructor(e){super(e)}}l.Showable=d,i=d,d.__name__="Showable",d.__module__="cubevis.bokeh.models._showable",i.prototype.default_view=n,i.define((({Ref:e,String:t})=>({ui:[e(r.UIElement)],disabled_message:[t,"Interaction Complete \u2713"]})))},
61
+ "92e97078e3": function _(e,t,o,l,s){var i;l();const a=e("@bokehjs/models/layouts/layout_dom"),n=e("@bokehjs/models/ui/ui_element");class r extends a.LayoutDOMView{constructor(){super(...arguments),this._overlay_el=null}get child_models(){return null!=this.model.ui?[this.model.ui]:[]}initialize(){super.initialize(),this.el.setAttribute("data-lm-suppress-shortcuts","true")}async lazy_initialize(){await super.lazy_initialize()}connect_signals(){super.connect_signals(),this.connect(this.model.properties.disabled.change,(()=>{this._update_disabled_state()})),this.connect(this.model.properties.disabled_message.change,(()=>{this.model.disabled&&null!=this._overlay_el&&this._update_overlay_message()}))}_update_layout(){super._update_layout()}render(){super.render(),console.log("Showable render() - disabled:",this.model.disabled,"shadow_el:",null!=this.shadow_el);try{console.log("Colab environment check:"),console.log("window.location:",window.location),console.log("window.google:",window.google),console.log("Parent frame:",window.parent!==window),window.parent!==window&&(console.log("Running in iframe"),console.log("Parent location:",document.referrer))}catch(e){console.warn("An error occurred during Colab diagnostics")}console.log("Past Colab diagnostics..."),0===this.child_views.length&&null==this.model.ui&&(this.el.innerHTML='<div style="color: gray; padding: 10px; border: 1px dashed gray;">\n Showable: No UI element set\n </div>'),this._update_disabled_state()}after_layout(){super.after_layout(),console.log("Showable after_layout() - disabled:",this.model.disabled,"shadow_el:",null!=this.shadow_el),this.model.disabled&&this._update_disabled_state()}_intrinsic_display(){return super._intrinsic_display()}_update_disabled_state(){console.log("_update_disabled_state called, disabled:",this.model.disabled),this.model.disabled?(this._show_disabled_overlay(),this.el.style.filter="grayscale(50%)"):(this._hide_disabled_overlay(),this.el.style.filter="")}_disable_interactive_elements(){this.el.querySelectorAll(".bk-toolbar, .bk-toolbar-button").forEach((e=>{e.style.pointerEvents="none",e.style.opacity="0.5"}));this.el.querySelectorAll("canvas").forEach((e=>{e.style.pointerEvents="none"}));this.el.querySelectorAll("button, .bk-btn").forEach((e=>{e.disabled=!0}))}_enable_interactive_elements(){const e=this.shadow_el;if(!e)return;e.querySelectorAll(".bk-toolbar, .bk-toolbar-button").forEach((e=>{e.style.pointerEvents="",e.style.opacity=""}));e.querySelectorAll("canvas").forEach((e=>{e.style.pointerEvents=""}));e.querySelectorAll("button, .bk-btn").forEach((e=>{e.disabled=!1})),console.log("Interactive elements re-enabled")}_show_disabled_overlay(){if(console.log("_show_disabled_overlay called, _overlay_el exists:",null!=this._overlay_el),console.log("disabled_message:",this.model.disabled_message),null==this._overlay_el){console.log("Creating new overlay element");const e=this.shadow_el;if(console.log("Shadow root:",e),!e)return void console.error("No shadow root found!");const t=document.createElement("div");t.className="showable-disabled-message",t.style.cssText="\n background: white;\n padding: 30px 40px;\n border-radius: 10px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);\n text-align: center;\n border: 2px solid #4CAF50;\n max-width: 90%;\n word-wrap: break-word;\n pointer-events: auto;\n cursor: default;\n ",t.innerHTML=`\n <div style="font-size: 24px; font-weight: bold; color: #4CAF50; margin-bottom: 10px;">\n ${this.model.disabled_message}\n </div>\n `,console.log("Message box created"),this._overlay_el=document.createElement("div"),this._overlay_el.appendChild(t),this._overlay_el.style.setProperty("position","absolute","important"),this._overlay_el.style.setProperty("top","0","important"),this._overlay_el.style.setProperty("left","0","important"),this._overlay_el.style.setProperty("right","0","important"),this._overlay_el.style.setProperty("bottom","0","important"),this._overlay_el.style.setProperty("width","100%","important"),this._overlay_el.style.setProperty("height","100%","important"),this._overlay_el.style.setProperty("background-color","rgba(220, 220, 220, 0.85)","important"),this._overlay_el.style.setProperty("display","flex","important"),this._overlay_el.style.setProperty("justify-content","center","important"),this._overlay_el.style.setProperty("align-items","center","important"),this._overlay_el.style.setProperty("z-index","2147483647","important"),this._overlay_el.style.setProperty("pointer-events","auto","important"),this._overlay_el.style.setProperty("cursor","not-allowed","important"),console.log("Overlay element styles set");const o=e.firstElementChild;o&&(o.style.position="relative",console.log("Set content container to relative positioning")),e.appendChild(this._overlay_el),console.log("Overlay appended to shadow root"),setTimeout((()=>{const e=this._overlay_el.getBoundingClientRect(),o=t.getBoundingClientRect();console.log("Overlay bounding rect:",{width:e.width,height:e.height,top:e.top,left:e.left}),console.log("Message box bounding rect:",{width:o.width,height:o.height})}),100),this._disable_interactive_elements();["mousedown","mouseup","click","dblclick","contextmenu","touchstart","touchmove","touchend","keydown","keyup","keypress","pointerdown","pointerup","pointermove","dragstart","drag","dragend"].forEach((e=>{this._overlay_el.addEventListener(e,(e=>{if(e.target!==t&&!t.contains(e.target))return e.stopPropagation(),e.preventDefault(),e.stopImmediatePropagation(),!1}),{capture:!0,passive:!1})})),this._overlay_el.addEventListener("wheel",(t=>{let o=e.host.parentElement;for(;o;){const e=window.getComputedStyle(o).overflow,t=window.getComputedStyle(o).overflowY;if("auto"===e||"scroll"===e||"auto"===t||"scroll"===t)break;o=o.parentElement}o&&(o.scrollTop+=t.deltaY,o.scrollLeft+=t.deltaX),t.stopPropagation(),t.preventDefault()}),{capture:!0,passive:!1})}else console.log("Overlay already exists, showing it"),this._overlay_el.style.display="flex";console.log("_show_disabled_overlay complete")}_update_overlay_message(){if(null!=this._overlay_el){const e=this._overlay_el.querySelector(".showable-disabled-message");e&&(e.innerHTML=`\n <div style="font-size: 24px; font-weight: bold; color: #4CAF50; margin-bottom: 10px;">\n ${this.model.disabled_message}\n </div>\n <div style="font-size: 14px; color: #666;">\n You can now close this GUI or continue working in your notebook\n </div>\n `)}}_hide_disabled_overlay(){null!=this._overlay_el&&(this._overlay_el.style.display="none",this._enable_interactive_elements())}remove(){null!=this._overlay_el&&(this._overlay_el.remove(),this._overlay_el=null),super.remove()}}o.ShowableView=r,r.__name__="ShowableView";class d extends a.LayoutDOM{constructor(e){super(e)}}o.Showable=d,i=d,d.__name__="Showable",d.__module__="cubevis.bokeh.models._showable",i.prototype.default_view=r,i.define((({Ref:e,String:t})=>({ui:[e(n.UIElement)],disabled_message:[t,"Interaction Complete \u2713"]})))},
62
62
  "467d2716b0": function _(e,i,s,t,o){var n;t();const p=e("@bokehjs/models/layouts/layout_dom"),a=e("@bokehjs/models/ui/ui_element");class l extends p.LayoutDOMView{get child_models(){return null!=this.model.ui?[this.model.ui]:[]}initialize(){super.initialize(),this.el&&(this.el.style.padding="0",this.el.style.margin="0",this.el.style.border="none",this.el.style.background="transparent",this.el.style.display="contents"),this.connect(this.model.properties.app_state.change,(()=>{const e=window.cubevisAppSession;(null==e?void 0:e.applications[this.model.app_id])&&(e.applications[this.model.app_id].state=this.model.app_state)}))}}s.BokehAppContextView=l,l.__name__="BokehAppContextView";class d extends p.LayoutDOM{constructor(e){super(e)}initialize(){super.initialize(),window.cubevisAppSession||(window.cubevisAppSession={sessionId:this.session_id,applications:{}},console.log(`Initialized Bokeh session: ${this.session_id}`));const e=window.cubevisAppSession;e.applications[this.app_id]||(e.applications[this.app_id]={appId:this.app_id,state:this.app_state,createdAt:(new Date).toISOString()},console.log(`Registered application: ${this.app_id}`))}}s.BokehAppContext=d,n=d,d.__name__="BokehAppContext",d.__module__="cubevis.bokeh.models._bokeh_app_context",n.prototype.default_view=l,n.define((({Ref:e,Nullable:i,Dict:s,String:t,Unknown:o})=>({ui:[i(e(a.UIElement)),null],app_id:[t,""],session_id:[t,""],app_state:[s(o),{}]})))},
63
63
  "50a1e32f01": function _(e,s,i,n,t){var a;n();const c=e("@bokehjs/core/view"),o=e("@bokehjs/model");class _ extends c.View{initialize(){super.initialize()}connect_signals(){super.connect_signals();const{values:e}=this.model.properties;this.on_change(e,(()=>this.values_changed()))}values_changed(){}}i.SharedDictView=_,_.__name__="SharedDictView";class d extends o.Model{constructor(e){super(e)}}i.SharedDict=d,a=d,d.__name__="SharedDict",d.__module__="cubevis.bokeh.models._shared_dict",a.prototype.default_view=_,a.define((({Dict:e,Unknown:s})=>({values:[e(s),{}]})))},
64
64
  "b55081402e": function _(e,n,t,_,s){var a;_();const o=e("@bokehjs/models/annotations/span"),p=e("@bokehjs/core/bokeh_events");class r extends o.SpanView{on_pan_start(e){const n=super.on_pan_start(e);return this.model.trigger_event(new p.LODStart),n}on_pan(e){super.on_pan(e)}on_pan_end(e){super.on_pan_end(e),this.model.trigger_event(new p.LODEnd)}}t.EditSpanView=r,r.__name__="EditSpanView";class d extends o.Span{constructor(e){super(e)}}t.EditSpan=d,a=d,d.__name__="EditSpan",d.__module__="cubevis.bokeh.models._edit_span",a.prototype.default_view=r},
65
65
  "9144bfc7a5": function _(e,t,s,n,r){var i;n();const l=e("@bokehjs/models/widgets/text_input"),o=e("@bokehjs/core/dom"),u=e("@bokehjs/core/bokeh_events");class _ extends l.TextInputView{stylesheets(){return[...super.stylesheets(),new o.InlineStyleSheet(".bk-input-prefix { padding: 0 var(--padding-vertical); }")]}connect_signals(){super.connect_signals(),this.el.addEventListener("mouseenter",(e=>{this.model.trigger_event(new u.MouseEnter(e.screenX,e.screenY,e.x,e.y,{shift:e.shiftKey,ctrl:e.ctrlKey,alt:e.altKey}))})),this.el.addEventListener("mouseleave",(e=>{this.model.trigger_event(new u.MouseLeave(e.screenX,e.screenY,e.x,e.y,{shift:e.shiftKey,ctrl:e.ctrlKey,alt:e.altKey}))}))}render(){super.render()}}s.EvTextInputView=_,_.__name__="EvTextInputView";class c extends l.TextInput{constructor(e){super(e)}}s.EvTextInput=c,i=c,c.__name__="EvTextInput",c.__module__="cubevis.bokeh.models._ev_text_input",i.prototype.default_view=_},
66
66
  "74e0abef8a": function _(e,t,n,s,o){var i;s();const r=e("@bokehjs/models/annotations/poly_annotation"),a=e("@bokehjs/core/bokeh_events");class _ extends r.PolyAnnotationView{on_enter(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.MouseEnter(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt}),o=super.on_enter(e);return this.model.trigger_event(s),o}on_leave(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.MouseLeave(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt});super.on_leave(e),this.model.trigger_event(s)}on_pan_start(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.PanStart(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt}),o=super.on_pan_start(e);return this.model.trigger_event(s),o}on_pan_end(e){const{x_scale:t,y_scale:n}=this.plot_view.frame,s=new a.PanEnd(e.sx,e.sy,t.invert(e.sx),n.invert(e.sy),{shift:e.modifiers.shift,ctrl:e.modifiers.ctrl,alt:e.modifiers.alt});super.on_pan_end(e),this.model.trigger_event(s)}on_pan(e){super.on_pan(e);const t=new a.RangesUpdate(e.sx,e.sx+e.dx,e.sy,e.sy+e.dy);this.model.trigger_event(t)}}n.EvPolyAnnotationView=_,_.__name__="EvPolyAnnotationView";class l extends r.PolyAnnotation{constructor(e){super(e)}}n.EvPolyAnnotation=l,i=l,l.__name__="EvPolyAnnotation",l.__module__="cubevis.bokeh.annotations._ev_poly_annotation",i.prototype.default_view=_},
67
- }, "07c9fde72b", {"index":"07c9fde72b","src/bokeh/sources/data_pipe":"f8eca7a5dd","src/bokeh/util/conversions":"e3901fa9f2","src/bokeh/sources/active_data_pipes":"5179d11e71","src/bokeh/util/model_manager":"30b45c52a1","src/bokeh/sources/image_pipe":"2889e0dd45","src/bokeh/sources/image_data_source":"de65005924","src/bokeh/sources/spectra_data_source":"02e3c3e46c","src/bokeh/sources/updatable_data_source":"64b16deff9","src/bokeh/format/wcs_ticks":"b6ae454f0d","src/bokeh/tools/drag_tool":"cb7d28d6b3","src/bokeh/events":"8fc7a9e935","src/bokeh/util/find":"484bb85d20","src/bokeh/tools/cbreset_tool":"9d3c34ff8e","src/bokeh/models/tip_button":"9f961622ce","src/bokeh/models/tip":"ca4c845905","src/bokeh/models/showable":"a6e757a69a","src/bokeh/models/bokeh_app_context":"467d2716b0","src/bokeh/models/shared_dict":"50a1e32f01","src/bokeh/models/edit_span":"b55081402e","src/bokeh/models/ev_text_input":"9144bfc7a5","src/bokeh/annotations/ev_poly_annotation":"74e0abef8a"}, {});});
67
+ }, "07c9fde72b", {"index":"07c9fde72b","src/bokeh/sources/data_pipe":"f8eca7a5dd","src/bokeh/util/conversions":"e3901fa9f2","src/bokeh/sources/active_data_pipes":"5179d11e71","src/bokeh/util/model_manager":"30b45c52a1","src/bokeh/sources/image_pipe":"2889e0dd45","src/bokeh/sources/image_data_source":"de65005924","src/bokeh/sources/spectra_data_source":"02e3c3e46c","src/bokeh/sources/updatable_data_source":"64b16deff9","src/bokeh/format/wcs_ticks":"b6ae454f0d","src/bokeh/tools/drag_tool":"cb7d28d6b3","src/bokeh/events":"8fc7a9e935","src/bokeh/util/find":"484bb85d20","src/bokeh/tools/cbreset_tool":"9d3c34ff8e","src/bokeh/models/tip_button":"9f961622ce","src/bokeh/models/tip":"ca4c845905","src/bokeh/models/showable":"92e97078e3","src/bokeh/models/bokeh_app_context":"467d2716b0","src/bokeh/models/shared_dict":"50a1e32f01","src/bokeh/models/edit_span":"b55081402e","src/bokeh/models/ev_text_input":"9144bfc7a5","src/bokeh/annotations/ev_poly_annotation":"74e0abef8a"}, {});});
@@ -30,6 +30,7 @@ provided by Bokeh'''
30
30
 
31
31
  from .state import order_bokeh_js as _order_bokeh_js
32
32
  from .state import register_model as _register_model
33
+ from .state._initialize import get_bokeh_js_paths
33
34
  from .state import set_cubevis_lib
34
35
 
35
36
  class BokehInit:
@@ -2,7 +2,13 @@ import logging
2
2
  from bokeh.core.properties import String, Dict, Any, Nullable, Instance
3
3
  from bokeh.models.layouts import LayoutDOM
4
4
  from bokeh.models.ui import UIElement
5
+ from bokeh.resources import CDN
6
+ from tempfile import TemporaryDirectory
5
7
  from uuid import uuid4
8
+ import unicodedata
9
+ import webbrowser
10
+ import os
11
+ import re
6
12
 
7
13
  logger = logging.getLogger(__name__)
8
14
 
@@ -29,9 +35,34 @@ class BokehAppContext(LayoutDOM):
29
35
  cls._session_id = str(uuid4())
30
36
  return cls._session_id
31
37
 
32
- def __init__( self, ui=None, **kwargs ):
38
+ def _slugify(self, value, allow_unicode=False):
39
+ """
40
+ Taken from https://github.com/django/django/blob/master/django/utils/text.py
41
+ Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
42
+ dashes to single dashes. Remove characters that aren't alphanumerics,
43
+ underscores, or hyphens. Convert to lowercase. Also strip leading and
44
+ trailing whitespace, dashes, and underscores.
45
+ https://stackoverflow.com/a/295466/2903943
46
+ """
47
+ value = str(value)
48
+ if allow_unicode:
49
+ value = unicodedata.normalize('NFKC', value)
50
+ else:
51
+ value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
52
+ value = re.sub(r'[^\w\s-]', '', value.lower())
53
+ return re.sub(r'[-\s]+', '-', value).strip('-_')
54
+
55
+ def __init__( self, ui=None, title=str(uuid4( )), prefix=None, **kwargs ):
33
56
  logger.debug(f"\tBokehAppContext::__init__(ui={type(ui).__name__ if ui else None}, {kwargs}): {id(self)}")
34
57
 
58
+ if prefix is None:
59
+ ## create a prefix from the title
60
+ prefix = self._slugify(title)[:10]
61
+
62
+ self.__title = title
63
+ self.__workdir = TemporaryDirectory(prefix=prefix)
64
+ self.__htmlpath = os.path.join( self.__workdir.name, f'''{self._slugify(self.__title)}.html''' )
65
+
35
66
  if ui is not None and 'ui' in kwargs:
36
67
  raise RuntimeError( "'ui' supplied as both a positional parameter and a keyword parameter" )
37
68
 
@@ -61,3 +92,19 @@ class BokehAppContext(LayoutDOM):
61
92
  current_state = dict(self.app_state)
62
93
  current_state.update(state_updates)
63
94
  self.app_state = current_state
95
+
96
+ def show( self ):
97
+ """Always show plot in a new browser tab without changing output settings.
98
+ Jupyter display is handled by the Showable class. However, at some
99
+ point this function might need to support more than just independent
100
+ browser tab display.
101
+ """
102
+ logger.debug(f"\tBokehAppContext::show( ): {id(self)}")
103
+
104
+ from bokeh.plotting import save
105
+
106
+ # Save the plot
107
+ save( self, filename=self.__htmlpath, resources=CDN, title=self.__title)
108
+
109
+ # Open in browser
110
+ webbrowser.open('file://' + os.path.abspath(self.__htmlpath))
@@ -5,6 +5,7 @@ from bokeh.core.properties import Instance, String
5
5
 
6
6
  from bokeh.io import curdoc
7
7
  from .. import BokehInit
8
+ from ...utils import is_colab
8
9
 
9
10
  logger = logging.getLogger(__name__)
10
11
 
@@ -48,39 +49,11 @@ class Showable(LayoutDOM,BokehInit):
48
49
  # It might be a regular function or static method without explicit 'self'/'cls'
49
50
  return None
50
51
 
51
- # Detect Bokeh usage mode (i.e. self.__class__._usage_mode unset)
52
- calling_mode = None
53
- if self.__class__._usage_mode is None:
54
- self.__class__._usage_mode = "bokeh"
55
- calling_mode = "bokeh"
56
-
57
52
  # Allow None (detaching from document) without any further checking
58
53
  if doc is None:
59
54
  self._document = None
60
55
  return
61
56
 
62
- if calling_mode is None:
63
- import inspect
64
- stack_frames = inspect.stack( )
65
- try:
66
- for frame in stack_frames[1:]:
67
- if frame.function == '_repr_mimebundle_' or frame.function == 'show':
68
- if get_caller_class_name(frame.frame) == self.__class__.__name__:
69
- calling_mode = "custom"
70
- break
71
- finally:
72
- # Essential to delete stack frames to avoid reference cycles
73
- del stack_frames
74
-
75
- if calling_mode != self.__class__._usage_mode:
76
- ### THIS CATCHES: using Bokeh show after Showable display methods
77
- ### using Showable.show after Bokeh show
78
- raise RuntimeError(
79
- f"\n{'='*70}\n" +
80
- ( (self._usage_error['custom'] % self.__class__.__name__) if calling_mode == 'custom' else
81
- (self._usage_error['bokeh'] % 'bokeh.plotting.show') ) +
82
- f"\n{'='*70}\n" )
83
-
84
57
  from bokeh.io.state import curstate
85
58
  state = curstate( )
86
59
 
@@ -118,12 +91,23 @@ class Showable(LayoutDOM,BokehInit):
118
91
  self._start_backend()
119
92
  self._backend_started = True
120
93
 
94
+ def to_serializable(self, *args, **kwargs):
95
+ if self._display_context:
96
+ self._display_context.on_to_serializable( )
97
+
98
+ # Call parent's to_serializable
99
+ return super().to_serializable(*args, **kwargs)
100
+
121
101
  def __init__( self, ui_element=None, backend_func=None,
122
102
  result_retrieval=None,
123
103
  notebook_width=1200, notebook_height=800,
124
- notebook_sizing='fixed', **kwargs):
104
+ notebook_sizing='fixed',
105
+ display_context=None,
106
+ **kwargs ):
125
107
  logger.debug(f"\tShowable::__init__(ui_element={type(ui_element).__name__ if ui_element else None}, {kwargs}): {id(self)}")
126
108
 
109
+ self._display_context = display_context
110
+
127
111
  # Set default sizing if not provided
128
112
  sizing_params = {'sizing_mode', 'width', 'height'}
129
113
  provided_sizing_params = set(kwargs.keys()) & sizing_params
@@ -147,18 +131,6 @@ class Showable(LayoutDOM,BokehInit):
147
131
  'browser': { 'mode': self.sizing_mode, 'width': self.width, 'height': self.height }
148
132
  }
149
133
 
150
- # Error messages included in RuntimeErrors
151
- self._usage_error = {
152
- 'custom': "❌ Cannot use %s display methods:\n\n" \
153
- "Reason: bokeh.plotting.show() has already been used for display\n" \
154
- " of this class. Mixing display methods within a single notebook\n" \
155
- " corrupts Bokeh display within the notebook\n",
156
- 'bokeh': "❌ Cannot use %s display method:\n\n" \
157
- "Reason: Showable display methods have already been used for display\n" \
158
- " of this class. Mixing display methods within a single notebook\n" \
159
- " corrupts Bokeh display within the notebook\n" }
160
-
161
-
162
134
  # Set the function to be called upon display
163
135
  if backend_func is not None:
164
136
  self._backend_startup_callback = backend_func
@@ -301,15 +273,11 @@ class Showable(LayoutDOM,BokehInit):
301
273
  Common logic for generating HTML in notebook environments.
302
274
  Returns the HTML string to display, or None if not in a notebook.
303
275
  """
304
- from bokeh.embed import components
276
+ from bokeh.embed import components, json_item
305
277
  from bokeh.io.state import curstate
306
-
307
- if self.__class__._usage_mode != "custom":
308
- ### THIS CATCHES: Showable display via evaluation ( "ic" ) after Bokeh show
309
- raise RuntimeError(
310
- f"\n{'='*70}\n" +
311
- (self._usage_error['custom'] % self.__class__.__name__) +
312
- f"\n{'='*70}\n" )
278
+ from bokeh.resources import CDN
279
+ import sys
280
+ import json as json_lib
313
281
 
314
282
  state = curstate()
315
283
 
@@ -319,29 +287,93 @@ class Showable(LayoutDOM,BokehInit):
319
287
  if self.ui is None:
320
288
  return '<div style="color: red; padding: 10px; border: 1px solid red;">Showable object with no UI set</div>'
321
289
 
290
+ if self._display_context:
291
+ self._display_context.on_show()
292
+
322
293
  if self._notebook_rendering:
323
294
  # Return a lightweight reference instead of re-rendering the full GUI
324
295
  return f'''
325
296
  <div style="padding: 10px; background: #f0f8f0; border-left: 4px solid #4CAF50; margin: 5px 0;">
326
- <strong>↑ iclean GUI active above</strong>
297
+ <strong>→ iclean GUI active above</strong>
327
298
  <small style="color: #666; display: block; margin-top: 5px;">
328
299
  Showable ID: {self.id[-8:]} | Backend: Running
329
300
  </small>
330
301
  </div>
331
302
  '''
332
303
 
333
- # Apply notebook sizing for Jupyter context
334
- ###if self._notebook_sizing == 'fixed':
335
- ### self.sizing_mode = None
336
- ### self.width = self._notebook_width
337
- ### self.height = self._notebook_height
304
+ if is_colab( ):
305
+ # Get all JS paths from the existing function
306
+ # This returns paths in the correct order:
307
+ # [casalib, bokeh-core, bokeh-widgets, bokeh-tables, cubevisjs]
308
+ from cubevis.bokeh import get_bokeh_js_paths
309
+ js_paths = get_bokeh_js_paths( )
310
+
311
+ # Build script tags for all libraries in order
312
+ all_scripts = '\n'.join([
313
+ f'<script type="text/javascript" src="{url}"></script>'
314
+ for url in js_paths
315
+ ])
316
+
317
+ # Use json_item approach which is more reliable in iframes
318
+ item = json_item(self, target=f"bokeh-{self.id}")
319
+ item_json = json_lib.dumps(item)
320
+
321
+ # Build complete HTML with proper loading sequence
322
+ # get_bokeh_js_paths() already returns libs in the correct order:
323
+ # 1. casalib (third-party libs for CustomJS)
324
+ # 2. bokeh-core
325
+ # 3. bokeh-widgets
326
+ # 4. bokeh-tables
327
+ # 5. cubevisjs (custom Bokeh models)
328
+ html = f'''
329
+ {f'<link href="{CDN.css_files[0]}" rel="stylesheet" type="text/css">' if CDN.css_files else ""}
330
+ <div id="bokeh-{self.id}" class="bk-root"></div>
331
+ {all_scripts}
332
+ <script type="text/javascript">
333
+ (function() {{
334
+ var item = {item_json};
335
+
336
+ function embedWhenReady() {{
337
+ // Check if all required libraries are loaded
338
+ if (typeof Bokeh !== 'undefined' && Bokeh.embed) {{
339
+ var target = document.getElementById("bokeh-{self.id}");
340
+ if (target) {{
341
+ try {{
342
+ Bokeh.embed.embed_item(item);
343
+ console.log("Bokeh plot embedded successfully");
344
+ }} catch(e) {{
345
+ console.error("Error embedding Bokeh plot:", e);
346
+ }}
347
+ }} else {{
348
+ console.error("Target element not found");
349
+ setTimeout(embedWhenReady, 50);
350
+ }}
351
+ }} else {{
352
+ setTimeout(embedWhenReady, 50);
353
+ }}
354
+ }}
355
+
356
+ if (document.readyState === 'loading') {{
357
+ document.addEventListener('DOMContentLoaded', embedWhenReady);
358
+ }} else {{
359
+ embedWhenReady();
360
+ }}
361
+ }})();
362
+ </script>
363
+ '''
338
364
 
339
- script, div = components(self)
340
- if start_backend:
341
- self._start_backend()
365
+ if start_backend:
366
+ self._start_backend()
342
367
 
343
- self._notebook_rendering = f'{script}\n{div}'
344
- return self._notebook_rendering
368
+ self._notebook_rendering = html
369
+ return html
370
+ else:
371
+ # In Jupyter Lab/Classic, use components() as before
372
+ script, div = components(self)
373
+ if start_backend:
374
+ self._start_backend()
375
+ self._notebook_rendering = f'{script}\n{div}'
376
+ return self._notebook_rendering
345
377
 
346
378
  def _repr_mimebundle_(self, include=None, exclude=None):
347
379
  """
@@ -321,6 +321,13 @@ def order_bokeh_js():
321
321
  resources.Resources.js_files = property(js_files)
322
322
  return
323
323
 
324
+ #def get_bokeh_js_paths( ):
325
+ # modes = ['cdn','inline','server','server-dev','relative','relative-dev','absolute','absolute-dev']
326
+ # return { 'new': { mode: resources.Resources(mode=mode).js_files for mode in modes },
327
+ # 'old': { mode: resources.Resources(mode=mode)._old_js_files for mode in modes } }
328
+ def get_bokeh_js_paths( ):
329
+ return resources.Resources(mode='cdn').js_files
330
+
324
331
  def get_jupyter_state( ):
325
332
  """Get the package-level Jupyter state"""
326
333
  return _JUPYTER_STATE
@@ -35,7 +35,7 @@ from contextlib import asynccontextmanager
35
35
  from bokeh.layouts import row, column
36
36
  from bokeh.plotting import show
37
37
  from bokeh.models import Button, CustomJS, TabPanel, Tabs, Spacer, Div
38
- from cubevis.toolbox import CubeMask, AppContext
38
+ from cubevis.toolbox import CubeMask
39
39
  from cubevis.bokeh.utils import svg_icon
40
40
  from bokeh.io import reset_output as reset_bokeh_output
41
41
  from bokeh.io import output_notebook
@@ -149,12 +149,6 @@ class CreateMask:
149
149
  if False a mask path which does not exist results in an exception
150
150
  '''
151
151
 
152
- ###
153
- ### Create application context (which includes a temporary directory).
154
- ### This sets the title of the plot.
155
- ###
156
- self._app_state = AppContext( 'Create Mask' )
157
-
158
152
  ###
159
153
  ### widgets shared across image tabs (masking multiple images)
160
154
  ###
@@ -382,7 +376,7 @@ class CreateMask:
382
376
  app_state={ ### while the state dictionary itself
383
377
  'name': 'create mask', ### is used, these particular element
384
378
  'initialized': True ### are not currently used for anything
385
- } )
379
+ }, title='Create Mask' )
386
380
 
387
381
  ###
388
382
  ### Keep track of which image is currently active in appstate.image_name (which is
@@ -411,7 +405,7 @@ class CreateMask:
411
405
  ### output_file(self._imagename+'_webpage/index.html')
412
406
  pass
413
407
 
414
- show(self._fig['layout'])
408
+ self._fig['layout'].show( )
415
409
 
416
410
  def _asyncio_loop( self ):
417
411
  '''return the event loop which can be mixed in with an existing event loop
@@ -35,7 +35,7 @@ from contextlib import asynccontextmanager
35
35
  from bokeh.layouts import row, column, grid
36
36
  from bokeh.plotting import show
37
37
  from bokeh.models import Button, CustomJS, TabPanel, Tabs, Spacer, Div, Dropdown
38
- from cubevis.toolbox import CubeMask, AppContext, RegionList
38
+ from cubevis.toolbox import CubeMask, RegionList
39
39
  from cubevis.bokeh.utils import svg_icon
40
40
  from bokeh.io import curdoc
41
41
  from bokeh.io import reset_output as reset_bokeh_output
@@ -141,12 +141,6 @@ class CreateRegion:
141
141
  path(s) to CASA image for which interactive regions will be drawn
142
142
  '''
143
143
 
144
- ###
145
- ### Create application context (which includes a temporary directory).
146
- ### This sets the title of the plot.
147
- ###
148
- self._app_state = AppContext( 'Create Region' )
149
-
150
144
  ###
151
145
  ### widgets shared across image tabs (masking multiple images)
152
146
  ###
@@ -435,7 +429,7 @@ class CreateRegion:
435
429
  app_state={ ### while the state dictionary itself
436
430
  'name': 'create region', ### is used, these particular element
437
431
  'initialized': True ### are not currently used for anything
438
- } )
432
+ }, title='Create Region' )
439
433
 
440
434
  ###
441
435
  ### Keep track of which image is currently active in appstate.image_name (which is
@@ -464,7 +458,7 @@ class CreateRegion:
464
458
  ### output_file(self._imagename+'_webpage/index.html')
465
459
  pass
466
460
 
467
- show(self._fig['layout'])
461
+ self._fig['layout'].show( )
468
462
 
469
463
  def _asyncio_loop( self ):
470
464
  '''return the event loop which can be mixed in with an existing event loop
@@ -87,7 +87,8 @@ class InteractiveClean:
87
87
  interpolation='nearest', ... )( ) )
88
88
  '''
89
89
  self._id = uuid4( )
90
+ self._ui.exclusion_mgr.set_mode("tab")
90
91
  context = exe.Context( exe.Mode.SYNC )
91
92
  bokeh_ui, exec_task = self._ui( context, self._id )
92
- show(bokeh_ui)
93
+ bokeh_ui.show( )
93
94
  return context.execute( exec_task, self._id )
@@ -1849,7 +1849,8 @@ class InteractiveClean:
1849
1849
  interpolation='nearest', ... )( ) )
1850
1850
  '''
1851
1851
  self._id = uuid4( )
1852
+ self._ui.exclusion_mgr.set_mode("tab")
1852
1853
  context = exe.Context( exe.Mode.SYNC )
1853
1854
  bokeh_ui, exec_task = self._ui( context, self._id )
1854
- show(bokeh_ui)
1855
+ bokeh_ui.show( )
1855
1856
  return context.execute( exec_task, self._id )
@@ -43,6 +43,22 @@ from cubevis.utils import find_pkg, load_pkg
43
43
  from cubevis.toolbox import InteractiveCleanUI
44
44
  from cubevis import exe
45
45
 
46
+ class DisplayContext:
47
+ def __init__(self, exclusion_manager):
48
+ self.exclusion_manager = exclusion_manager
49
+ self._custom_show_called = False
50
+
51
+ def on_show(self):
52
+ self._custom_show_called = True
53
+ self.exclusion_manager.set_mode('cell-custom-show')
54
+
55
+ def on_to_serializable(self):
56
+ # Only set mode if custom_show hasn't been called
57
+ if not self._custom_show_called:
58
+ self.exclusion_manager.set_mode('cell-bokeh-show')
59
+ # If custom_show was called, we're already in 'cell-custom-show' mode
60
+ # and don't need to do anything
61
+
46
62
  class InteractiveCleanNotebook:
47
63
  r'''InteractiveCleanNotebook(...) implements interactive clean using Bokeh
48
64
  {{docstring}}
@@ -107,6 +123,13 @@ class InteractiveCleanNotebook:
107
123
  def startup( ):
108
124
  self._future = context.execute( exec_task, self._id )
109
125
 
126
+ display_ctx = DisplayContext(self._ui.exclusion_mgr)
110
127
  ### name is used in summary output of the Showable
111
- showed = Showable(bokeh_ui, startup, self.get_future, name="iclean-jpy")
128
+ showed = Showable(
129
+ bokeh_ui,
130
+ startup,
131
+ self.get_future,
132
+ name="iclean-jpy",
133
+ display_context=display_ctx
134
+ )
112
135
  return showed
@@ -42,6 +42,22 @@ from cubevis.utils import find_pkg, load_pkg
42
42
  from cubevis.toolbox import InteractiveCleanUI
43
43
  from cubevis import exe
44
44
 
45
+ class DisplayContext:
46
+ def __init__(self, exclusion_manager):
47
+ self.exclusion_manager = exclusion_manager
48
+ self._custom_show_called = False
49
+
50
+ def on_show(self):
51
+ self._custom_show_called = True
52
+ self.exclusion_manager.set_mode('cell-custom-show')
53
+
54
+ def on_to_serializable(self):
55
+ # Only set mode if custom_show hasn't been called
56
+ if not self._custom_show_called:
57
+ self.exclusion_manager.set_mode('cell-bokeh-show')
58
+ # If custom_show was called, we're already in 'cell-custom-show' mode
59
+ # and don't need to do anything
60
+
45
61
  class InteractiveCleanNotebook:
46
62
  r'''InteractiveCleanNotebook(...) implements interactive clean using Bokeh
47
63
  tclean ---- Radio Interferometric Image Reconstruction
@@ -1869,6 +1885,13 @@ class InteractiveCleanNotebook:
1869
1885
  def startup( ):
1870
1886
  self._future = context.execute( exec_task, self._id )
1871
1887
 
1888
+ display_ctx = DisplayContext(self._ui.exclusion_mgr)
1872
1889
  ### name is used in summary output of the Showable
1873
- showed = Showable(bokeh_ui, startup, self.get_future, name="iclean-jpy")
1890
+ showed = Showable(
1891
+ bokeh_ui,
1892
+ startup,
1893
+ self.get_future,
1894
+ name="iclean-jpy",
1895
+ display_context=display_ctx
1896
+ )
1874
1897
  return showed
@@ -28,6 +28,5 @@
28
28
  '''Common tools used in creating applications.'''
29
29
 
30
30
  from ._cube import CubeMask
31
- from ._app_context import AppContext
32
31
  from ._region_list import RegionList
33
32
  from ._interactive_clean_ui import InteractiveCleanUI