figscribe 0.2.0__tar.gz → 0.3.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 (295) hide show
  1. figscribe-0.3.0/.github/workflows/installers.yml +87 -0
  2. {figscribe-0.2.0 → figscribe-0.3.0}/.github/workflows/release.yml +1 -1
  3. figscribe-0.3.0/.github/workflows/tests.yml +118 -0
  4. {figscribe-0.2.0 → figscribe-0.3.0}/.gitignore +5 -0
  5. figscribe-0.3.0/CHANGELOG.md +295 -0
  6. {figscribe-0.2.0 → figscribe-0.3.0}/CLAUDE.md +4 -2
  7. {figscribe-0.2.0 → figscribe-0.3.0}/PKG-INFO +92 -23
  8. {figscribe-0.2.0 → figscribe-0.3.0}/README.md +86 -18
  9. {figscribe-0.2.0 → figscribe-0.3.0}/docs/decisions.md +1327 -0
  10. figscribe-0.3.0/docs/testing.md +232 -0
  11. figscribe-0.3.0/examples/beam-profile/beam.figscribe +307 -0
  12. figscribe-0.3.0/examples/beam-profile/beam.npz +0 -0
  13. figscribe-0.3.0/examples/beam-profile/thumbnail.png +0 -0
  14. figscribe-0.3.0/examples/calibration/standards.csv +27 -0
  15. figscribe-0.3.0/examples/calibration/standards.figscribe +320 -0
  16. figscribe-0.3.0/examples/calibration/thumbnail.png +0 -0
  17. figscribe-0.3.0/examples/clock-stability/oscillators.csv +2003 -0
  18. figscribe-0.3.0/examples/clock-stability/oscillators.figscribe +184 -0
  19. figscribe-0.3.0/examples/clock-stability/thumbnail.png +0 -0
  20. figscribe-0.3.0/examples/diode-iv/diode.csv +73 -0
  21. figscribe-0.3.0/examples/diode-iv/diode.figscribe +197 -0
  22. figscribe-0.3.0/examples/diode-iv/thumbnail.png +0 -0
  23. figscribe-0.3.0/examples/distribution/samples.csv +1503 -0
  24. figscribe-0.3.0/examples/distribution/samples.figscribe +185 -0
  25. figscribe-0.3.0/examples/distribution/thumbnail.png +0 -0
  26. figscribe-0.3.0/examples/freezing-plateau/furnace.csv +543 -0
  27. figscribe-0.3.0/examples/freezing-plateau/furnace.figscribe +280 -0
  28. figscribe-0.3.0/examples/freezing-plateau/thumbnail.png +0 -0
  29. figscribe-0.3.0/examples/gaussian-scan/thumbnail.png +0 -0
  30. figscribe-0.3.0/examples/growth-curve/growth.csv +52 -0
  31. figscribe-0.3.0/examples/growth-curve/growth.figscribe +347 -0
  32. figscribe-0.3.0/examples/growth-curve/thumbnail.png +0 -0
  33. figscribe-0.3.0/examples/index.json +76 -0
  34. figscribe-0.3.0/examples/make.py +1056 -0
  35. figscribe-0.3.0/examples/noisy-sensor/sensor.csv +3003 -0
  36. figscribe-0.3.0/examples/noisy-sensor/sensor.figscribe +209 -0
  37. figscribe-0.3.0/examples/noisy-sensor/thumbnail.png +0 -0
  38. figscribe-0.3.0/examples/paper-figure/noise.csv +903 -0
  39. figscribe-0.3.0/examples/paper-figure/paper.figscribe +610 -0
  40. figscribe-0.3.0/examples/paper-figure/results.csv +83 -0
  41. figscribe-0.3.0/examples/paper-figure/thumbnail.png +0 -0
  42. figscribe-0.3.0/examples/rc-discharge/discharge.csv +63 -0
  43. figscribe-0.3.0/examples/rc-discharge/discharge.figscribe +292 -0
  44. figscribe-0.3.0/examples/rc-discharge/thumbnail.png +0 -0
  45. figscribe-0.3.0/examples/resonance/resonator.csv +93 -0
  46. figscribe-0.3.0/examples/resonance/resonator.figscribe +330 -0
  47. figscribe-0.3.0/examples/resonance/thumbnail.png +0 -0
  48. figscribe-0.3.0/packaging/figscribe.iss +47 -0
  49. figscribe-0.3.0/packaging/figscribe.spec +66 -0
  50. figscribe-0.3.0/packaging/launcher.py +14 -0
  51. figscribe-0.3.0/packaging/licenses/GPL-3.0.txt +674 -0
  52. figscribe-0.3.0/packaging/licenses/LGPL-3.0.txt +165 -0
  53. figscribe-0.3.0/packaging/make_installer.py +214 -0
  54. figscribe-0.3.0/packaging/notices.py +221 -0
  55. figscribe-0.3.0/packaging/requirements-build.txt +4 -0
  56. figscribe-0.3.0/packaging/smoke.py +125 -0
  57. {figscribe-0.2.0 → figscribe-0.3.0}/plan/roadmap.md +778 -26
  58. {figscribe-0.2.0 → figscribe-0.3.0}/pyproject.toml +7 -6
  59. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/__init__.py +1 -1
  60. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/allan.py +14 -0
  61. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/assistant/chat.py +3 -1
  62. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/cli.py +22 -8
  63. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/codegen/script.py +428 -33
  64. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/commands/history.py +5 -0
  65. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/commands/ops.py +6 -0
  66. figscribe-0.3.0/src/figscribe/convert.py +216 -0
  67. figscribe-0.3.0/src/figscribe/examples.py +58 -0
  68. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/fit/__init__.py +2 -0
  69. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/fit/expression.py +99 -2
  70. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/fit/models.py +28 -1
  71. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/fit/ops.py +6 -9
  72. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/fit/run.py +19 -0
  73. figscribe-0.3.0/src/figscribe/gui/about.py +37 -0
  74. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/app.py +8 -1
  75. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/axisform.py +62 -24
  76. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/document.py +15 -2
  77. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/effect.py +79 -1
  78. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fitdialog.py +69 -15
  79. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/mainwindow.py +379 -70
  80. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/names.py +34 -0
  81. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/outline.py +45 -4
  82. figscribe-0.3.0/src/figscribe/gui/palette.py +234 -0
  83. figscribe-0.3.0/src/figscribe/gui/path.py +68 -0
  84. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/plotdialog.py +5 -1
  85. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/preview.py +103 -12
  86. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/properties.py +681 -101
  87. figscribe-0.3.0/src/figscribe/gui/recovery.py +84 -0
  88. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/start.py +98 -8
  89. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/theme.py +39 -3
  90. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/widgets.py +45 -0
  91. figscribe-0.3.0/src/figscribe/io/files.py +92 -0
  92. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/__init__.py +2 -0
  93. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/artists.py +87 -7
  94. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/axes.py +3 -0
  95. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/figure.py +11 -4
  96. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/styles.py +3 -0
  97. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/validate.py +62 -12
  98. figscribe-0.3.0/src/figscribe/reduce.py +122 -0
  99. figscribe-0.3.0/src/figscribe/render/export.py +113 -0
  100. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/render/figure.py +91 -26
  101. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/tools/spec.py +3 -2
  102. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/units.py +22 -4
  103. {figscribe-0.2.0 → figscribe-0.3.0}/tests/codegen/test_equivalence.py +532 -1
  104. {figscribe-0.2.0 → figscribe-0.3.0}/tests/codegen/test_script.py +95 -0
  105. {figscribe-0.2.0 → figscribe-0.3.0}/tests/commands/test_everyday.py +16 -0
  106. {figscribe-0.2.0 → figscribe-0.3.0}/tests/commands/test_panels.py +19 -0
  107. figscribe-0.3.0/tests/commands/test_undo_stress.py +483 -0
  108. {figscribe-0.2.0 → figscribe-0.3.0}/tests/conftest.py +11 -0
  109. {figscribe-0.2.0 → figscribe-0.3.0}/tests/dataio/test_csv.py +126 -2
  110. figscribe-0.3.0/tests/dataio/test_write_atomically.py +171 -0
  111. figscribe-0.3.0/tests/fit/test_allan_fit.py +119 -0
  112. {figscribe-0.2.0 → figscribe-0.3.0}/tests/fit/test_expression.py +68 -0
  113. {figscribe-0.2.0 → figscribe-0.3.0}/tests/fit/test_run.py +70 -0
  114. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/everything.py +19 -2
  115. figscribe-0.3.0/tests/gui/test_about.py +112 -0
  116. figscribe-0.3.0/tests/gui/test_allan_fit.py +129 -0
  117. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_axis_form.py +96 -9
  118. figscribe-0.3.0/tests/gui/test_axis_search.py +153 -0
  119. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_document.py +62 -0
  120. figscribe-0.3.0/tests/gui/test_element_sections.py +177 -0
  121. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_every_action.py +20 -3
  122. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_every_field.py +3 -1
  123. figscribe-0.3.0/tests/gui/test_export_memory.py +222 -0
  124. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_find.py +18 -3
  125. figscribe-0.3.0/tests/gui/test_find_words.py +120 -0
  126. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_fit.py +124 -1
  127. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_images.py +5 -2
  128. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_left_out.py +20 -0
  129. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_look.py +17 -5
  130. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_markings.py +7 -2
  131. figscribe-0.3.0/tests/gui/test_navigation.py +270 -0
  132. figscribe-0.3.0/tests/gui/test_note_arrow.py +150 -0
  133. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_panels.py +3 -2
  134. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_plot_menu.py +2 -1
  135. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_properties.py +377 -23
  136. figscribe-0.3.0/tests/gui/test_recovery.py +208 -0
  137. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_start.py +106 -2
  138. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_ticks.py +7 -4
  139. figscribe-0.3.0/tests/gui/test_toolbar.py +269 -0
  140. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_units_panel.py +64 -1
  141. figscribe-0.3.0/tests/gui/test_unwell_files.py +113 -0
  142. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_without_effect.py +194 -10
  143. figscribe-0.3.0/tests/render/baseline/converted.png +0 -0
  144. figscribe-0.3.0/tests/render/baseline/error_band.png +0 -0
  145. figscribe-0.3.0/tests/render/baseline/errorbar.png +0 -0
  146. figscribe-0.3.0/tests/render/baseline/line.png +0 -0
  147. figscribe-0.3.0/tests/render/baseline/note_background.png +0 -0
  148. figscribe-0.3.0/tests/render/baseline/panel_labels.png +0 -0
  149. figscribe-0.3.0/tests/render/baseline/reduced.png +0 -0
  150. figscribe-0.3.0/tests/render/baseline/reference_lines.png +0 -0
  151. figscribe-0.3.0/tests/render/baseline/residuals.png +0 -0
  152. figscribe-0.3.0/tests/render/baseline/scatter.png +0 -0
  153. figscribe-0.3.0/tests/render/baseline/two_panels.png +0 -0
  154. figscribe-0.3.0/tests/render/test_align.py +289 -0
  155. figscribe-0.3.0/tests/render/test_export.py +183 -0
  156. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/test_fit_render.py +61 -0
  157. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/test_images.py +102 -0
  158. figscribe-0.3.0/tests/render/test_reduction.py +114 -0
  159. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/test_render.py +25 -0
  160. figscribe-0.3.0/tests/test_convert.py +161 -0
  161. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_edge_data.py +15 -0
  162. figscribe-0.3.0/tests/test_examples.py +173 -0
  163. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_model_roundtrip.py +114 -0
  164. figscribe-0.3.0/tests/test_packaging_notices.py +215 -0
  165. figscribe-0.3.0/tests/test_reduce.py +242 -0
  166. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_styles.py +3 -0
  167. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_units.py +106 -0
  168. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_validate_references.py +40 -0
  169. {figscribe-0.2.0 → figscribe-0.3.0}/tests/tools/test_tools.py +107 -0
  170. figscribe-0.2.0/.github/workflows/tests.yml +0 -48
  171. figscribe-0.2.0/CHANGELOG.md +0 -107
  172. figscribe-0.2.0/docs/testing.md +0 -78
  173. figscribe-0.2.0/src/figscribe/convert.py +0 -126
  174. figscribe-0.2.0/src/figscribe/examples.py +0 -17
  175. figscribe-0.2.0/src/figscribe/gui/palette.py +0 -124
  176. figscribe-0.2.0/src/figscribe/io/files.py +0 -32
  177. figscribe-0.2.0/src/figscribe/render/export.py +0 -55
  178. figscribe-0.2.0/tests/render/baseline/converted.png +0 -0
  179. figscribe-0.2.0/tests/render/baseline/error_band.png +0 -0
  180. figscribe-0.2.0/tests/render/baseline/errorbar.png +0 -0
  181. figscribe-0.2.0/tests/render/baseline/line.png +0 -0
  182. figscribe-0.2.0/tests/render/baseline/panel_labels.png +0 -0
  183. figscribe-0.2.0/tests/render/baseline/reference_lines.png +0 -0
  184. figscribe-0.2.0/tests/render/baseline/scatter.png +0 -0
  185. figscribe-0.2.0/tests/render/baseline/two_panels.png +0 -0
  186. figscribe-0.2.0/tests/render/test_export.py +0 -90
  187. figscribe-0.2.0/tests/test_convert.py +0 -83
  188. figscribe-0.2.0/tests/test_examples.py +0 -33
  189. {figscribe-0.2.0 → figscribe-0.3.0}/.gitattributes +0 -0
  190. {figscribe-0.2.0 → figscribe-0.3.0}/LICENSE +0 -0
  191. {figscribe-0.2.0 → figscribe-0.3.0}/examples/gaussian-scan/scan.csv +0 -0
  192. {figscribe-0.2.0 → figscribe-0.3.0}/examples/gaussian-scan/scan.figscribe +0 -0
  193. {figscribe-0.2.0 → figscribe-0.3.0}/packaging/icons/figscribe.icns +0 -0
  194. {figscribe-0.2.0 → figscribe-0.3.0}/packaging/icons/figscribe.ico +0 -0
  195. {figscribe-0.2.0 → figscribe-0.3.0}/packaging/icons/figscribe.png +0 -0
  196. {figscribe-0.2.0 → figscribe-0.3.0}/packaging/make_icons.py +0 -0
  197. {figscribe-0.2.0 → figscribe-0.3.0}/plan/project-brief.md +0 -0
  198. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/__main__.py +0 -0
  199. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/assistant/__init__.py +0 -0
  200. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/assistant/bench.py +0 -0
  201. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/assistant/endpoint.py +0 -0
  202. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/assistant/mcpserver.py +0 -0
  203. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/assistant/toolserver.py +0 -0
  204. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/codegen/__init__.py +0 -0
  205. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/commands/__init__.py +0 -0
  206. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/commands/core.py +0 -0
  207. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/__init__.py +0 -0
  208. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/assistantdialog.py +0 -0
  209. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/chatpanel.py +0 -0
  210. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/clipboard.py +0 -0
  211. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/codepanel.py +0 -0
  212. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/databar.py +0 -0
  213. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/errors.py +0 -0
  214. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/figures.py +0 -0
  215. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fonts/IBMPlexMono-Medium.ttf +0 -0
  216. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fonts/IBMPlexMono-Regular.ttf +0 -0
  217. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fonts/IBMPlexSans-Medium.ttf +0 -0
  218. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fonts/IBMPlexSans-Regular.ttf +0 -0
  219. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fonts/IBMPlexSans-SemiBold.ttf +0 -0
  220. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/fonts/OFL.txt +0 -0
  221. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/icons/check.svg +0 -0
  222. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/icons/chevron-down.svg +0 -0
  223. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/icons/figscribe-small.svg +0 -0
  224. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/icons/figscribe.svg +0 -0
  225. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/importdialog.py +0 -0
  226. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/inplace.py +0 -0
  227. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/markers.py +0 -0
  228. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/markings.py +0 -0
  229. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/numbers.py +0 -0
  230. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/problems.py +0 -0
  231. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/resize.py +0 -0
  232. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/gui/reveal.py +0 -0
  233. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/io/__init__.py +0 -0
  234. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/io/csvfile.py +0 -0
  235. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/io/errors.py +0 -0
  236. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/io/numpyfile.py +0 -0
  237. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/io/provider.py +0 -0
  238. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/io/refresh.py +0 -0
  239. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/base.py +0 -0
  240. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/data.py +0 -0
  241. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/model/project.py +0 -0
  242. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/render/__init__.py +0 -0
  243. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/render/labels.py +0 -0
  244. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/render/layout.py +0 -0
  245. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/render/ticks.py +0 -0
  246. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/suggest.py +0 -0
  247. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/templates.py +0 -0
  248. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/tools/__init__.py +0 -0
  249. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/tools/operations.py +0 -0
  250. {figscribe-0.2.0 → figscribe-0.3.0}/src/figscribe/tools/workspace.py +0 -0
  251. {figscribe-0.2.0 → figscribe-0.3.0}/tests/assistant/__init__.py +0 -0
  252. {figscribe-0.2.0 → figscribe-0.3.0}/tests/assistant/fakeserver.py +0 -0
  253. {figscribe-0.2.0 → figscribe-0.3.0}/tests/assistant/test_bench.py +0 -0
  254. {figscribe-0.2.0 → figscribe-0.3.0}/tests/assistant/test_chat.py +0 -0
  255. {figscribe-0.2.0 → figscribe-0.3.0}/tests/assistant/test_mcp.py +0 -0
  256. {figscribe-0.2.0 → figscribe-0.3.0}/tests/codegen/__init__.py +0 -0
  257. {figscribe-0.2.0 → figscribe-0.3.0}/tests/commands/__init__.py +0 -0
  258. {figscribe-0.2.0 → figscribe-0.3.0}/tests/commands/test_commands.py +0 -0
  259. {figscribe-0.2.0 → figscribe-0.3.0}/tests/commands/test_figures.py +0 -0
  260. {figscribe-0.2.0 → figscribe-0.3.0}/tests/commands/test_ops.py +0 -0
  261. {figscribe-0.2.0 → figscribe-0.3.0}/tests/dataio/__init__.py +0 -0
  262. {figscribe-0.2.0 → figscribe-0.3.0}/tests/dataio/test_numpy.py +0 -0
  263. {figscribe-0.2.0 → figscribe-0.3.0}/tests/dataio/test_refresh.py +0 -0
  264. {figscribe-0.2.0 → figscribe-0.3.0}/tests/fit/__init__.py +0 -0
  265. {figscribe-0.2.0 → figscribe-0.3.0}/tests/fit/test_refit.py +0 -0
  266. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/__init__.py +0 -0
  267. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_axis_zoom.py +0 -0
  268. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_code_panel.py +0 -0
  269. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_databar.py +0 -0
  270. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_errors.py +0 -0
  271. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_everyday.py +0 -0
  272. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_figures.py +0 -0
  273. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_import.py +0 -0
  274. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_inplace.py +0 -0
  275. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_interaction.py +0 -0
  276. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_mainwindow.py +0 -0
  277. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_notes.py +0 -0
  278. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_numbers.py +0 -0
  279. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_plot_dialog.py +0 -0
  280. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_preview.py +0 -0
  281. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_reading_options.py +0 -0
  282. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_resize.py +0 -0
  283. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_reveal.py +0 -0
  284. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_right_axis.py +0 -0
  285. {figscribe-0.2.0 → figscribe-0.3.0}/tests/gui/test_several.py +0 -0
  286. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/__init__.py +0 -0
  287. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/conftest.py +0 -0
  288. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/test_conversion.py +0 -0
  289. {figscribe-0.2.0 → figscribe-0.3.0}/tests/render/test_labels.py +0 -0
  290. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_allan.py +0 -0
  291. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_field_coverage.py +0 -0
  292. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_left_out.py +0 -0
  293. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_packaging_icons.py +0 -0
  294. {figscribe-0.2.0 → figscribe-0.3.0}/tests/test_suggest.py +0 -0
  295. {figscribe-0.2.0 → figscribe-0.3.0}/tests/tools/__init__.py +0 -0
