scanpath-studio 0.40.0__tar.gz → 0.41.0__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 (156) hide show
  1. {scanpath_studio-0.40.0/scanpath_studio.egg-info → scanpath_studio-0.41.0}/PKG-INFO +13 -13
  2. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/README.md +12 -12
  3. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/__init__.py +1 -1
  4. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/analysis_recipe.py +2 -2
  5. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/api.py +69 -64
  6. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/app.py +1 -1
  7. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/build_info.py +19 -1
  8. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/cli.py +339 -328
  9. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/code_snippet.py +1 -0
  10. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/controls.py +12 -1
  11. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/datasets.py +1 -1
  12. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/plot_config.py +1 -0
  13. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/plots.py +166 -4
  14. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/session_keys.py +4 -0
  15. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/tabs.py +3 -1
  16. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/updates.py +1 -1
  17. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/url_state.py +6 -0
  18. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0/scanpath_studio.egg-info}/PKG-INFO +13 -13
  19. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/LICENSE +0 -0
  20. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/MANIFEST.in +0 -0
  21. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/NOTICE +0 -0
  22. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/pyproject.toml +0 -0
  23. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/__main__.py +0 -0
  24. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/aggregation.py +0 -0
  25. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/alignment.py +0 -0
  26. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/animation_export.py +0 -0
  27. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/annotations.py +0 -0
  28. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/assets/scanpath_studio_title_logo.png +0 -0
  29. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/assets/scanpath_studio_title_logo_dark.png +0 -0
  30. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/authoring.py +0 -0
  31. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/authoring_component.py +0 -0
  32. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/color_picker_commit.py +0 -0
  33. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/column_names.py +0 -0
  34. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/compare_source.py +0 -0
  35. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/computations.py +0 -0
  36. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/constants.py +0 -0
  37. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/crash_report.py +0 -0
  38. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/data.py +0 -0
  39. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/data_health.py +0 -0
  40. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/dataset_table.py +0 -0
  41. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/debug_log.py +0 -0
  42. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/desktop_update.py +0 -0
  43. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/easter_egg.py +0 -0
  44. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/experimental_setup.py +0 -0
  45. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/export.py +0 -0
  46. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/export_status.py +0 -0
  47. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/feedback.py +0 -0
  48. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/fields.py +0 -0
  49. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/html_embed.py +0 -0
  50. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/illustration.py +0 -0
  51. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/loading.py +0 -0
  52. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/measures.py +0 -0
  53. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/menu.py +0 -0
  54. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/metadata.py +0 -0
  55. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/model_scanpaths.py +0 -0
  56. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/multipart.py +0 -0
  57. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/onestop_shard.py +0 -0
  58. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/persistence.py +0 -0
  59. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/preprocessing.py +0 -0
  60. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/progress.py +0 -0
  61. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/fixations.csv +0 -0
  62. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/fixations.parquet +0 -0
  63. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/ia.csv +0 -0
  64. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/ia.parquet +0 -0
  65. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_a.png +0 -0
  66. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_b.png +0 -0
  67. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_c.png +0 -0
  68. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__answer_d.png +0 -0
  69. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__paragraph.png +0 -0
  70. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_1_Ele__question.png +0 -0
  71. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_a.png +0 -0
  72. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_b.png +0 -0
  73. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_c.png +0 -0
  74. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__answer_d.png +0 -0
  75. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__paragraph.png +0 -0
  76. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_2_Ele__question.png +0 -0
  77. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_a.png +0 -0
  78. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_b.png +0 -0
  79. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_c.png +0 -0
  80. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__answer_d.png +0 -0
  81. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__paragraph.png +0 -0
  82. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_3_Adv__question.png +0 -0
  83. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_a.png +0 -0
  84. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_b.png +0 -0
  85. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_c.png +0 -0
  86. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__answer_d.png +0 -0
  87. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__paragraph.png +0 -0
  88. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_4_Adv__question.png +0 -0
  89. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_a.png +0 -0
  90. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_b.png +0 -0
  91. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_c.png +0 -0
  92. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__answer_d.png +0 -0
  93. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__paragraph.png +0 -0
  94. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_5_Adv__question.png +0 -0
  95. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_a.png +0 -0
  96. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_b.png +0 -0
  97. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_c.png +0 -0
  98. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__answer_d.png +0 -0
  99. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__paragraph.png +0 -0
  100. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_1_6_Ele__question.png +0 -0
  101. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_a.png +0 -0
  102. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_b.png +0 -0
  103. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_c.png +0 -0
  104. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__answer_d.png +0 -0
  105. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__paragraph.png +0 -0
  106. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_1_Adv__question.png +0 -0
  107. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_a.png +0 -0
  108. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_b.png +0 -0
  109. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_c.png +0 -0
  110. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__answer_d.png +0 -0
  111. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__paragraph.png +0 -0
  112. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_2_Adv__question.png +0 -0
  113. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_a.png +0 -0
  114. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_b.png +0 -0
  115. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_c.png +0 -0
  116. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__answer_d.png +0 -0
  117. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__paragraph.png +0 -0
  118. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_3_Adv__question.png +0 -0
  119. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_a.png +0 -0
  120. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_b.png +0 -0
  121. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_c.png +0 -0
  122. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__answer_d.png +0 -0
  123. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__paragraph.png +0 -0
  124. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_4_Ele__question.png +0 -0
  125. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_a.png +0 -0
  126. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_b.png +0 -0
  127. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_c.png +0 -0
  128. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__answer_d.png +0 -0
  129. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__paragraph.png +0 -0
  130. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_5_Ele__question.png +0 -0
  131. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_a.png +0 -0
  132. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_b.png +0 -0
  133. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_c.png +0 -0
  134. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__answer_d.png +0 -0
  135. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__paragraph.png +0 -0
  136. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/2_2_6_Ele__question.png +0 -0
  137. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/images/manifest.csv +0 -0
  138. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/raw_gaze.csv +0 -0
  139. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/sample_data/raw_gaze.parquet +0 -0
  140. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/save_dialog.py +0 -0
  141. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/similarity.py +0 -0
  142. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/streamlit_entry.py +0 -0
  143. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/styles.py +0 -0
  144. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/synthetic.py +0 -0
  145. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/tour.py +0 -0
  146. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/truncation_tooltip.py +0 -0
  147. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/update_sample_data.py +0 -0
  148. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/utils.py +0 -0
  149. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/wizard.py +0 -0
  150. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio/wizard_shell.py +0 -0
  151. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/SOURCES.txt +0 -0
  152. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/dependency_links.txt +0 -0
  153. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/entry_points.txt +0 -0
  154. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/requires.txt +0 -0
  155. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/scanpath_studio.egg-info/top_level.txt +0 -0
  156. {scanpath_studio-0.40.0 → scanpath_studio-0.41.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scanpath-studio
3
- Version: 0.40.0
3
+ Version: 0.41.0
4
4
  Summary: Interactive visualization of eye-tracking data from reading studies: draw, replay and compare scanpaths, fixations, saccades and gaze heatmaps over the text, and export figures and tables.
5
5
  Author: Keren Gruteke Klein, Maya Grossman, Ella Lion, Deborah N. Jakobi, David R. Reich, Lena Jäger, Yevgeni Berzak
6
6
  Author-email: Omer Shubi <lacclab.technion@gmail.com>
@@ -60,11 +60,11 @@ Dynamic: license-file
60
60
 
61
61
  **See how people read.**
62
62
 
63
- A free, open-source app for exploring eye movements in reading. Check data quality, compare readers, and create figures and scanpath videos — no coding required.
63
+ A free, open-source app for exploring eye movements in reading. Check data quality, compare participants, and make figures and replay videos. No coding required.
64
64
 
65
65
  **[Try it online](https://scanpath-studio.streamlit.app)** · **[Download the app](https://lacclab.github.io/scanpath-studio/install/)** · **[Browse the gallery](https://lacclab.github.io/scanpath-studio/gallery/)**
66
66
 
67
- ![Scanpath Studio: exploring trials, replaying a reading, comparing readers and analyzing a corpus](https://raw.githubusercontent.com/lacclab/scanpath-studio/main/docs/assets/app_demo.gif)
67
+ ![Scanpath Studio: stepping through trials, replaying a reading, comparing two participants and analyzing a corpus](https://raw.githubusercontent.com/lacclab/scanpath-studio/main/docs/assets/app_demo.gif)
68
68
 
69
69
  [![PyPI](https://img.shields.io/pypi/v/scanpath-studio.svg)](https://pypi.org/project/scanpath-studio/)
70
70
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/lacclab/scanpath-studio/blob/main/LICENSE)
@@ -72,21 +72,21 @@ A free, open-source app for exploring eye movements in reading. Check data quali
72
72
 
73
73
  ## From your data to a figure
74
74
 
75
- - **Bring your own data.** Load a Fixations table and a Words table, with optional raw gaze and metadata. [Data guide →](https://lacclab.github.io/scanpath-studio/guides/loading-data/)
76
- - **Explore and replay.** View fixations, saccades and heatmaps; filter trials, then star or tag the ones to revisit.
77
- - **Compare readings.** Overlay two scanpaths or place them side by side, across readers, conditions or datasets.
78
- - **Analyze a corpus.** Explore the reading measures in your data by text, reader or group.
79
- - **Export and share.** Save publication-ready figures, GIF/MP4 replays or a batch of trials, and share a view with a link.
75
+ - **Bring your own data.** Load your Fixations, Words or Raw gaze tables, and add participant, trial and text metadata. [Data guide →](https://lacclab.github.io/scanpath-studio/guides/loading-data/)
76
+ - **Explore and replay.** View fixations, saccades and heatmaps, and replay a reading fixation by fixation. Filter trials, then star or tag the ones to revisit.
77
+ - **Compare readings.** Overlay two scanpaths or place them side by side, from two participants, conditions or datasets.
78
+ - **Analyze a corpus.** Summarize the reading measures in your data by text, participant or group.
79
+ - **Export and share.** Save publication-ready figures, GIF or MP4 replays, or a batch of trials. Share a view as a link.
80
80
 
81
81
  [Follow a tutorial](https://lacclab.github.io/scanpath-studio/tutorials/) · [See it in action](https://lacclab.github.io/scanpath-studio/#from-recording-to-publication)
82
82
 
83
83
  ## Run it on your computer
84
84
 
85
- Download the desktop app — **no Python needed**:
85
+ Download the desktop app. You don't need Python.
86
86
 
87
87
  [Windows](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-windows-x86_64-setup.exe) · [macOS (Apple silicon)](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-macos-arm64.dmg) · [Linux](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-linux-x86_64.tar.gz)
88
88
 
89
- Local installs keep your data on your machine. To try the app first, open the [online demo](https://scanpath-studio.streamlit.app) with its bundled data.
89
+ A local install keeps your data on your computer. To try the app first, open the bundled demo [online](https://scanpath-studio.streamlit.app).
90
90
 
91
91
  <details>
92
92
  <summary>Prefer pip? Install with Python 3.11–3.14</summary>
@@ -102,14 +102,14 @@ scanpath-studio
102
102
 
103
103
  ## Python & command line
104
104
 
105
- Build a figure in the app, then copy its Python or CLI code from **Share**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) include scripting examples alongside the app instructions.
105
+ Build a figure in the app, then copy its Python or CLI code from **Share** → **Code**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) also include Python and CLI examples.
106
106
 
107
107
  [Python API](https://lacclab.github.io/scanpath-studio/api/) · [CLI reference](https://lacclab.github.io/scanpath-studio/cli/)
108
108
 
109
109
  ## Research & community
110
110
 
111
- Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/cite/) (Shubi et al., 2026). The bundled demo is a subset of [OneStop Eye Movements](https://doi.org/10.1038/s41597-025-06272-2).
111
+ Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/about/#cite-scanpath-studio) (Shubi et al., 2026). The bundled demo is a subset of [OneStop Eye Movements](https://doi.org/10.1038/s41597-025-06272-2).
112
112
 
113
113
  [Report a bug or request a feature](https://github.com/lacclab/scanpath-studio/issues) · [Contribute](https://github.com/lacclab/scanpath-studio/blob/main/CONTRIBUTING.md) · [Code of conduct](https://github.com/lacclab/scanpath-studio/blob/main/CODE_OF_CONDUCT.md)
114
114
 
115
- <sub>Developed with AI assistance. As with any research software, cross-check results before using them in published work.</sub>
115
+ <sub>Scanpath Studio was developed with AI assistance. As with any research software, cross-check its results before you rely on them in published work.</sub>
@@ -2,11 +2,11 @@
2
2
 
3
3
  **See how people read.**
4
4
 
5
- A free, open-source app for exploring eye movements in reading. Check data quality, compare readers, and create figures and scanpath videos — no coding required.
5
+ A free, open-source app for exploring eye movements in reading. Check data quality, compare participants, and make figures and replay videos. No coding required.
6
6
 
7
7
  **[Try it online](https://scanpath-studio.streamlit.app)** · **[Download the app](https://lacclab.github.io/scanpath-studio/install/)** · **[Browse the gallery](https://lacclab.github.io/scanpath-studio/gallery/)**
8
8
 
9
- ![Scanpath Studio: exploring trials, replaying a reading, comparing readers and analyzing a corpus](https://raw.githubusercontent.com/lacclab/scanpath-studio/main/docs/assets/app_demo.gif)
9
+ ![Scanpath Studio: stepping through trials, replaying a reading, comparing two participants and analyzing a corpus](https://raw.githubusercontent.com/lacclab/scanpath-studio/main/docs/assets/app_demo.gif)
10
10
 
11
11
  [![PyPI](https://img.shields.io/pypi/v/scanpath-studio.svg)](https://pypi.org/project/scanpath-studio/)
12
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/lacclab/scanpath-studio/blob/main/LICENSE)
@@ -14,21 +14,21 @@ A free, open-source app for exploring eye movements in reading. Check data quali
14
14
 
15
15
  ## From your data to a figure
16
16
 
17
- - **Bring your own data.** Load a Fixations table and a Words table, with optional raw gaze and metadata. [Data guide →](https://lacclab.github.io/scanpath-studio/guides/loading-data/)
18
- - **Explore and replay.** View fixations, saccades and heatmaps; filter trials, then star or tag the ones to revisit.
19
- - **Compare readings.** Overlay two scanpaths or place them side by side, across readers, conditions or datasets.
20
- - **Analyze a corpus.** Explore the reading measures in your data by text, reader or group.
21
- - **Export and share.** Save publication-ready figures, GIF/MP4 replays or a batch of trials, and share a view with a link.
17
+ - **Bring your own data.** Load your Fixations, Words or Raw gaze tables, and add participant, trial and text metadata. [Data guide →](https://lacclab.github.io/scanpath-studio/guides/loading-data/)
18
+ - **Explore and replay.** View fixations, saccades and heatmaps, and replay a reading fixation by fixation. Filter trials, then star or tag the ones to revisit.
19
+ - **Compare readings.** Overlay two scanpaths or place them side by side, from two participants, conditions or datasets.
20
+ - **Analyze a corpus.** Summarize the reading measures in your data by text, participant or group.
21
+ - **Export and share.** Save publication-ready figures, GIF or MP4 replays, or a batch of trials. Share a view as a link.
22
22
 
23
23
  [Follow a tutorial](https://lacclab.github.io/scanpath-studio/tutorials/) · [See it in action](https://lacclab.github.io/scanpath-studio/#from-recording-to-publication)
24
24
 
25
25
  ## Run it on your computer
26
26
 
27
- Download the desktop app — **no Python needed**:
27
+ Download the desktop app. You don't need Python.
28
28
 
29
29
  [Windows](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-windows-x86_64-setup.exe) · [macOS (Apple silicon)](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-macos-arm64.dmg) · [Linux](https://github.com/lacclab/scanpath-studio/releases/latest/download/ScanpathStudio-linux-x86_64.tar.gz)
30
30
 
31
- Local installs keep your data on your machine. To try the app first, open the [online demo](https://scanpath-studio.streamlit.app) with its bundled data.
31
+ A local install keeps your data on your computer. To try the app first, open the bundled demo [online](https://scanpath-studio.streamlit.app).
32
32
 
33
33
  <details>
34
34
  <summary>Prefer pip? Install with Python 3.11–3.14</summary>
@@ -44,14 +44,14 @@ scanpath-studio
44
44
 
45
45
  ## Python & command line
46
46
 
47
- Build a figure in the app, then copy its Python or CLI code from **Share**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) include scripting examples alongside the app instructions.
47
+ Build a figure in the app, then copy its Python or CLI code from **Share** → **Code**. The [feature guides](https://lacclab.github.io/scanpath-studio/guides/) also include Python and CLI examples.
48
48
 
49
49
  [Python API](https://lacclab.github.io/scanpath-studio/api/) · [CLI reference](https://lacclab.github.io/scanpath-studio/cli/)
50
50
 
51
51
  ## Research & community
52
52
 
53
- Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/cite/) (Shubi et al., 2026). The bundled demo is a subset of [OneStop Eye Movements](https://doi.org/10.1038/s41597-025-06272-2).
53
+ Using Scanpath Studio in your research? [Cite the software](https://lacclab.github.io/scanpath-studio/about/#cite-scanpath-studio) (Shubi et al., 2026). The bundled demo is a subset of [OneStop Eye Movements](https://doi.org/10.1038/s41597-025-06272-2).
54
54
 
55
55
  [Report a bug or request a feature](https://github.com/lacclab/scanpath-studio/issues) · [Contribute](https://github.com/lacclab/scanpath-studio/blob/main/CONTRIBUTING.md) · [Code of conduct](https://github.com/lacclab/scanpath-studio/blob/main/CODE_OF_CONDUCT.md)
56
56
 
57
- <sub>Developed with AI assistance. As with any research software, cross-check results before using them in published work.</sub>
57
+ <sub>Scanpath Studio was developed with AI assistance. As with any research software, cross-check its results before you rely on them in published work.</sub>
@@ -41,7 +41,7 @@ __all__ = [
41
41
  # against. `__version__` is the exact build, worked out from it lazily
42
42
  # (build_info.py) — "0.35.0" at the release itself,
43
43
  # "0.35.0.post3+g8f18219" three commits after it.
44
- __release__ = "0.40.0"
44
+ __release__ = "0.41.0"
45
45
 
46
46
  # Public headless API (see api.py / datasets.py). Resolved lazily so
47
47
  # `import scanpath_studio` stays cheap and doesn't pull in pandas/plotly/
@@ -10,7 +10,7 @@ It **references** the dataset by name and never embeds it: no table rows, and
10
10
  no annotation notes (a *Favorites only* or tag filter is recorded as the filter
11
11
  it is, not as the annotations behind it). Filters and figure settings are kept
12
12
  apart on purpose — this file holds the filters and no styling; the figure
13
- settings file (🔗 Share → File) holds styling and no filters — so the recipe
13
+ settings file (🔗 Share → Settings file) holds styling and no filters — so the recipe
14
14
  says so in ``excludes`` rather than leaving a reader to guess.
15
15
 
16
16
  It describes the current analysis only. There is no runner and no workspace
@@ -36,7 +36,7 @@ RECIPE_VERSION = 1
36
36
  EXCLUDES = {
37
37
  "figure_settings": (
38
38
  "Palette, canvas and fonts are in the settings file (Scanpath → "
39
- "Share → File). That file has no trial filters."
39
+ "Share → Settings file). That file has no trial filters."
40
40
  ),
41
41
  "data": (
42
42
  "The recipe has no table rows or annotation notes. It names the "
@@ -107,7 +107,7 @@ if TYPE_CHECKING:
107
107
  def build_authored_scanpath(
108
108
  text: str, events: pd.DataFrame | None = None, **layout_options: Any
109
109
  ) -> tuple[pd.DataFrame, pd.DataFrame]:
110
- """Build normalized word and fixation frames from hand-authored reading events.
110
+ """Build normalized Words and Fixations frames from a hand-drawn scanpath.
111
111
 
112
112
  Args:
113
113
  text: The text to lay out as word boxes. Explicit line breaks are kept.
@@ -132,7 +132,7 @@ def build_authored_scanpath(
132
132
  def load_authored_scanpath(
133
133
  source: str | Path,
134
134
  ) -> tuple[pd.DataFrame, pd.DataFrame]:
135
- """Load a draft file as normalized word and fixation frames.
135
+ """Load a draft file as normalized Words and Fixations frames.
136
136
 
137
137
  Args:
138
138
  source: The JSON the app's **Download draft (JSON)** saves on *Draw a
@@ -816,7 +816,7 @@ def load_scanpath_data(
816
816
  keep_columns: Iterable[str] | None = None,
817
817
  names: str = NAMES_SOURCE,
818
818
  ) -> ScanpathData:
819
- """Load and normalize a words table and/or a fixations table.
819
+ """Load and normalize a Words table, a Fixations table, or both.
820
820
 
821
821
  Columns keep the names your files give them: ``CURRENT_FIX_DURATION``, not
822
822
  ``duration_ms``. A column Scanpath Studio built, converted, computed or changed
@@ -831,27 +831,27 @@ def load_scanpath_data(
831
831
 
832
832
  - **One table is enough.** Leave out the table a dataset doesn't have: it comes back
833
833
  as an empty frame, and the plots skip that layer.
834
- - **Stimulus-level words.** A words table without a participant column holds each
834
+ - **Stimulus-level words.** A Words table without a participant column holds each
835
835
  text's word boxes once, and they are copied onto every trial in the fixations.
836
- Each trial is matched by its trial id, else the trial id it had before a repeat's
837
- ``_r2`` suffix, else its ``text_id`` (for trial ids that embed the participant).
836
+ Each trial is matched by its trial ID, else the trial ID it had before a repeat's
837
+ ``_r2`` suffix, else its ``text_id`` (for trial IDs that embed the participant).
838
838
  Trials that match none raise a ``data.StimulusJoinWarning`` (a ``UserWarning``).
839
- - **Fixations without x/y** but with a word or AOI id are placed at the centers of
840
- their word boxes.
839
+ - **Fixations without x/y** but with a word ID are placed at the centers of their
840
+ word boxes.
841
841
  - **Bookkeeping columns.** Columns named in ``data.INTERNAL_COLUMNS`` are the
842
842
  pipeline's own; ``data.drop_internal_columns`` removes them.
843
843
 
844
844
  Args:
845
- words: The words (AOI) table: a DataFrame; a ``.csv``, ``.tsv``, ``.txt``,
845
+ words: The Words table, one word box per row: a DataFrame; a ``.csv``, ``.tsv``, ``.txt``,
846
846
  ``.tab``, ``.parquet``, ``.feather``, ``.xlsx`` or ``.xls`` file, or a
847
847
  ``.zip`` of them; a glob pattern; or a list of paths. Several files (one per
848
848
  participant and/or text) are concatenated, with each file's stem in a
849
849
  ``source_file`` column.
850
- fixations: The fixations table, in any form ``words`` takes.
851
- word_schema: A mapping from field to column name for the words table, replacing
850
+ fixations: The Fixations table, in any form ``words`` takes.
851
+ word_schema: A mapping from field to column name for the Words table, replacing
852
852
  auto-detection. Start from
853
853
  [`propose_schema`][scanpath_studio.api.propose_schema].
854
- fix_schema: The same for the fixations table.
854
+ fix_schema: The same for the Fixations table.
855
855
  trial_parts_manifest: A nested parent-trial/parts definition, for datasets whose
856
856
  tables identify screens through arbitrary selector columns. Explicit
857
857
  ``screen_id`` / ``screen_index`` columns can instead be mapped in each
@@ -882,11 +882,11 @@ def load_scanpath_data(
882
882
  column names auto-detection looked for, and the columns the table has.
883
883
  ValueError: ``image_root`` does not exist, or ``image_pattern`` is absolute or
884
884
  climbs out of it.
885
- data.StimulusJoinError: A stimulus-level words table shares neither a trial id
885
+ data.StimulusJoinError: A stimulus-level Words table shares neither a trial ID
886
886
  nor a ``text_id`` with any trial (or, multipart, with every screen a trial
887
887
  has fixations on). A ``ValueError``.
888
- data.UnplacedFixationsError: The fixations map no x/y and their word/AOI id
889
- column holds no numbers, so none could be placed. A ``ValueError``.
888
+ data.UnplacedFixationsError: The fixations map no x/y and their word ID column
889
+ holds no numbers, so none could be placed. A ``ValueError``.
890
890
  """
891
891
  _check_names_choice(names)
892
892
  if words is None and fixations is None:
@@ -1087,12 +1087,12 @@ def load_participant_metadata(
1087
1087
 
1088
1088
  Args:
1089
1089
  table: A DataFrame, or a path or glob to a CSV, TSV, Parquet or Excel file, or a
1090
- list of them, stacked into one table. It holds an id column plus anything
1090
+ list of them, stacked into one table. It holds an ID column plus anything
1091
1091
  known about each participant (``native_language``, ``age``, a comprehension
1092
1092
  score).
1093
- id_column: The participant-id column. Defaults to the first recognized spelling
1093
+ id_column: The participant ID column. Defaults to the first recognized spelling
1094
1094
  (``participant_id``, ``subject``, ``RECORDING_SESSION_LABEL``, …).
1095
- participants: A normalized frame or a list of ids to validate the join against.
1095
+ participants: A normalized frame or a list of IDs to validate the join against.
1096
1096
  The result's ``.report`` then names the participants missing from either
1097
1097
  side.
1098
1098
 
@@ -1103,7 +1103,7 @@ def load_participant_metadata(
1103
1103
  Examples:
1104
1104
  ```python
1105
1105
  words, fixations = sps.load_sample_data()
1106
- meta = sps.load_participant_metadata("readers.csv", participants=fixations)
1106
+ meta = sps.load_participant_metadata("participants.csv", participants=fixations)
1107
1107
  meta.names # ('native_language', 'age')
1108
1108
  ```
1109
1109
  """
@@ -1144,18 +1144,18 @@ def load_trial_metadata(
1144
1144
  grain down. Nothing is broadcast onto the words or fixations frames.
1145
1145
 
1146
1146
  **The key is yours to state, and it changes what the table means.** Keyed by trial
1147
- id alone, a row describes a *text*, and every trial of it inherits that row. Keyed
1147
+ ID alone, a row describes a *text*, and every trial of it inherits that row. Keyed
1148
1148
  by participant **and** trial (``participant_column``), a row describes one *trial*.
1149
1149
  Nothing in a file says which a corpus is, so this is never inferred.
1150
1150
 
1151
1151
  Args:
1152
1152
  table: A DataFrame or file(s), as `load_participant_metadata` takes, stacked
1153
- into one table. It holds a trial-id column plus anything known about each
1153
+ into one table. It holds a trial ID column plus anything known about each
1154
1154
  trial (a list name, a condition, a per-trial comprehension score).
1155
- id_column: The trial-id column. Defaults to the first recognized spelling
1155
+ id_column: The trial ID column. Defaults to the first recognized spelling
1156
1156
  (``trial_id``, ``item_id``, ``TRIAL_INDEX``, …).
1157
- participant_column: The participant-id column, to key each row by participant
1158
- and trial. Left out, rows are keyed by trial id alone.
1157
+ participant_column: The participant ID column, to key each row by participant
1158
+ and trial. Left out, rows are keyed by trial ID alone.
1159
1159
  trials: A normalized fixations or words frame, or any frame with
1160
1160
  ``participant_id`` and ``trial_id``, to validate the join against. The
1161
1161
  result's ``.report`` then names the trials missing from either side.
@@ -1217,12 +1217,12 @@ def load_text_metadata(
1217
1217
 
1218
1218
  Args:
1219
1219
  table: A DataFrame or file(s), as `load_participant_metadata` takes, stacked
1220
- into one table. It holds a text-id column plus anything known about each
1220
+ into one table. It holds a text ID column plus anything known about each
1221
1221
  text (genre, difficulty, a stimulus-level comprehension score).
1222
- id_column: The text-id column, or several columns that build a composite id, as
1223
- the data's own Text ID mapping can. Defaults to the first recognized
1222
+ id_column: The text ID column, or several columns that build a composite ID, as
1223
+ the Text ID mapping can. Defaults to the first recognized
1224
1224
  spelling (``text_id``, ``paragraph_id``, ``stimulus_id``, …).
1225
- texts: A normalized fixations or words frame, or any iterable of text ids, to
1225
+ texts: A normalized fixations or words frame, or any iterable of text IDs, to
1226
1226
  validate the join against. The result's ``.report`` then names the texts
1227
1227
  missing from either side.
1228
1228
 
@@ -1262,9 +1262,10 @@ def load_text_metadata(
1262
1262
 
1263
1263
 
1264
1264
  def load_sample_data(*, names: str = NAMES_SOURCE) -> ScanpathData:
1265
- """Load the bundled OneStop demo, normalized and ready to plot.
1265
+ """Load the bundled demo, normalized and ready to plot.
1266
1266
 
1267
- Two participants, twelve trials each, every one with fixations. The frames carry the
1267
+ The bundled demo is a subset of OneStop: two participants, twelve trials each, every
1268
+ one with fixations. The frames carry the
1268
1269
  demo's recorded screen (OneStop's 2560×1440), so ``plot_scanpath`` draws them on it
1269
1270
  without a ``canvas_size``, as ``scanpath-studio render --sample`` does.
1270
1271
 
@@ -1821,7 +1822,7 @@ def list_trials(
1821
1822
  raw_gaze: A frame from [`load_raw_gaze`][scanpath_studio.api.load_raw_gaze].
1822
1823
 
1823
1824
  Returns:
1824
- The participant and trial ids, sorted by participant, then trial, under the
1825
+ The participant and trial IDs, sorted by participant, then trial, under the
1825
1826
  names the frames carry.
1826
1827
  """
1827
1828
  words, word_names = _named_in(words, "words", optional=True)
@@ -2369,7 +2370,8 @@ def figure_options(kind: str = "static", *, choices: bool = False) -> dict:
2369
2370
 
2370
2371
  The defaults are what a call actually renders with, the app's Scanpath design, so a
2371
2372
  script can diff its intended settings against what it would get. The *Figure
2372
- options* page of these docs lists them by section, with their ``render`` flags.
2373
+ options* section of the API reference lists them by section, with their ``render``
2374
+ flags.
2373
2375
 
2374
2376
  Args:
2375
2377
  kind: ``"static"`` for [`plot_scanpath`][scanpath_studio.api.plot_scanpath],
@@ -2541,9 +2543,9 @@ def plot_scanpath(
2541
2543
  [`load_scanpath_data`][scanpath_studio.api.load_scanpath_data], or ``None``
2542
2544
  for a dataset recorded as raw gaze alone.
2543
2545
  fixations: The normalized fixations frame, or ``None`` likewise.
2544
- participant: The participant id. It may be left out when the frames hold one
2546
+ participant: The participant ID. It may be left out when the frames hold one
2545
2547
  trial.
2546
- trial: The trial id. It may be left out when the frames hold one trial.
2548
+ trial: The trial ID. It may be left out when the frames hold one trial.
2547
2549
  screen: The screen of a multipart trial to draw. Left out, the first recorded
2548
2550
  screen; coordinate spaces are never concatenated.
2549
2551
  canvas_size: The monitor size in px, ``(width, height)``. Estimated from the
@@ -2561,9 +2563,11 @@ def plot_scanpath(
2561
2563
  but ``None`` or ``"off"`` raises ``ValueError``.
2562
2564
  drift_connectors: Experimental, with ``drift_correction``.
2563
2565
  fix_index_range: ``(start, end)``: draw only fixations ``start`` to ``end`` of
2564
- the trial, 1-based and both inclusive. The app's fixation-index window.
2565
- illustration: Apply the Illustration preset: fixations snapped to their lines,
2566
- arced saccades, uniform colors, no heatmap or word boxes. Keywords you pass
2566
+ the trial, 1-based and both inclusive. The app's **Filters & highlights** →
2567
+ **Fixations** → *Index range*.
2568
+ illustration: Apply the Illustration design: fixations snapped to their lines,
2569
+ arced saccades, one color for the fixations and one for the saccades, no
2570
+ heatmap or word boxes. Keywords you pass
2567
2571
  still win.
2568
2572
  illustration_label: ``"auto"`` labels the figure an illustration when it no
2569
2573
  longer shows the data as recorded; ``"show"`` and ``"hide"`` decide it.
@@ -2581,10 +2585,10 @@ def plot_scanpath(
2581
2585
  app's names) sets a group of colors at once. A color you pass explicitly
2582
2586
  wins.
2583
2587
  - ``show_stimulus_image=True`` draws the trial's own stimulus page under the
2584
- scanpath, as the app's 📄 Stimulus → image does: the ``image_path`` its
2588
+ scanpath, as the app's **Stimulus** → **Image** does: the ``image_path`` its
2585
2589
  rows carry (from ``load_scanpath_data(image_root=…)`` or
2586
2590
  [`attach_stimulus_images`][scanpath_studio.api.attach_stimulus_images];
2587
- the demo ships its pages), at its ``image_x`` / ``image_y``. A
2591
+ the bundled demo ships its pages), at its ``image_x`` / ``image_y``. A
2588
2592
  ``background_image=`` you pass wins.
2589
2593
  - ``fixation_snap_to_line=True`` moves each fixation up or down onto its
2590
2594
  text line, keeping its x: its word's line, else the nearest.
@@ -2750,9 +2754,9 @@ def animate_scanpath(
2750
2754
  """Build the animated replay of one trial's scanpath, or of two on one clock.
2751
2755
 
2752
2756
  Save it as interactive HTML with [`save_figure`][scanpath_studio.api.save_figure],
2753
- whose page keeps the replay's clock itself, or rasterize it to GIF or MP4 with
2754
- `animation_export.export_animation`, which lasts as long. ``fig.show()`` plays it on
2755
- Plotly's own frame queue, which runs slow.
2757
+ whose page keeps the replay's timing, or render it to GIF or MP4 with
2758
+ `animation_export.export_animation`, which keeps the same timing. ``fig.show()``
2759
+ plays it on Plotly's frame queue, which runs slower than the reading did.
2756
2760
 
2757
2761
  The replay takes a subset of [`plot_scanpath`][scanpath_studio.api.plot_scanpath]'s
2758
2762
  options (``figure_options("animation")``), and the ones they share default alike, so
@@ -2800,8 +2804,8 @@ def animate_scanpath(
2800
2804
  ``fixations_b`` when they are given, else in ``words`` / ``fixations``, as
2801
2805
  [`compare_scanpaths`][scanpath_studio.api.compare_scanpaths] takes it.
2802
2806
  dataset_b: The name of B's dataset, when it is another one. The pair is then
2803
- checked as an overlay, and B's participant ids are prefixed with it, so a
2804
- hover says whose participant it is.
2807
+ checked as an overlay, and B's participant IDs are prefixed with it, so a
2808
+ hover says which dataset the participant is from.
2805
2809
  setup: A's `experimental_setup.SetupSnapshot`, the screen the overlay check
2806
2810
  reads. A side without one is read off its data: the extent of that one
2807
2811
  trial, which rarely spans the whole screen, so state both when you know
@@ -3230,8 +3234,8 @@ def render_parent_trial(
3230
3234
  Args:
3231
3235
  words: The normalized words frame.
3232
3236
  fixations: The normalized fixations frame.
3233
- participant: The participant id.
3234
- trial: The parent trial's id.
3237
+ participant: The participant ID.
3238
+ trial: The parent trial's ID.
3235
3239
  animate: Build replays with
3236
3240
  [`animate_scanpath`][scanpath_studio.api.animate_scanpath] instead of
3237
3241
  [`plot_scanpath`][scanpath_studio.api.plot_scanpath]'s figures.
@@ -3239,8 +3243,8 @@ def render_parent_trial(
3239
3243
  ``layout.meta['transition_after_ms']`` records the delay before the next
3240
3244
  screen: zero for ``"instant"``, the observed parent-clock gap for
3241
3245
  ``"recorded"``.
3242
- screens: Only these screen ids (one may be given as a string), in the trial's
3243
- own order, as the app's Export → *Screens* does. ``None`` renders them all.
3246
+ screens: Only these screen IDs (one may be given as a string), in the trial's
3247
+ order, as the app's **Export** → *Screens* does. ``None`` renders them all.
3244
3248
  Each figure's ``screen_index`` meta stays the screen's place in the trial.
3245
3249
  **options: Passed to [`plot_scanpath`][scanpath_studio.api.plot_scanpath] or
3246
3250
  [`animate_scanpath`][scanpath_studio.api.animate_scanpath].
@@ -3250,7 +3254,7 @@ def render_parent_trial(
3250
3254
  A single-screen trial gives ``{"screen-1": figure}``.
3251
3255
 
3252
3256
  Raises:
3253
- ValueError: A screen id the trial does not have, or ``screens`` for a trial with
3257
+ ValueError: A screen ID the trial does not have, or ``screens`` for a trial with
3254
3258
  no screens to choose from.
3255
3259
  """
3256
3260
  if transition_mode not in {"instant", "recorded"}:
@@ -3413,9 +3417,9 @@ def compare_scanpaths(
3413
3417
 
3414
3418
  **Two datasets.** ``words_b`` / ``fixations_b`` draw B from a *different* dataset.
3415
3419
  Two datasets can hold the same ``(participant_id, trial_id)``, so B's participant
3416
- ids are namespaced with ``dataset_b`` inside the merged frames; without that, one
3417
- trial would silently render as two. The frames you pass in are never modified, and
3418
- only the trace labels show the namespace.
3420
+ IDs are prefixed with ``dataset_b`` inside the merged frames. Without that, the
3421
+ figure could draw the wrong reading, or rows of both mixed into one. The frames you
3422
+ pass in are not modified, and only the trace labels show the prefix.
3419
3423
 
3420
3424
  **The overlay gate.** An overlay needs both readings on screens of the same size, or
3421
3425
  it raises ``ValueError`` (the app falls back to side by side). That holds across
@@ -3453,7 +3457,7 @@ def compare_scanpaths(
3453
3457
  another dataset's.
3454
3458
  words_b: B's words frame, to draw B from another dataset.
3455
3459
  fixations_b: B's fixations frame, likewise.
3456
- dataset_b: The name B's participant ids are namespaced with.
3460
+ dataset_b: The name B's participant IDs are prefixed with.
3457
3461
  raw_gaze: Samples from [`load_raw_gaze`][scanpath_studio.api.load_raw_gaze],
3458
3462
  drawn under each reading's scanpath in its color. It serves both readings of
3459
3463
  one dataset; across datasets it is A's. Passing it turns the layer on;
@@ -3868,10 +3872,11 @@ def save_figure_layers(
3868
3872
  ) -> dict:
3869
3873
  """Save a scanpath figure as one file per layer, for editing.
3870
3874
 
3871
- Each *visible* layer (word boxes, fixations, saccades, heatmap, labels, stimulus
3872
- image, frame) is the full figure with only that layer's elements, on a transparent
3873
- background, at the same size and axis ranges. The files line up exactly when stacked
3874
- in Illustrator or Inkscape.
3875
+ Each *visible* layer (stimulus image, heatmap, word boxes, saccades, fixations, raw
3876
+ gaze, labels, frame) is the full figure with only that layer's elements, on a
3877
+ transparent background, at the same size and axis ranges. The files line up when
3878
+ stacked in Illustrator or Inkscape. The app's Export bundle writes the same files
3879
+ under *Layers as separate files*.
3875
3880
 
3876
3881
  Args:
3877
3882
  fig: A scanpath figure.
@@ -3940,7 +3945,7 @@ def figure_code(
3940
3945
  ) -> str:
3941
3946
  """Write the API or CLI code that reproduces a figure.
3942
3947
 
3943
- The headless twin of the app's 🔗 Share → *Reproduce this figure in code* block. Give
3948
+ The headless form of the app's **Share** → **Code**. Give
3944
3949
  it the arguments you would give [`plot_scanpath`][scanpath_studio.api.plot_scanpath]
3945
3950
  (``kind="static"``), [`animate_scanpath`][scanpath_studio.api.animate_scanpath]
3946
3951
  (``"animation"``) or [`compare_scanpaths`][scanpath_studio.api.compare_scanpaths]
@@ -3960,9 +3965,9 @@ def figure_code(
3960
3965
  "fixations": [...]}`` and so on. With ``show_raw_gaze=True``, ``"raw_gaze"``
3961
3966
  (and an optional ``"raw_gaze_schema"``) names the raw-gaze files; the demo
3962
3967
  uses its own.
3963
- participant: The participant id. Left empty, the snippet renders the first
3968
+ participant: The participant ID. Left empty, the snippet renders the first
3964
3969
  available trial, as ``render`` does.
3965
- trial: The trial id, likewise.
3970
+ trial: The trial ID, likewise.
3966
3971
  screen: A's screen of a multipart trial (``screen=``, ``--screen``).
3967
3972
  compare: B's ``(participant, trial)``, for a comparison or a co-animation.
3968
3973
  compare_screen: B's screen (``screen_b=``, ``--compare-screen``).
@@ -3980,7 +3985,7 @@ def figure_code(
3980
3985
  compare_labels: The two trace labels, when they are not the composed defaults:
3981
3986
  ``labels=`` and ``--label-a`` / ``--label-b``.
3982
3987
  canvas_size: The monitor size. Defaults to the screen ``render`` assumes for the
3983
- source (the demo's 2560×1440, PoTeC's 1680×1050, …), so both forms draw the
3988
+ source (the bundled demo's 2560×1440, PoTeC's 1680×1050, …), so both forms draw the
3984
3989
  same figure.
3985
3990
  base_font_size: As the builders take it.
3986
3991
  font_family: As the builders take it.
@@ -4106,9 +4111,9 @@ def cache_status() -> dict:
4106
4111
 
4107
4112
  A local or desktop app stores completed uploaded datasets, column mappings, view
4108
4113
  settings, saved designs, metadata tables and annotations under your cache folder, so
4109
- a refresh or a restart resumes where it left off. A hosted deployment never does.
4110
- The same information is in the app's 🗂️ Data Management → *Saved on this computer*,
4111
- and in ``scanpath-studio cache``.
4114
+ a refresh or a restart resumes where it left off. A hosted deployment, such as the
4115
+ online demo, keeps none. The same information is on the app's **Data Management**
4116
+ page under *Saved on this computer*, and in ``scanpath-studio cache``.
4112
4117
 
4113
4118
  | Key | What it holds |
4114
4119
  | --- | --- |
@@ -1461,7 +1461,7 @@ def _about_dialog() -> None:
1461
1461
  f"""
1462
1462
  Developed by [Omer Shubi](https://omershubi.github.io/),
1463
1463
  [Keren Gruteke Klein](https://kerengruteke.github.io/),
1464
- [Maya Grossman](https://www.linkedin.com/in/maya-harram-32b547292/),
1464
+ [Maya Grossman](https://www.linkedin.com/in/maya-grossman-32b547292/),
1465
1465
  [Ella Lion](https://ella-lion.github.io/),
1466
1466
  [Deborah N. Jakobi]({_DILI}/lab-members/jakobi.html),
1467
1467
  [David R. Reich]({_DILI}/lab-members/reich.html),
@@ -173,6 +173,25 @@ def read_checkout(
173
173
  return None
174
174
  if done.returncode != 0:
175
175
  return None
176
+ line = done.stdout.strip()
177
+ match = _DESCRIBE.match(line)
178
+ if not match:
179
+ return None
180
+ # describe counts every commit the tag lacks, merges included. The demo's
181
+ # `stable` branch takes each release as a merge commit, so there that
182
+ # count grows by one per release ever merged; count only the commits that
183
+ # brought changes of their own.
184
+ try:
185
+ counted = run(
186
+ ["git", "-C", str(root), "rev-list", "--count", "--no-merges"]
187
+ + [f"v{match['release']}..HEAD"],
188
+ **options,
189
+ )
190
+ distance = int(counted.stdout) if counted.returncode == 0 else None
191
+ except (OSError, subprocess.SubprocessError, ValueError):
192
+ distance = None
193
+ if distance is not None:
194
+ line = f"v{match['release']}-{distance}-g{match['commit']}"
176
195
  # `describe --dirty` would refresh and rewrite .git/index, which can make
177
196
  # a concurrent `git commit` fail; `status` with optional locks off does
178
197
  # not.
@@ -184,7 +203,6 @@ def read_checkout(
184
203
  dirty = status.returncode == 0 and bool(status.stdout.strip())
185
204
  except (OSError, subprocess.SubprocessError):
186
205
  dirty = False
187
- line = done.stdout.strip()
188
206
  return from_describe(line + "-dirty" if dirty else line)
189
207
 
190
208