@@ -0,0 +1,87 @@
1
+ name: Installers
2
+
3
+ # An installer for each system (decision 0066): built from the wheel, tried
4
+ # (the frozen program writes a script that runs, and its window starts), and
5
+ # attached to the GitHub release. A pull request that touches the packaging
6
+ # builds them too, so a change cannot break them unseen; the files are kept
7
+ # with the run for a week.
8
+
9
+ on:
10
+ pull_request:
11
+ paths:
12
+ - "packaging/**"
13
+ - ".github/workflows/installers.yml"
14
+ - "pyproject.toml"
15
+ release:
16
+ types: [published]
17
+ workflow_dispatch:
18
+
19
+ permissions:
20
+ contents: read
21
+
22
+ jobs:
23
+ build:
24
+ name: installer (${{ matrix.name }})
25
+ runs-on: ${{ matrix.os }}
26
+ env:
27
+ QT_QPA_PLATFORM: offscreen
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ include:
32
+ - name: macOS
33
+ os: macos-latest
34
+ - name: Windows
35
+ os: windows-latest
36
+ # The oldest system that is offered: a program built on a newer glibc
37
+ # does not start on an older one, not the other way round.
38
+ - name: Linux
39
+ os: ubuntu-22.04
40
+ steps:
41
+ - uses: actions/checkout@v5
42
+ - uses: actions/setup-python@v6
43
+ with:
44
+ python-version: "3.13"
45
+ - name: Install the build tools
46
+ run: python -m pip install -r packaging/requirements-build.txt
47
+ - name: Build the wheel
48
+ run: python -m build --wheel --outdir wheel
49
+ - name: Install figscribe from the wheel
50
+ shell: bash
51
+ run: python -m pip install "$(ls wheel/*.whl)[chat,mcp]"
52
+ - name: Linux only, the libraries Qt loads when it is imported
53
+ if: runner.os == 'Linux'
54
+ run: sudo apt-get update && sudo apt-get install -y libegl1 libxkbcommon0 libfontconfig1 libdbus-1-3
55
+ - name: Windows only, the installer compiler
56
+ if: runner.os == 'Windows'
57
+ run: choco install innosetup --no-progress -y
58
+ - name: Build the installer
59
+ run: python packaging/make_installer.py
60
+ - name: Try the frozen program
61
+ run: python packaging/smoke.py
62
+ - uses: actions/upload-artifact@v5
63
+ with:
64
+ name: installer-${{ matrix.name }}
65
+ path: |
66
+ packaging/out/*.dmg
67
+ packaging/out/*.exe
68
+ packaging/out/*.AppImage
69
+ retention-days: 7
70
+ if-no-files-found: error
71
+
72
+ attach:
73
+ name: attach to the release
74
+ if: github.event_name == 'release'
75
+ needs: build
76
+ runs-on: ubuntu-latest
77
+ permissions:
78
+ contents: write # upload to the release; the PyPI workflow has no such right
79
+ steps:
80
+ - uses: actions/download-artifact@v5
81
+ with:
82
+ path: installers
83
+ merge-multiple: true
84
+ - name: Upload
85
+ env:
86
+ GH_TOKEN: ${{ github.token }}
87
+ run: gh release upload "${{ github.event.release.tag_name }}" installers/* --repo "${{ github.repository }}"
@@ -13,7 +13,7 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  environment: pypi
15
15
  permissions:
16
- contents: read # actions/checkout; required because this repo is private
16
+ contents: read # actions/checkout
17
17
  id-token: write # OIDC token for Trusted Publishing
18
18
  steps:
19
19
  - uses: actions/checkout@v5
@@ -0,0 +1,118 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ # A newer push to a pull request makes the older run pointless. Pushes to
12
+ # develop and main are never cancelled.
13
+ concurrency:
14
+ group: tests-${{ github.ref }}
15
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
16
+
17
+ jobs:
18
+ ruff:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v5
22
+ - uses: actions/setup-python@v6
23
+ with:
24
+ python-version: "3.13"
25
+ cache: pip
26
+ cache-dependency-path: pyproject.toml
27
+ - name: Install
28
+ run: python -m pip install -e ".[dev]"
29
+ - name: Lint
30
+ run: ruff check --output-format=github .
31
+ - name: Format
32
+ run: ruff format --check .
33
+
34
+ pytest:
35
+ runs-on: ${{ matrix.os }}
36
+ env:
37
+ QT_QPA_PLATFORM: offscreen
38
+ strategy:
39
+ fail-fast: false
40
+ matrix:
41
+ os: [ubuntu-latest, windows-latest, macos-latest]
42
+ python-version: ["3.11", "3.13"]
43
+ steps:
44
+ - uses: actions/checkout@v5
45
+ with:
46
+ fetch-depth: 0
47
+ # The branch protection requires these jobs, so a pull request that only
48
+ # touches the plan, the docs or a Markdown file runs them but not the
49
+ # tests; skipping the whole workflow would leave the checks pending.
50
+ - name: Look at what changed
51
+ id: changes
52
+ shell: bash
53
+ run: |
54
+ other=""
55
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
56
+ other="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" HEAD \
57
+ | grep -vE '^(plan/|docs/|.*\.md$)' || true)"
58
+ if [ -z "$other" ]; then echo "docs_only=true" >> "$GITHUB_OUTPUT"; fi
59
+ fi
60
+ - uses: actions/setup-python@v6
61
+ if: steps.changes.outputs.docs_only != 'true'
62
+ with:
63
+ python-version: ${{ matrix.python-version }}
64
+ cache: pip
65
+ cache-dependency-path: pyproject.toml
66
+ - name: Install Qt's system libraries
67
+ if: runner.os == 'Linux' && steps.changes.outputs.docs_only != 'true'
68
+ run: sudo apt-get update && sudo apt-get install -y libegl1 libxkbcommon0 libfontconfig1 libdbus-1-3
69
+ - name: Install
70
+ if: steps.changes.outputs.docs_only != 'true'
71
+ run: python -m pip install -e ".[dev]"
72
+ # The slow tests operate every field and action of the GUI; once is
73
+ # enough, on one platform (decision 0028).
74
+ - name: Run tests
75
+ if: steps.changes.outputs.docs_only != 'true'
76
+ run: python -m pytest -q -n auto --dist worksteal ${{ (matrix.os != 'ubuntu-latest' || matrix.python-version != '3.13') && '-m "not slow"' || '' }}
77
+
78
+ # The oldest versions pyproject.toml allows (Matplotlib, NumPy, SciPy,
79
+ # pydantic, PySide6), since a floor nobody installs is only a guess. Left
80
+ # out: the images of tests/render/test_images.py, made with the newest
81
+ # Matplotlib, and the slow GUI sweeps (decision 0044). Not required by the
82
+ # branch protection.
83
+ minimum-versions:
84
+ runs-on: ubuntu-latest
85
+ env:
86
+ QT_QPA_PLATFORM: offscreen
87
+ steps:
88
+ - uses: actions/checkout@v5
89
+ with:
90
+ fetch-depth: 0
91
+ - name: Look at what changed
92
+ id: changes
93
+ run: |
94
+ other=""
95
+ if [ "${{ github.event_name }}" = "pull_request" ]; then
96
+ other="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" HEAD \
97
+ | grep -vE '^(plan/|docs/|.*\.md$)' || true)"
98
+ if [ -z "$other" ]; then echo "docs_only=true" >> "$GITHUB_OUTPUT"; fi
99
+ fi
100
+ - uses: actions/setup-python@v6
101
+ if: steps.changes.outputs.docs_only != 'true'
102
+ with:
103
+ python-version: "3.11"
104
+ cache: pip
105
+ cache-dependency-path: pyproject.toml
106
+ - name: Install Qt's system libraries
107
+ if: steps.changes.outputs.docs_only != 'true'
108
+ run: sudo apt-get update && sudo apt-get install -y libegl1 libxkbcommon0 libfontconfig1 libdbus-1-3
109
+ - name: Install the oldest allowed versions
110
+ if: steps.changes.outputs.docs_only != 'true'
111
+ run: |
112
+ python -m pip install uv
113
+ uv venv .venv-min
114
+ uv pip install --python .venv-min/bin/python --resolution lowest-direct -e .
115
+ uv pip install --python .venv-min/bin/python pytest pytest-qt pytest-xdist hypothesis
116
+ - name: Run tests
117
+ if: steps.changes.outputs.docs_only != 'true'
118
+ run: .venv-min/bin/python -m pytest -q -n auto --dist worksteal -m "not slow" --ignore=tests/render/test_images.py
@@ -15,6 +15,7 @@ venv/
15
15
 
16
16
  # Tests and tooling
17
17
  .pytest_cache/
18
+ .hypothesis/
18
19
  .mypy_cache/
19
20
  .ruff_cache/
20
21
  .coverage
@@ -27,3 +28,7 @@ tests/**/output/
27
28
  # Editors
28
29
  .idea/
29
30
  .vscode/
31
+
32
+ # Backups of saved projects (figscribe keeps the version before the last save)
33
+ *.figscribe.bak
34
+ packaging/out/
@@ -0,0 +1,295 @@
1
+ # Changelog
2
+
3
+ What changed in each release of figscribe. Project files written by a
4
+ release open in every later one.
5
+
6
+ ## 0.3.0 — 2026-11-05
7
+
8
+ - **Installers for macOS, Windows and Linux.** Each release carries a disk image
9
+ for Apple silicon Macs, an installer for Windows and an AppImage for Linux, so
10
+ figscribe runs without Python or `pip`. They are not signed: macOS asks once to
11
+ confirm opening the app (right-click, *Open*), and Windows SmartScreen shows
12
+ "More info", then "Run anyway". The AppImage needs `libxcb-cursor0`. Intel Macs,
13
+ ARM Linux and `.deb`/Flatpak are not built; `pip install` works everywhere
14
+ (decision 0066).
15
+ - **The installers carry the licenses of what is in them.** A
16
+ `THIRD-PARTY-NOTICES.txt` lists the packages in each installer, with their
17
+ license texts, the LGPL for Qt with where its source is, and Python's license;
18
+ `LICENSE.txt` is figscribe's own. They are in the program's `licenses` folder,
19
+ beside the app in the macOS disk image, in the install folder on Windows (whose
20
+ installer shows the license first) and in the AppImage. An installer is not
21
+ built when a package it contains has no license file.
22
+ - *Help > About figscribe* says which licenses figscribe is under: MIT, Qt (through
23
+ PySide6) under the LGPL-3.0, and where the licenses of the rest are.
24
+
25
+ - **Fixed: a script written into a folder behind a link could not find its
26
+ data.** The script works out its folder with every link followed, but the
27
+ path to the data was worked out from the folder as it was named, so on macOS
28
+ (where `/tmp` and the temporary folders are links) it was off by a level.
29
+
30
+ - **Export as TIFF and EPS, and with a transparent background.** *Export
31
+ Figure…* and *Export All Figures…* offer TIFF (LZW compressed, at the figure's
32
+ dpi) and EPS; *File > Export with a Transparent Background* leaves out the white
33
+ background of PNG, TIFF and SVG.
34
+ - **An Allan deviation can be fitted.** *Fit…* is offered on it, with
35
+ the error bars as weights, a fit range in τ, a smooth curve along the log axis
36
+ and residuals; the generated script fits the same points. A new *Power law*
37
+ model (a·x^b) fits the slope of white noise, −1/2.
38
+ - **Unsaved work survives a crash.** Every minute figscribe writes the changes
39
+ not yet saved to `<project>.figscribe.autosave` next to the project (for an
40
+ untitled project, to its data folder); the next time the project is opened it
41
+ offers to recover them. A save or a close removes the file.
42
+ - **Histogram, Allan deviation, image and color mesh have their settings
43
+ arranged: *Label and data* says what is plotted, *Look* how; the
44
+ color bar is one box with its settings under it (it was a box and a section),
45
+ and an image's extent, first row and pixel shape are in the folded *Pixel grid*.
46
+ - **A fit and its residuals have their settings arranged: the fit's
47
+ result card sits under *Label and data*, *Look* has how the curve is drawn,
48
+ and the residuals' zero line is a section of its own, a box with its settings
49
+ under it.
50
+ - **A note, a reference line and a shaded range have their settings in sections
51
+ by what they are for: a note has *Text* and
52
+ *Place* (with *Position in*, which its position is read in), lines and ranges
53
+ *Label and data* with *Legend entry* first, then *Look*.
54
+ - **Fixed: the script of a fit whose range keeps no row.** After narrowing the
55
+ range of a fitted plot (or of the fit) to one without a point, the exported
56
+ script stopped with "`ydata` must not be empty". It now draws no fit, as the
57
+ figure does, and says why in a comment; the problem list says so too, at the fit.
58
+ - **The settings of an axis are found with Ctrl+K.** *log*, *symlog*, *limits*,
59
+ *range*, *ticks*, *prefix* and more find the axis's scale, range, ticks and
60
+ label, also when only the panel or a plot is selected: choosing one opens that
61
+ axis on the setting. A setting that waits for another, such as *Linear within ±*
62
+ on a linear axis, is no longer offered.
63
+ - **A note's arrow is made in the figure.** Right-click a note and choose *Add an
64
+ arrow*; select the note and right-click where the arrow should point:
65
+ *Point the note's arrow here*. *Remove the note's arrow* takes it away. The
66
+ numbers in the property panel still work, and their tip says how to do it by
67
+ clicking.
68
+ - **Finding by what a thing is known as.** *Find Setting or Action* (Ctrl+K)
69
+ now also knows other words: *band*, *region*, *highlight* or *threshold* find
70
+ the shaded range and the reference lines, *arrow* and *callout* the note,
71
+ *heatmap* the color mesh, *residuals* the fit, *downsample* the setting
72
+ *Fewer points*, and so on. An entry found by its name comes before one found by
73
+ another word. The empty property panel says where to look.
74
+ - **The toolbar's adding buttons show what they make.** *Data*, *Figure*,
75
+ *Panel*, *Plot*, *Fit*, *Note* and a new *Reference* each have a picture of
76
+ their own instead of the same plus, in two groups (what holds, and what goes
77
+ into a panel). *Plot*, like *Panel* and *Reference*, opens a menu with *Add Plot…*
78
+ and the kinds (*Histogram*, *Allan deviation*, *Color mesh* …), which opens
79
+ the dialog on the one chosen; the entries of these menus have pictures;
80
+ *Reference* adds a vertical or horizontal line or a shaded range. *Insert >
81
+ Add Line or Range* is now *Insert > Add Reference*, and *Insert > Add Plot
82
+ As* lists the kinds. In a narrow window the toolbar shows the icons alone
83
+ instead of hiding buttons behind the » menu.
84
+ - **A gallery of twelve examples on the start page**, with a picture each: a
85
+ Gaussian scan, a noisy sensor (the means of a minute over the raw line), an
86
+ RC discharge, a freezing plateau (°C by a formula, a second axis), a
87
+ resonance, a diode's current on a symmetric log axis, a growth curve, a
88
+ calibration, a distribution, the stability of a clock, a beam profile (a color
89
+ map) and a paper figure. A click asks for a folder and opens a copy of the
90
+ example there. The data are generated, not measured. On the start page, the recent
91
+ projects now come before the layouts and the examples, *New to figscribe?*
92
+ has *Try an example* (scrolls to the gallery) and *How it works* (the README's
93
+ *Getting started*) next to *Describe it instead*, and the date of a recent
94
+ project is written in English whatever the system language.
95
+ - **New figures have inward tick marks on all four sides**, as many journals
96
+ want, and the figure's settings are arranged by what they are for: *Format*
97
+ (name, size, PNG resolution, style sheet), *Text* (font size, units in
98
+ labels, panel letters), *Ticks and frame* with the two tick settings in view,
99
+ their widths and lengths and the panel layout (folded). Saved projects keep
100
+ their own ticks. A thin line now separates the sections of the property
101
+ panel, as the design had always meant, and every section folds with a click
102
+ on its title; the panel remembers it for each kind of object while you work. The
103
+ section titles are larger (14 px, as large as the figure's name in the outline;
104
+ the object's title 17 px), to be read at a glance.
105
+ - **A panel's settings are arranged by what they are for:** *Title and axes*
106
+ (the title, then the cards of the axes, the right y axis with its card right
107
+ after its box), *Grid* (the box with all its settings under it, no longer
108
+ hidden under *More*), *Legend*, and the folded *Place in layout*. The legend's
109
+ *Columns* is *Columns of entries*, and its *Position* is *Position (dragged)*.
110
+ - **The axis form has three sections:** *Label* (with the label's size and
111
+ distance), *Scale* (type, range, the linear zone of a symmetric log axis and
112
+ the size of the numbers) and *Ticks* (major and minor, the row under the major
113
+ ticks says *Distance* or *Values*). A section folded on an axis stays folded.
114
+ - **The settings of a line, points or error bar plot are arranged by what they
115
+ are for:** *Label and data* (the legend entry is *Legend entry*), *Units and
116
+ rows* (with *Fewer points* right under its box), *Look* (marker size and line
117
+ width next to their marker and line), *Error bars* (the band under its box) and
118
+ the folded *Left-out points* and *More*.
119
+ - **A formula for a plot's units.** *x in* and *y in* offer "a formula" next to
120
+ "another factor": `x / 60 - 273.15`, `(x - 32) / 1.8`. It is written in `x`
121
+ and numbers and must be linear and rise with x, so that error bars, axis,
122
+ marks, ranges and fits follow as they do for a factor; the start (*Start at
123
+ 0*) is taken off first. The script shows the formula in Python's spacing. For
124
+ the τ of an Allan deviation it may scale but not shift, e.g. cycles of 2.88 s
125
+ in hours as `x * 2.88 / 3600`. Saved files are drawn as before.
126
+ - **Fewer points drawn.** A line, points or error bar plot can show the mean
127
+ or median of every *n* rows, every *n*-th row, or the mean over intervals
128
+ of x (*Fewer points*, under *Data*). The data file stays as it is; the
129
+ figure, the export and the script show the reduced points and fits use
130
+ them. An averaged point has its standard error as error bar (propagated
131
+ when the plot has y errors); the median has none. Rows left out count as
132
+ before and can be replaced by the mean of their neighbours. While a plot is
133
+ reduced, leaving rows out and finding outliers are off. Saved files are
134
+ drawn as before.
135
+ - Fixed: *Save As* into another folder no longer loses the data (every file was
136
+ "missing" there). A data file outside the project's folder, or imported before
137
+ the first save, is now stored relative to the project (`../data/scan.csv`),
138
+ where it used to be stored by its whole path, so a project and its data can be
139
+ moved or shared together; a file on another volume or drive keeps its whole path.
140
+ - Fixed: the script of a plot with fewer points drawn stopped with an error
141
+ when its range of x kept no row; it now draws no point, as figscribe does.
142
+ - **Saving cannot destroy the saved project.** The project is written to a
143
+ temporary file and put in place when that is complete, so a full disk or a
144
+ lost share leaves the last saved version; that version also stays next to it
145
+ as `NAME.figscribe.bak`. A project made read-only is still refused. Project
146
+ files are written with the same line ends on every platform.
147
+ - **Export remembers its format and folder.** *Export Figure…* and *Export All
148
+ Figures…* open in the folder last used with the format last chosen, and
149
+ suggest the figure's name as the file name; a name typed without a suffix gets
150
+ the chosen format's.
151
+ - Fixed: `figscribe script` showed a Python traceback when it could not write
152
+ the script (a folder that is not there, a read-only one); it now says so
153
+ in one line.
154
+ - Fixed: *Add Panel* and *Duplicate* on a panel of a grid whose column
155
+ widths or row heights do not match its columns or rows (after the number
156
+ of columns was changed while widths were set, say) stopped with an error;
157
+ they now say what to put right.
158
+ - Fixed: the fit dialog no longer fails on data near the limits of floating
159
+ point (about 1e-300 or 1e300), says in words when a fit stops because the
160
+ model is not finite for values it tried, and says which uncertainties
161
+ cannot be estimated when the data does not determine a parameter.
162
+ - Fixed: the residuals, and the count of points fitted, no longer include
163
+ points whose y error is missing (NaN); the fit never used them.
164
+ - Fixed: with PySide6 before 6.12, running a command from a submenu in the
165
+ command palette (Ctrl+K), such as *Add Panel*, did nothing and printed an
166
+ error.
167
+ - **Requirements:** Matplotlib 3.10, NumPy 2.0, SciPy 1.13 and PySide6 6.7 at
168
+ least. Older Matplotlib (3.8) did not run at all and older NumPy read some
169
+ text files wrongly; the minimum versions are now installed and tested on
170
+ every change.
171
+ - **Axis labels are lined up across panels**, by default and also in files
172
+ saved before: in a column of panels the y labels stand on one line instead
173
+ of following the width of their numbers (and x labels in a row).
174
+ Saved figures with several panels therefore look a little different once
175
+ opened. *Line up axis labels* in the figure's settings turns it off. Each
176
+ axis has a *Distance (pt)* for its label.
177
+ - Residuals have a *Line width* (the joining line and the error bars) and
178
+ the zero line is set apart: switch it off, or change its color, width and
179
+ style. Saved files are drawn as before.
180
+ - A note can have a *Background*, white or any color, so it reads over a grid.
181
+ Notes without one are drawn as before.
182
+ - Where am I: above the settings a path, such as *Scan › Panel A › X axis*,
183
+ whose parts lead back to the figure or the panel with a click. The axes
184
+ are rows in the outline, under their panel, and the axis chosen in the
185
+ figure is the row selected there; a right-click offers the same range for
186
+ all panels.
187
+ - *Help > About figscribe* shows the version of figscribe and of Python,
188
+ Matplotlib and Qt, for bug reports. On macOS it stays under *Help*.
189
+ - **The Insert menu has pictures all the way down.** *Add Plot As*, *Add
190
+ Reference*, *Add Panel* and *Duplicate Figure* have one now, and the menu lists
191
+ its entries in the toolbar's order. In the toolbar *Reference* comes before
192
+ *Note*, and the note's picture is a T.
193
+
194
+ ## 0.2.0 — 2026-10-29
195
+
196
+ **Figures**
197
+ - Several figures in one project: tabs above the figure, *Insert > Add
198
+ Figure* (with the look of the shown one), duplicate, rename, reorder and
199
+ delete from a tab or the outline; *File > Export All Figures…*;
200
+ `figscribe script --figure NAME` and `--all`.
201
+ - Error bands: an error bar plot draws its y errors as a shaded band around
202
+ the line, for dense data.
203
+ - The Allan deviation: σ_y(τ) of an evenly sampled series, frequency or
204
+ phase, on log-log axes, for the stability of clocks, lasers and sensors.
205
+ The script computes it with a few lines of NumPy, no extra package.
206
+ - The plot dialog shows the kinds as tiles, the columns as a sentence, a
207
+ preview with the data, and which panel the plot goes to.
208
+ - Settings that do nothing in the current state are greyed out, and their
209
+ tooltip says why.
210
+ - Units and a range per plot: show values in min, h, days, ms or by any
211
+ factor, counted from the start if wanted, and draw, count and fit only the
212
+ rows within a range of x. The axis, its marks and the plot's fits follow;
213
+ the script converts with one line of NumPy per column.
214
+
215
+ **Finding things**
216
+ - A search above the property panel (Ctrl+F) keeps the settings whose name
217
+ or explanation has the typed words.
218
+ - A command palette (Ctrl+K) finds any menu action or setting by typing.
219
+ - The property panel shows the settings used most first; the rest are
220
+ under *More*.
221
+ - Several plots chosen with Ctrl or Shift in the outline: change their
222
+ color, marker or line for all at once, or delete them together.
223
+
224
+ **Editing where it is drawn**
225
+ - Double-click an axis label, title, legend entry or note to edit it in
226
+ place.
227
+ - Drag an inset's corner to resize it, or the gap between two panels.
228
+ - Right-click a plot: *Style like…*, *Bring to front*, *Send to back*,
229
+ *Fit…*, *Hide*.
230
+
231
+ **Everyday actions**
232
+ - *Edit > Duplicate* (Ctrl+D) copies the selected plots, panel or figure.
233
+ A panel comes with its plots, fits and insets, beside the original.
234
+ - *Copy Style* and *Paste Style* (Ctrl+Alt+C, Ctrl+Alt+V) give one or
235
+ several plots the look of another, as one undo step.
236
+ - *Same x range in all panels* and *Same y range in all panels*, from a
237
+ panel's menu in the outline or the preview.
238
+ - *Edit > Copy Figure* (Ctrl+Shift+C) puts the figure on the clipboard as
239
+ PNG and SVG, for slides and documents. When figscribe closes, only the
240
+ bitmap stays on the clipboard.
241
+ - *Show in Finder* (Explorer, file manager) on a data file in the outline.
242
+
243
+ **Fixed**
244
+ - With openai 3.38 or later, the assistant again says why Gemini refused a
245
+ request and how long to wait at a rate limit.
246
+ - A color the panel refused no longer breaks the panel the next time it is
247
+ built.
248
+ - The script names the encoding of a UTF-8 data file with letters beyond
249
+ ASCII, so that it also reads the file on Windows.
250
+
251
+ ## 0.1.0 — 2026-10-26
252
+
253
+ The first release to use: 0.0.1 and 0.0.2 only held the name on PyPI.
254
+
255
+ **Figures**
256
+ - Line, scatter and error bar plots, histograms, images and color meshes
257
+ with color bars, notes of one or several lines with or without an arrow,
258
+ reference lines and shaded ranges.
259
+ - Exact sizes in mm and pt; several panels in a grid, insets, shared axes,
260
+ a second y axis on the right; panel letters for journals.
261
+ - Axes in plain terms: range, linear, log or symlog, major ticks every *n*
262
+ with minor ticks between them, values shown with an SI prefix (Hz drawn
263
+ as kHz), tick marks inward or outward and on all sides.
264
+ - Line widths of the frame, the ticks and the grid, and the grid's color and
265
+ line style; an error bar plot's line has a width apart from its bars.
266
+ - Export as PDF, SVG and PNG.
267
+
268
+ **Data**
269
+ - CSV with automatic detection of delimiter, decimal comma, header and unit
270
+ rows and text encoding; NumPy `.npy` and `.npz`.
271
+ - Each file is recorded with its SHA-256: a changed or moved file is
272
+ reported, and its new version or new place taken on request.
273
+ - A data file can be removed with the plots that draw from it.
274
+
275
+ **Fits**
276
+ - Linear, polynomial, Gaussian, Lorentzian, exponential, or an expression of
277
+ your own, weighted by the error bars, with residuals in a panel below.
278
+ - Fit only a range of x; hold parameters fixed or within bounds; change a
279
+ fit that is already there; copy the result.
280
+ - Leave points out by right-clicking them, or have outliers found and left
281
+ out on confirmation. The data file stays as it is.
282
+
283
+ **The generated script**
284
+ - Every figure as a standalone Python script that needs only NumPy,
285
+ Matplotlib and, for fits, SciPy, and reads as if a person wrote it. A test
286
+ compares its output with figscribe's own pixel by pixel.
287
+
288
+ **Working with it**
289
+ - Select in the figure or the contents list, edit in the property panel,
290
+ undo every step. Zoom and pan with the mouse, one axis at a time over its
291
+ frame; *Use as axis range* keeps the view.
292
+ - An assistant that makes changes described in words: Claude, ChatGPT,
293
+ Google Gemini or a local model, built in (`figscribe[chat]`) or through
294
+ MCP (`figscribe[mcp]`). It sees names, never the data values.
295
+ `figscribe bench` measures how well a model does.
@@ -38,8 +38,10 @@ FIGSCRIBE_UPDATE_BASELINES=1 python -m pytest tests/render/test_images.py
38
38
 
39
39
  - Everything in the repository is English, commit messages included.
40
40
  - Test first; for a bug, a failing test that reproduces it comes first.
41
- - One task per branch, branched from `develop`; pull request into `develop`;
42
- CI must be green on Linux, Windows and macOS. `main` only gets releases.
41
+ - One theme per branch, branched from `develop`: a large task alone, or a
42
+ few small related ones (at most four or five), one commit per task. Pull
43
+ request into `develop`; CI must be green on Linux, Windows and macOS.
44
+ `main` only gets releases. Run the whole suite before pushing.
43
45
  - Commits use the repository's configured identity (f4ren); no
44
46
  Co-Authored-By trailer.
45
47
  - No new dependency without a rationale in `docs/decisions.md`.