pycforge-sdk 0.6.3__py3-none-any.whl

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 (559) hide show
  1. pycforge_sdk/__init__.py +6 -0
  2. pycforge_sdk/__main__.py +3 -0
  3. pycforge_sdk/catalogue.py +287 -0
  4. pycforge_sdk/cli.py +114 -0
  5. pycforge_sdk/connection.py +136 -0
  6. pycforge_sdk/discovery.py +227 -0
  7. pycforge_sdk/editing.py +84 -0
  8. pycforge_sdk/engine_launch.py +45 -0
  9. pycforge_sdk/engine_worker.py +53 -0
  10. pycforge_sdk/gui/__init__.py +1 -0
  11. pycforge_sdk/gui/__main__.py +3 -0
  12. pycforge_sdk/gui/app.py +36 -0
  13. pycforge_sdk/gui/dialogs.py +310 -0
  14. pycforge_sdk/gui/editor.py +412 -0
  15. pycforge_sdk/gui/help/index.html +9 -0
  16. pycforge_sdk/gui/identity.py +139 -0
  17. pycforge_sdk/gui/jobs.py +146 -0
  18. pycforge_sdk/gui/reference.py +39 -0
  19. pycforge_sdk/gui/resources/sdk-mark.png +0 -0
  20. pycforge_sdk/gui/scanner.py +134 -0
  21. pycforge_sdk/gui/startup.py +118 -0
  22. pycforge_sdk/gui/visual/__init__.py +1 -0
  23. pycforge_sdk/gui/visual/action_contract.py +550 -0
  24. pycforge_sdk/gui/visual/icons.py +117 -0
  25. pycforge_sdk/gui/visual/qt_actions.py +501 -0
  26. pycforge_sdk/gui/visual/qt_branding.py +47 -0
  27. pycforge_sdk/gui/visual/qt_dialog_chrome.py +337 -0
  28. pycforge_sdk/gui/visual/qt_file_icons.py +130 -0
  29. pycforge_sdk/gui/visual/qt_icons.py +80 -0
  30. pycforge_sdk/gui/visual/qt_screen_geometry.py +166 -0
  31. pycforge_sdk/gui/visual/qt_title_controls.py +333 -0
  32. pycforge_sdk/gui/visual/qt_window_chrome.py +314 -0
  33. pycforge_sdk/gui/visual/qt_window_resize.py +93 -0
  34. pycforge_sdk/gui/visual/qt_workspace_widgets.py +448 -0
  35. pycforge_sdk/gui/visual/resources/icons/128/add-document.png +0 -0
  36. pycforge_sdk/gui/visual/resources/icons/128/brand-mark.png +0 -0
  37. pycforge_sdk/gui/visual/resources/icons/128/bundle-search.png +0 -0
  38. pycforge_sdk/gui/visual/resources/icons/128/cancel.png +0 -0
  39. pycforge_sdk/gui/visual/resources/icons/128/check.png +0 -0
  40. pycforge_sdk/gui/visual/resources/icons/128/chevron-right.png +0 -0
  41. pycforge_sdk/gui/visual/resources/icons/128/close.png +0 -0
  42. pycforge_sdk/gui/visual/resources/icons/128/collapse-all.png +0 -0
  43. pycforge_sdk/gui/visual/resources/icons/128/command-palette.png +0 -0
  44. pycforge_sdk/gui/visual/resources/icons/128/convert.png +0 -0
  45. pycforge_sdk/gui/visual/resources/icons/128/copy.png +0 -0
  46. pycforge_sdk/gui/visual/resources/icons/128/cut.png +0 -0
  47. pycforge_sdk/gui/visual/resources/icons/128/decision-trace.png +0 -0
  48. pycforge_sdk/gui/visual/resources/icons/128/details.png +0 -0
  49. pycforge_sdk/gui/visual/resources/icons/128/diagnostics.png +0 -0
  50. pycforge_sdk/gui/visual/resources/icons/128/duplicate-line.png +0 -0
  51. pycforge_sdk/gui/visual/resources/icons/128/exit.png +0 -0
  52. pycforge_sdk/gui/visual/resources/icons/128/expand-all.png +0 -0
  53. pycforge_sdk/gui/visual/resources/icons/128/export.png +0 -0
  54. pycforge_sdk/gui/visual/resources/icons/128/find.png +0 -0
  55. pycforge_sdk/gui/visual/resources/icons/128/go-to-line.png +0 -0
  56. pycforge_sdk/gui/visual/resources/icons/128/go-to-output.png +0 -0
  57. pycforge_sdk/gui/visual/resources/icons/128/go-to-source.png +0 -0
  58. pycforge_sdk/gui/visual/resources/icons/128/help.png +0 -0
  59. pycforge_sdk/gui/visual/resources/icons/128/history.png +0 -0
  60. pycforge_sdk/gui/visual/resources/icons/128/indent.png +0 -0
  61. pycforge_sdk/gui/visual/resources/icons/128/link-c.png +0 -0
  62. pycforge_sdk/gui/visual/resources/icons/128/mappings.png +0 -0
  63. pycforge_sdk/gui/visual/resources/icons/128/module.png +0 -0
  64. pycforge_sdk/gui/visual/resources/icons/128/move-down.png +0 -0
  65. pycforge_sdk/gui/visual/resources/icons/128/move-line-down.png +0 -0
  66. pycforge_sdk/gui/visual/resources/icons/128/move-line-up.png +0 -0
  67. pycforge_sdk/gui/visual/resources/icons/128/move-up.png +0 -0
  68. pycforge_sdk/gui/visual/resources/icons/128/next.png +0 -0
  69. pycforge_sdk/gui/visual/resources/icons/128/open.png +0 -0
  70. pycforge_sdk/gui/visual/resources/icons/128/outdent.png +0 -0
  71. pycforge_sdk/gui/visual/resources/icons/128/outline.png +0 -0
  72. pycforge_sdk/gui/visual/resources/icons/128/paste.png +0 -0
  73. pycforge_sdk/gui/visual/resources/icons/128/previous.png +0 -0
  74. pycforge_sdk/gui/visual/resources/icons/128/primary-module.png +0 -0
  75. pycforge_sdk/gui/visual/resources/icons/128/redo.png +0 -0
  76. pycforge_sdk/gui/visual/resources/icons/128/remove-document.png +0 -0
  77. pycforge_sdk/gui/visual/resources/icons/128/replace.png +0 -0
  78. pycforge_sdk/gui/visual/resources/icons/128/save-as.png +0 -0
  79. pycforge_sdk/gui/visual/resources/icons/128/save.png +0 -0
  80. pycforge_sdk/gui/visual/resources/icons/128/select-all.png +0 -0
  81. pycforge_sdk/gui/visual/resources/icons/128/settings.png +0 -0
  82. pycforge_sdk/gui/visual/resources/icons/128/show-c.png +0 -0
  83. pycforge_sdk/gui/visual/resources/icons/128/split-view.png +0 -0
  84. pycforge_sdk/gui/visual/resources/icons/128/summary.png +0 -0
  85. pycforge_sdk/gui/visual/resources/icons/128/telemetry.png +0 -0
  86. pycforge_sdk/gui/visual/resources/icons/128/toggle-comment.png +0 -0
  87. pycforge_sdk/gui/visual/resources/icons/128/toggle-fold.png +0 -0
  88. pycforge_sdk/gui/visual/resources/icons/128/tools.png +0 -0
  89. pycforge_sdk/gui/visual/resources/icons/128/undo.png +0 -0
  90. pycforge_sdk/gui/visual/resources/icons/128/whitespace.png +0 -0
  91. pycforge_sdk/gui/visual/resources/icons/16/add-document.png +0 -0
  92. pycforge_sdk/gui/visual/resources/icons/16/brand-mark.png +0 -0
  93. pycforge_sdk/gui/visual/resources/icons/16/bundle-search.png +0 -0
  94. pycforge_sdk/gui/visual/resources/icons/16/cancel.png +0 -0
  95. pycforge_sdk/gui/visual/resources/icons/16/check.png +0 -0
  96. pycforge_sdk/gui/visual/resources/icons/16/chevron-right.png +0 -0
  97. pycforge_sdk/gui/visual/resources/icons/16/close.png +0 -0
  98. pycforge_sdk/gui/visual/resources/icons/16/collapse-all.png +0 -0
  99. pycforge_sdk/gui/visual/resources/icons/16/command-palette.png +0 -0
  100. pycforge_sdk/gui/visual/resources/icons/16/convert.png +0 -0
  101. pycforge_sdk/gui/visual/resources/icons/16/copy.png +0 -0
  102. pycforge_sdk/gui/visual/resources/icons/16/cut.png +0 -0
  103. pycforge_sdk/gui/visual/resources/icons/16/decision-trace.png +0 -0
  104. pycforge_sdk/gui/visual/resources/icons/16/details.png +0 -0
  105. pycforge_sdk/gui/visual/resources/icons/16/diagnostics.png +0 -0
  106. pycforge_sdk/gui/visual/resources/icons/16/duplicate-line.png +0 -0
  107. pycforge_sdk/gui/visual/resources/icons/16/exit.png +0 -0
  108. pycforge_sdk/gui/visual/resources/icons/16/expand-all.png +0 -0
  109. pycforge_sdk/gui/visual/resources/icons/16/export.png +0 -0
  110. pycforge_sdk/gui/visual/resources/icons/16/find.png +0 -0
  111. pycforge_sdk/gui/visual/resources/icons/16/go-to-line.png +0 -0
  112. pycforge_sdk/gui/visual/resources/icons/16/go-to-output.png +0 -0
  113. pycforge_sdk/gui/visual/resources/icons/16/go-to-source.png +0 -0
  114. pycforge_sdk/gui/visual/resources/icons/16/help.png +0 -0
  115. pycforge_sdk/gui/visual/resources/icons/16/history.png +0 -0
  116. pycforge_sdk/gui/visual/resources/icons/16/indent.png +0 -0
  117. pycforge_sdk/gui/visual/resources/icons/16/link-c.png +0 -0
  118. pycforge_sdk/gui/visual/resources/icons/16/mappings.png +0 -0
  119. pycforge_sdk/gui/visual/resources/icons/16/module.png +0 -0
  120. pycforge_sdk/gui/visual/resources/icons/16/move-down.png +0 -0
  121. pycforge_sdk/gui/visual/resources/icons/16/move-line-down.png +0 -0
  122. pycforge_sdk/gui/visual/resources/icons/16/move-line-up.png +0 -0
  123. pycforge_sdk/gui/visual/resources/icons/16/move-up.png +0 -0
  124. pycforge_sdk/gui/visual/resources/icons/16/next.png +0 -0
  125. pycforge_sdk/gui/visual/resources/icons/16/open.png +0 -0
  126. pycforge_sdk/gui/visual/resources/icons/16/outdent.png +0 -0
  127. pycforge_sdk/gui/visual/resources/icons/16/outline.png +0 -0
  128. pycforge_sdk/gui/visual/resources/icons/16/paste.png +0 -0
  129. pycforge_sdk/gui/visual/resources/icons/16/previous.png +0 -0
  130. pycforge_sdk/gui/visual/resources/icons/16/primary-module.png +0 -0
  131. pycforge_sdk/gui/visual/resources/icons/16/redo.png +0 -0
  132. pycforge_sdk/gui/visual/resources/icons/16/remove-document.png +0 -0
  133. pycforge_sdk/gui/visual/resources/icons/16/replace.png +0 -0
  134. pycforge_sdk/gui/visual/resources/icons/16/save-as.png +0 -0
  135. pycforge_sdk/gui/visual/resources/icons/16/save.png +0 -0
  136. pycforge_sdk/gui/visual/resources/icons/16/select-all.png +0 -0
  137. pycforge_sdk/gui/visual/resources/icons/16/settings.png +0 -0
  138. pycforge_sdk/gui/visual/resources/icons/16/show-c.png +0 -0
  139. pycforge_sdk/gui/visual/resources/icons/16/split-view.png +0 -0
  140. pycforge_sdk/gui/visual/resources/icons/16/summary.png +0 -0
  141. pycforge_sdk/gui/visual/resources/icons/16/telemetry.png +0 -0
  142. pycforge_sdk/gui/visual/resources/icons/16/toggle-comment.png +0 -0
  143. pycforge_sdk/gui/visual/resources/icons/16/toggle-fold.png +0 -0
  144. pycforge_sdk/gui/visual/resources/icons/16/tools.png +0 -0
  145. pycforge_sdk/gui/visual/resources/icons/16/undo.png +0 -0
  146. pycforge_sdk/gui/visual/resources/icons/16/whitespace.png +0 -0
  147. pycforge_sdk/gui/visual/resources/icons/20/add-document.png +0 -0
  148. pycforge_sdk/gui/visual/resources/icons/20/brand-mark.png +0 -0
  149. pycforge_sdk/gui/visual/resources/icons/20/bundle-search.png +0 -0
  150. pycforge_sdk/gui/visual/resources/icons/20/cancel.png +0 -0
  151. pycforge_sdk/gui/visual/resources/icons/20/check.png +0 -0
  152. pycforge_sdk/gui/visual/resources/icons/20/chevron-right.png +0 -0
  153. pycforge_sdk/gui/visual/resources/icons/20/close.png +0 -0
  154. pycforge_sdk/gui/visual/resources/icons/20/collapse-all.png +0 -0
  155. pycforge_sdk/gui/visual/resources/icons/20/command-palette.png +0 -0
  156. pycforge_sdk/gui/visual/resources/icons/20/convert.png +0 -0
  157. pycforge_sdk/gui/visual/resources/icons/20/copy.png +0 -0
  158. pycforge_sdk/gui/visual/resources/icons/20/cut.png +0 -0
  159. pycforge_sdk/gui/visual/resources/icons/20/decision-trace.png +0 -0
  160. pycforge_sdk/gui/visual/resources/icons/20/details.png +0 -0
  161. pycforge_sdk/gui/visual/resources/icons/20/diagnostics.png +0 -0
  162. pycforge_sdk/gui/visual/resources/icons/20/duplicate-line.png +0 -0
  163. pycforge_sdk/gui/visual/resources/icons/20/exit.png +0 -0
  164. pycforge_sdk/gui/visual/resources/icons/20/expand-all.png +0 -0
  165. pycforge_sdk/gui/visual/resources/icons/20/export.png +0 -0
  166. pycforge_sdk/gui/visual/resources/icons/20/find.png +0 -0
  167. pycforge_sdk/gui/visual/resources/icons/20/go-to-line.png +0 -0
  168. pycforge_sdk/gui/visual/resources/icons/20/go-to-output.png +0 -0
  169. pycforge_sdk/gui/visual/resources/icons/20/go-to-source.png +0 -0
  170. pycforge_sdk/gui/visual/resources/icons/20/help.png +0 -0
  171. pycforge_sdk/gui/visual/resources/icons/20/history.png +0 -0
  172. pycforge_sdk/gui/visual/resources/icons/20/indent.png +0 -0
  173. pycforge_sdk/gui/visual/resources/icons/20/link-c.png +0 -0
  174. pycforge_sdk/gui/visual/resources/icons/20/mappings.png +0 -0
  175. pycforge_sdk/gui/visual/resources/icons/20/module.png +0 -0
  176. pycforge_sdk/gui/visual/resources/icons/20/move-down.png +0 -0
  177. pycforge_sdk/gui/visual/resources/icons/20/move-line-down.png +0 -0
  178. pycforge_sdk/gui/visual/resources/icons/20/move-line-up.png +0 -0
  179. pycforge_sdk/gui/visual/resources/icons/20/move-up.png +0 -0
  180. pycforge_sdk/gui/visual/resources/icons/20/next.png +0 -0
  181. pycforge_sdk/gui/visual/resources/icons/20/open.png +0 -0
  182. pycforge_sdk/gui/visual/resources/icons/20/outdent.png +0 -0
  183. pycforge_sdk/gui/visual/resources/icons/20/outline.png +0 -0
  184. pycforge_sdk/gui/visual/resources/icons/20/paste.png +0 -0
  185. pycforge_sdk/gui/visual/resources/icons/20/previous.png +0 -0
  186. pycforge_sdk/gui/visual/resources/icons/20/primary-module.png +0 -0
  187. pycforge_sdk/gui/visual/resources/icons/20/redo.png +0 -0
  188. pycforge_sdk/gui/visual/resources/icons/20/remove-document.png +0 -0
  189. pycforge_sdk/gui/visual/resources/icons/20/replace.png +0 -0
  190. pycforge_sdk/gui/visual/resources/icons/20/save-as.png +0 -0
  191. pycforge_sdk/gui/visual/resources/icons/20/save.png +0 -0
  192. pycforge_sdk/gui/visual/resources/icons/20/select-all.png +0 -0
  193. pycforge_sdk/gui/visual/resources/icons/20/settings.png +0 -0
  194. pycforge_sdk/gui/visual/resources/icons/20/show-c.png +0 -0
  195. pycforge_sdk/gui/visual/resources/icons/20/split-view.png +0 -0
  196. pycforge_sdk/gui/visual/resources/icons/20/summary.png +0 -0
  197. pycforge_sdk/gui/visual/resources/icons/20/telemetry.png +0 -0
  198. pycforge_sdk/gui/visual/resources/icons/20/toggle-comment.png +0 -0
  199. pycforge_sdk/gui/visual/resources/icons/20/toggle-fold.png +0 -0
  200. pycforge_sdk/gui/visual/resources/icons/20/tools.png +0 -0
  201. pycforge_sdk/gui/visual/resources/icons/20/undo.png +0 -0
  202. pycforge_sdk/gui/visual/resources/icons/20/whitespace.png +0 -0
  203. pycforge_sdk/gui/visual/resources/icons/24/add-document.png +0 -0
  204. pycforge_sdk/gui/visual/resources/icons/24/brand-mark.png +0 -0
  205. pycforge_sdk/gui/visual/resources/icons/24/bundle-search.png +0 -0
  206. pycforge_sdk/gui/visual/resources/icons/24/cancel.png +0 -0
  207. pycforge_sdk/gui/visual/resources/icons/24/check.png +0 -0
  208. pycforge_sdk/gui/visual/resources/icons/24/chevron-right.png +0 -0
  209. pycforge_sdk/gui/visual/resources/icons/24/close.png +0 -0
  210. pycforge_sdk/gui/visual/resources/icons/24/collapse-all.png +0 -0
  211. pycforge_sdk/gui/visual/resources/icons/24/command-palette.png +0 -0
  212. pycforge_sdk/gui/visual/resources/icons/24/convert.png +0 -0
  213. pycforge_sdk/gui/visual/resources/icons/24/copy.png +0 -0
  214. pycforge_sdk/gui/visual/resources/icons/24/cut.png +0 -0
  215. pycforge_sdk/gui/visual/resources/icons/24/decision-trace.png +0 -0
  216. pycforge_sdk/gui/visual/resources/icons/24/details.png +0 -0
  217. pycforge_sdk/gui/visual/resources/icons/24/diagnostics.png +0 -0
  218. pycforge_sdk/gui/visual/resources/icons/24/duplicate-line.png +0 -0
  219. pycforge_sdk/gui/visual/resources/icons/24/exit.png +0 -0
  220. pycforge_sdk/gui/visual/resources/icons/24/expand-all.png +0 -0
  221. pycforge_sdk/gui/visual/resources/icons/24/export.png +0 -0
  222. pycforge_sdk/gui/visual/resources/icons/24/find.png +0 -0
  223. pycforge_sdk/gui/visual/resources/icons/24/go-to-line.png +0 -0
  224. pycforge_sdk/gui/visual/resources/icons/24/go-to-output.png +0 -0
  225. pycforge_sdk/gui/visual/resources/icons/24/go-to-source.png +0 -0
  226. pycforge_sdk/gui/visual/resources/icons/24/help.png +0 -0
  227. pycforge_sdk/gui/visual/resources/icons/24/history.png +0 -0
  228. pycforge_sdk/gui/visual/resources/icons/24/indent.png +0 -0
  229. pycforge_sdk/gui/visual/resources/icons/24/link-c.png +0 -0
  230. pycforge_sdk/gui/visual/resources/icons/24/mappings.png +0 -0
  231. pycforge_sdk/gui/visual/resources/icons/24/module.png +0 -0
  232. pycforge_sdk/gui/visual/resources/icons/24/move-down.png +0 -0
  233. pycforge_sdk/gui/visual/resources/icons/24/move-line-down.png +0 -0
  234. pycforge_sdk/gui/visual/resources/icons/24/move-line-up.png +0 -0
  235. pycforge_sdk/gui/visual/resources/icons/24/move-up.png +0 -0
  236. pycforge_sdk/gui/visual/resources/icons/24/next.png +0 -0
  237. pycforge_sdk/gui/visual/resources/icons/24/open.png +0 -0
  238. pycforge_sdk/gui/visual/resources/icons/24/outdent.png +0 -0
  239. pycforge_sdk/gui/visual/resources/icons/24/outline.png +0 -0
  240. pycforge_sdk/gui/visual/resources/icons/24/paste.png +0 -0
  241. pycforge_sdk/gui/visual/resources/icons/24/previous.png +0 -0
  242. pycforge_sdk/gui/visual/resources/icons/24/primary-module.png +0 -0
  243. pycforge_sdk/gui/visual/resources/icons/24/redo.png +0 -0
  244. pycforge_sdk/gui/visual/resources/icons/24/remove-document.png +0 -0
  245. pycforge_sdk/gui/visual/resources/icons/24/replace.png +0 -0
  246. pycforge_sdk/gui/visual/resources/icons/24/save-as.png +0 -0
  247. pycforge_sdk/gui/visual/resources/icons/24/save.png +0 -0
  248. pycforge_sdk/gui/visual/resources/icons/24/select-all.png +0 -0
  249. pycforge_sdk/gui/visual/resources/icons/24/settings.png +0 -0
  250. pycforge_sdk/gui/visual/resources/icons/24/show-c.png +0 -0
  251. pycforge_sdk/gui/visual/resources/icons/24/split-view.png +0 -0
  252. pycforge_sdk/gui/visual/resources/icons/24/summary.png +0 -0
  253. pycforge_sdk/gui/visual/resources/icons/24/telemetry.png +0 -0
  254. pycforge_sdk/gui/visual/resources/icons/24/toggle-comment.png +0 -0
  255. pycforge_sdk/gui/visual/resources/icons/24/toggle-fold.png +0 -0
  256. pycforge_sdk/gui/visual/resources/icons/24/tools.png +0 -0
  257. pycforge_sdk/gui/visual/resources/icons/24/undo.png +0 -0
  258. pycforge_sdk/gui/visual/resources/icons/24/whitespace.png +0 -0
  259. pycforge_sdk/gui/visual/resources/icons/256/add-document.png +0 -0
  260. pycforge_sdk/gui/visual/resources/icons/256/brand-mark.png +0 -0
  261. pycforge_sdk/gui/visual/resources/icons/256/bundle-search.png +0 -0
  262. pycforge_sdk/gui/visual/resources/icons/256/cancel.png +0 -0
  263. pycforge_sdk/gui/visual/resources/icons/256/check.png +0 -0
  264. pycforge_sdk/gui/visual/resources/icons/256/chevron-right.png +0 -0
  265. pycforge_sdk/gui/visual/resources/icons/256/close.png +0 -0
  266. pycforge_sdk/gui/visual/resources/icons/256/collapse-all.png +0 -0
  267. pycforge_sdk/gui/visual/resources/icons/256/command-palette.png +0 -0
  268. pycforge_sdk/gui/visual/resources/icons/256/convert.png +0 -0
  269. pycforge_sdk/gui/visual/resources/icons/256/copy.png +0 -0
  270. pycforge_sdk/gui/visual/resources/icons/256/cut.png +0 -0
  271. pycforge_sdk/gui/visual/resources/icons/256/decision-trace.png +0 -0
  272. pycforge_sdk/gui/visual/resources/icons/256/details.png +0 -0
  273. pycforge_sdk/gui/visual/resources/icons/256/diagnostics.png +0 -0
  274. pycforge_sdk/gui/visual/resources/icons/256/duplicate-line.png +0 -0
  275. pycforge_sdk/gui/visual/resources/icons/256/exit.png +0 -0
  276. pycforge_sdk/gui/visual/resources/icons/256/expand-all.png +0 -0
  277. pycforge_sdk/gui/visual/resources/icons/256/export.png +0 -0
  278. pycforge_sdk/gui/visual/resources/icons/256/find.png +0 -0
  279. pycforge_sdk/gui/visual/resources/icons/256/go-to-line.png +0 -0
  280. pycforge_sdk/gui/visual/resources/icons/256/go-to-output.png +0 -0
  281. pycforge_sdk/gui/visual/resources/icons/256/go-to-source.png +0 -0
  282. pycforge_sdk/gui/visual/resources/icons/256/help.png +0 -0
  283. pycforge_sdk/gui/visual/resources/icons/256/history.png +0 -0
  284. pycforge_sdk/gui/visual/resources/icons/256/indent.png +0 -0
  285. pycforge_sdk/gui/visual/resources/icons/256/link-c.png +0 -0
  286. pycforge_sdk/gui/visual/resources/icons/256/mappings.png +0 -0
  287. pycforge_sdk/gui/visual/resources/icons/256/module.png +0 -0
  288. pycforge_sdk/gui/visual/resources/icons/256/move-down.png +0 -0
  289. pycforge_sdk/gui/visual/resources/icons/256/move-line-down.png +0 -0
  290. pycforge_sdk/gui/visual/resources/icons/256/move-line-up.png +0 -0
  291. pycforge_sdk/gui/visual/resources/icons/256/move-up.png +0 -0
  292. pycforge_sdk/gui/visual/resources/icons/256/next.png +0 -0
  293. pycforge_sdk/gui/visual/resources/icons/256/open.png +0 -0
  294. pycforge_sdk/gui/visual/resources/icons/256/outdent.png +0 -0
  295. pycforge_sdk/gui/visual/resources/icons/256/outline.png +0 -0
  296. pycforge_sdk/gui/visual/resources/icons/256/paste.png +0 -0
  297. pycforge_sdk/gui/visual/resources/icons/256/previous.png +0 -0
  298. pycforge_sdk/gui/visual/resources/icons/256/primary-module.png +0 -0
  299. pycforge_sdk/gui/visual/resources/icons/256/redo.png +0 -0
  300. pycforge_sdk/gui/visual/resources/icons/256/remove-document.png +0 -0
  301. pycforge_sdk/gui/visual/resources/icons/256/replace.png +0 -0
  302. pycforge_sdk/gui/visual/resources/icons/256/save-as.png +0 -0
  303. pycforge_sdk/gui/visual/resources/icons/256/save.png +0 -0
  304. pycforge_sdk/gui/visual/resources/icons/256/select-all.png +0 -0
  305. pycforge_sdk/gui/visual/resources/icons/256/settings.png +0 -0
  306. pycforge_sdk/gui/visual/resources/icons/256/show-c.png +0 -0
  307. pycforge_sdk/gui/visual/resources/icons/256/split-view.png +0 -0
  308. pycforge_sdk/gui/visual/resources/icons/256/summary.png +0 -0
  309. pycforge_sdk/gui/visual/resources/icons/256/telemetry.png +0 -0
  310. pycforge_sdk/gui/visual/resources/icons/256/toggle-comment.png +0 -0
  311. pycforge_sdk/gui/visual/resources/icons/256/toggle-fold.png +0 -0
  312. pycforge_sdk/gui/visual/resources/icons/256/tools.png +0 -0
  313. pycforge_sdk/gui/visual/resources/icons/256/undo.png +0 -0
  314. pycforge_sdk/gui/visual/resources/icons/256/whitespace.png +0 -0
  315. pycforge_sdk/gui/visual/resources/icons/32/add-document.png +0 -0
  316. pycforge_sdk/gui/visual/resources/icons/32/brand-mark.png +0 -0
  317. pycforge_sdk/gui/visual/resources/icons/32/bundle-search.png +0 -0
  318. pycforge_sdk/gui/visual/resources/icons/32/cancel.png +0 -0
  319. pycforge_sdk/gui/visual/resources/icons/32/check.png +0 -0
  320. pycforge_sdk/gui/visual/resources/icons/32/chevron-right.png +0 -0
  321. pycforge_sdk/gui/visual/resources/icons/32/close.png +0 -0
  322. pycforge_sdk/gui/visual/resources/icons/32/collapse-all.png +0 -0
  323. pycforge_sdk/gui/visual/resources/icons/32/command-palette.png +0 -0
  324. pycforge_sdk/gui/visual/resources/icons/32/convert.png +0 -0
  325. pycforge_sdk/gui/visual/resources/icons/32/copy.png +0 -0
  326. pycforge_sdk/gui/visual/resources/icons/32/cut.png +0 -0
  327. pycforge_sdk/gui/visual/resources/icons/32/decision-trace.png +0 -0
  328. pycforge_sdk/gui/visual/resources/icons/32/details.png +0 -0
  329. pycforge_sdk/gui/visual/resources/icons/32/diagnostics.png +0 -0
  330. pycforge_sdk/gui/visual/resources/icons/32/duplicate-line.png +0 -0
  331. pycforge_sdk/gui/visual/resources/icons/32/exit.png +0 -0
  332. pycforge_sdk/gui/visual/resources/icons/32/expand-all.png +0 -0
  333. pycforge_sdk/gui/visual/resources/icons/32/export.png +0 -0
  334. pycforge_sdk/gui/visual/resources/icons/32/find.png +0 -0
  335. pycforge_sdk/gui/visual/resources/icons/32/go-to-line.png +0 -0
  336. pycforge_sdk/gui/visual/resources/icons/32/go-to-output.png +0 -0
  337. pycforge_sdk/gui/visual/resources/icons/32/go-to-source.png +0 -0
  338. pycforge_sdk/gui/visual/resources/icons/32/help.png +0 -0
  339. pycforge_sdk/gui/visual/resources/icons/32/history.png +0 -0
  340. pycforge_sdk/gui/visual/resources/icons/32/indent.png +0 -0
  341. pycforge_sdk/gui/visual/resources/icons/32/link-c.png +0 -0
  342. pycforge_sdk/gui/visual/resources/icons/32/mappings.png +0 -0
  343. pycforge_sdk/gui/visual/resources/icons/32/module.png +0 -0
  344. pycforge_sdk/gui/visual/resources/icons/32/move-down.png +0 -0
  345. pycforge_sdk/gui/visual/resources/icons/32/move-line-down.png +0 -0
  346. pycforge_sdk/gui/visual/resources/icons/32/move-line-up.png +0 -0
  347. pycforge_sdk/gui/visual/resources/icons/32/move-up.png +0 -0
  348. pycforge_sdk/gui/visual/resources/icons/32/next.png +0 -0
  349. pycforge_sdk/gui/visual/resources/icons/32/open.png +0 -0
  350. pycforge_sdk/gui/visual/resources/icons/32/outdent.png +0 -0
  351. pycforge_sdk/gui/visual/resources/icons/32/outline.png +0 -0
  352. pycforge_sdk/gui/visual/resources/icons/32/paste.png +0 -0
  353. pycforge_sdk/gui/visual/resources/icons/32/previous.png +0 -0
  354. pycforge_sdk/gui/visual/resources/icons/32/primary-module.png +0 -0
  355. pycforge_sdk/gui/visual/resources/icons/32/redo.png +0 -0
  356. pycforge_sdk/gui/visual/resources/icons/32/remove-document.png +0 -0
  357. pycforge_sdk/gui/visual/resources/icons/32/replace.png +0 -0
  358. pycforge_sdk/gui/visual/resources/icons/32/save-as.png +0 -0
  359. pycforge_sdk/gui/visual/resources/icons/32/save.png +0 -0
  360. pycforge_sdk/gui/visual/resources/icons/32/select-all.png +0 -0
  361. pycforge_sdk/gui/visual/resources/icons/32/settings.png +0 -0
  362. pycforge_sdk/gui/visual/resources/icons/32/show-c.png +0 -0
  363. pycforge_sdk/gui/visual/resources/icons/32/split-view.png +0 -0
  364. pycforge_sdk/gui/visual/resources/icons/32/summary.png +0 -0
  365. pycforge_sdk/gui/visual/resources/icons/32/telemetry.png +0 -0
  366. pycforge_sdk/gui/visual/resources/icons/32/toggle-comment.png +0 -0
  367. pycforge_sdk/gui/visual/resources/icons/32/toggle-fold.png +0 -0
  368. pycforge_sdk/gui/visual/resources/icons/32/tools.png +0 -0
  369. pycforge_sdk/gui/visual/resources/icons/32/undo.png +0 -0
  370. pycforge_sdk/gui/visual/resources/icons/32/whitespace.png +0 -0
  371. pycforge_sdk/gui/visual/resources/icons/48/add-document.png +0 -0
  372. pycforge_sdk/gui/visual/resources/icons/48/brand-mark.png +0 -0
  373. pycforge_sdk/gui/visual/resources/icons/48/bundle-search.png +0 -0
  374. pycforge_sdk/gui/visual/resources/icons/48/cancel.png +0 -0
  375. pycforge_sdk/gui/visual/resources/icons/48/check.png +0 -0
  376. pycforge_sdk/gui/visual/resources/icons/48/chevron-right.png +0 -0
  377. pycforge_sdk/gui/visual/resources/icons/48/close.png +0 -0
  378. pycforge_sdk/gui/visual/resources/icons/48/collapse-all.png +0 -0
  379. pycforge_sdk/gui/visual/resources/icons/48/command-palette.png +0 -0
  380. pycforge_sdk/gui/visual/resources/icons/48/convert.png +0 -0
  381. pycforge_sdk/gui/visual/resources/icons/48/copy.png +0 -0
  382. pycforge_sdk/gui/visual/resources/icons/48/cut.png +0 -0
  383. pycforge_sdk/gui/visual/resources/icons/48/decision-trace.png +0 -0
  384. pycforge_sdk/gui/visual/resources/icons/48/details.png +0 -0
  385. pycforge_sdk/gui/visual/resources/icons/48/diagnostics.png +0 -0
  386. pycforge_sdk/gui/visual/resources/icons/48/duplicate-line.png +0 -0
  387. pycforge_sdk/gui/visual/resources/icons/48/exit.png +0 -0
  388. pycforge_sdk/gui/visual/resources/icons/48/expand-all.png +0 -0
  389. pycforge_sdk/gui/visual/resources/icons/48/export.png +0 -0
  390. pycforge_sdk/gui/visual/resources/icons/48/find.png +0 -0
  391. pycforge_sdk/gui/visual/resources/icons/48/go-to-line.png +0 -0
  392. pycforge_sdk/gui/visual/resources/icons/48/go-to-output.png +0 -0
  393. pycforge_sdk/gui/visual/resources/icons/48/go-to-source.png +0 -0
  394. pycforge_sdk/gui/visual/resources/icons/48/help.png +0 -0
  395. pycforge_sdk/gui/visual/resources/icons/48/history.png +0 -0
  396. pycforge_sdk/gui/visual/resources/icons/48/indent.png +0 -0
  397. pycforge_sdk/gui/visual/resources/icons/48/link-c.png +0 -0
  398. pycforge_sdk/gui/visual/resources/icons/48/mappings.png +0 -0
  399. pycforge_sdk/gui/visual/resources/icons/48/module.png +0 -0
  400. pycforge_sdk/gui/visual/resources/icons/48/move-down.png +0 -0
  401. pycforge_sdk/gui/visual/resources/icons/48/move-line-down.png +0 -0
  402. pycforge_sdk/gui/visual/resources/icons/48/move-line-up.png +0 -0
  403. pycforge_sdk/gui/visual/resources/icons/48/move-up.png +0 -0
  404. pycforge_sdk/gui/visual/resources/icons/48/next.png +0 -0
  405. pycforge_sdk/gui/visual/resources/icons/48/open.png +0 -0
  406. pycforge_sdk/gui/visual/resources/icons/48/outdent.png +0 -0
  407. pycforge_sdk/gui/visual/resources/icons/48/outline.png +0 -0
  408. pycforge_sdk/gui/visual/resources/icons/48/paste.png +0 -0
  409. pycforge_sdk/gui/visual/resources/icons/48/previous.png +0 -0
  410. pycforge_sdk/gui/visual/resources/icons/48/primary-module.png +0 -0
  411. pycforge_sdk/gui/visual/resources/icons/48/redo.png +0 -0
  412. pycforge_sdk/gui/visual/resources/icons/48/remove-document.png +0 -0
  413. pycforge_sdk/gui/visual/resources/icons/48/replace.png +0 -0
  414. pycforge_sdk/gui/visual/resources/icons/48/save-as.png +0 -0
  415. pycforge_sdk/gui/visual/resources/icons/48/save.png +0 -0
  416. pycforge_sdk/gui/visual/resources/icons/48/select-all.png +0 -0
  417. pycforge_sdk/gui/visual/resources/icons/48/settings.png +0 -0
  418. pycforge_sdk/gui/visual/resources/icons/48/show-c.png +0 -0
  419. pycforge_sdk/gui/visual/resources/icons/48/split-view.png +0 -0
  420. pycforge_sdk/gui/visual/resources/icons/48/summary.png +0 -0
  421. pycforge_sdk/gui/visual/resources/icons/48/telemetry.png +0 -0
  422. pycforge_sdk/gui/visual/resources/icons/48/toggle-comment.png +0 -0
  423. pycforge_sdk/gui/visual/resources/icons/48/toggle-fold.png +0 -0
  424. pycforge_sdk/gui/visual/resources/icons/48/tools.png +0 -0
  425. pycforge_sdk/gui/visual/resources/icons/48/undo.png +0 -0
  426. pycforge_sdk/gui/visual/resources/icons/48/whitespace.png +0 -0
  427. pycforge_sdk/gui/visual/resources/icons/64/add-document.png +0 -0
  428. pycforge_sdk/gui/visual/resources/icons/64/brand-mark.png +0 -0
  429. pycforge_sdk/gui/visual/resources/icons/64/bundle-search.png +0 -0
  430. pycforge_sdk/gui/visual/resources/icons/64/cancel.png +0 -0
  431. pycforge_sdk/gui/visual/resources/icons/64/check.png +0 -0
  432. pycforge_sdk/gui/visual/resources/icons/64/chevron-right.png +0 -0
  433. pycforge_sdk/gui/visual/resources/icons/64/close.png +0 -0
  434. pycforge_sdk/gui/visual/resources/icons/64/collapse-all.png +0 -0
  435. pycforge_sdk/gui/visual/resources/icons/64/command-palette.png +0 -0
  436. pycforge_sdk/gui/visual/resources/icons/64/convert.png +0 -0
  437. pycforge_sdk/gui/visual/resources/icons/64/copy.png +0 -0
  438. pycforge_sdk/gui/visual/resources/icons/64/cut.png +0 -0
  439. pycforge_sdk/gui/visual/resources/icons/64/decision-trace.png +0 -0
  440. pycforge_sdk/gui/visual/resources/icons/64/details.png +0 -0
  441. pycforge_sdk/gui/visual/resources/icons/64/diagnostics.png +0 -0
  442. pycforge_sdk/gui/visual/resources/icons/64/duplicate-line.png +0 -0
  443. pycforge_sdk/gui/visual/resources/icons/64/exit.png +0 -0
  444. pycforge_sdk/gui/visual/resources/icons/64/expand-all.png +0 -0
  445. pycforge_sdk/gui/visual/resources/icons/64/export.png +0 -0
  446. pycforge_sdk/gui/visual/resources/icons/64/find.png +0 -0
  447. pycforge_sdk/gui/visual/resources/icons/64/go-to-line.png +0 -0
  448. pycforge_sdk/gui/visual/resources/icons/64/go-to-output.png +0 -0
  449. pycforge_sdk/gui/visual/resources/icons/64/go-to-source.png +0 -0
  450. pycforge_sdk/gui/visual/resources/icons/64/help.png +0 -0
  451. pycforge_sdk/gui/visual/resources/icons/64/history.png +0 -0
  452. pycforge_sdk/gui/visual/resources/icons/64/indent.png +0 -0
  453. pycforge_sdk/gui/visual/resources/icons/64/link-c.png +0 -0
  454. pycforge_sdk/gui/visual/resources/icons/64/mappings.png +0 -0
  455. pycforge_sdk/gui/visual/resources/icons/64/module.png +0 -0
  456. pycforge_sdk/gui/visual/resources/icons/64/move-down.png +0 -0
  457. pycforge_sdk/gui/visual/resources/icons/64/move-line-down.png +0 -0
  458. pycforge_sdk/gui/visual/resources/icons/64/move-line-up.png +0 -0
  459. pycforge_sdk/gui/visual/resources/icons/64/move-up.png +0 -0
  460. pycforge_sdk/gui/visual/resources/icons/64/next.png +0 -0
  461. pycforge_sdk/gui/visual/resources/icons/64/open.png +0 -0
  462. pycforge_sdk/gui/visual/resources/icons/64/outdent.png +0 -0
  463. pycforge_sdk/gui/visual/resources/icons/64/outline.png +0 -0
  464. pycforge_sdk/gui/visual/resources/icons/64/paste.png +0 -0
  465. pycforge_sdk/gui/visual/resources/icons/64/previous.png +0 -0
  466. pycforge_sdk/gui/visual/resources/icons/64/primary-module.png +0 -0
  467. pycforge_sdk/gui/visual/resources/icons/64/redo.png +0 -0
  468. pycforge_sdk/gui/visual/resources/icons/64/remove-document.png +0 -0
  469. pycforge_sdk/gui/visual/resources/icons/64/replace.png +0 -0
  470. pycforge_sdk/gui/visual/resources/icons/64/save-as.png +0 -0
  471. pycforge_sdk/gui/visual/resources/icons/64/save.png +0 -0
  472. pycforge_sdk/gui/visual/resources/icons/64/select-all.png +0 -0
  473. pycforge_sdk/gui/visual/resources/icons/64/settings.png +0 -0
  474. pycforge_sdk/gui/visual/resources/icons/64/show-c.png +0 -0
  475. pycforge_sdk/gui/visual/resources/icons/64/split-view.png +0 -0
  476. pycforge_sdk/gui/visual/resources/icons/64/summary.png +0 -0
  477. pycforge_sdk/gui/visual/resources/icons/64/telemetry.png +0 -0
  478. pycforge_sdk/gui/visual/resources/icons/64/toggle-comment.png +0 -0
  479. pycforge_sdk/gui/visual/resources/icons/64/toggle-fold.png +0 -0
  480. pycforge_sdk/gui/visual/resources/icons/64/tools.png +0 -0
  481. pycforge_sdk/gui/visual/resources/icons/64/undo.png +0 -0
  482. pycforge_sdk/gui/visual/resources/icons/64/whitespace.png +0 -0
  483. pycforge_sdk/gui/visual/resources/icons/96/add-document.png +0 -0
  484. pycforge_sdk/gui/visual/resources/icons/96/brand-mark.png +0 -0
  485. pycforge_sdk/gui/visual/resources/icons/96/bundle-search.png +0 -0
  486. pycforge_sdk/gui/visual/resources/icons/96/cancel.png +0 -0
  487. pycforge_sdk/gui/visual/resources/icons/96/check.png +0 -0
  488. pycforge_sdk/gui/visual/resources/icons/96/chevron-right.png +0 -0
  489. pycforge_sdk/gui/visual/resources/icons/96/close.png +0 -0
  490. pycforge_sdk/gui/visual/resources/icons/96/collapse-all.png +0 -0
  491. pycforge_sdk/gui/visual/resources/icons/96/command-palette.png +0 -0
  492. pycforge_sdk/gui/visual/resources/icons/96/convert.png +0 -0
  493. pycforge_sdk/gui/visual/resources/icons/96/copy.png +0 -0
  494. pycforge_sdk/gui/visual/resources/icons/96/cut.png +0 -0
  495. pycforge_sdk/gui/visual/resources/icons/96/decision-trace.png +0 -0
  496. pycforge_sdk/gui/visual/resources/icons/96/details.png +0 -0
  497. pycforge_sdk/gui/visual/resources/icons/96/diagnostics.png +0 -0
  498. pycforge_sdk/gui/visual/resources/icons/96/duplicate-line.png +0 -0
  499. pycforge_sdk/gui/visual/resources/icons/96/exit.png +0 -0
  500. pycforge_sdk/gui/visual/resources/icons/96/expand-all.png +0 -0
  501. pycforge_sdk/gui/visual/resources/icons/96/export.png +0 -0
  502. pycforge_sdk/gui/visual/resources/icons/96/find.png +0 -0
  503. pycforge_sdk/gui/visual/resources/icons/96/go-to-line.png +0 -0
  504. pycforge_sdk/gui/visual/resources/icons/96/go-to-output.png +0 -0
  505. pycforge_sdk/gui/visual/resources/icons/96/go-to-source.png +0 -0
  506. pycforge_sdk/gui/visual/resources/icons/96/help.png +0 -0
  507. pycforge_sdk/gui/visual/resources/icons/96/history.png +0 -0
  508. pycforge_sdk/gui/visual/resources/icons/96/indent.png +0 -0
  509. pycforge_sdk/gui/visual/resources/icons/96/link-c.png +0 -0
  510. pycforge_sdk/gui/visual/resources/icons/96/mappings.png +0 -0
  511. pycforge_sdk/gui/visual/resources/icons/96/module.png +0 -0
  512. pycforge_sdk/gui/visual/resources/icons/96/move-down.png +0 -0
  513. pycforge_sdk/gui/visual/resources/icons/96/move-line-down.png +0 -0
  514. pycforge_sdk/gui/visual/resources/icons/96/move-line-up.png +0 -0
  515. pycforge_sdk/gui/visual/resources/icons/96/move-up.png +0 -0
  516. pycforge_sdk/gui/visual/resources/icons/96/next.png +0 -0
  517. pycforge_sdk/gui/visual/resources/icons/96/open.png +0 -0
  518. pycforge_sdk/gui/visual/resources/icons/96/outdent.png +0 -0
  519. pycforge_sdk/gui/visual/resources/icons/96/outline.png +0 -0
  520. pycforge_sdk/gui/visual/resources/icons/96/paste.png +0 -0
  521. pycforge_sdk/gui/visual/resources/icons/96/previous.png +0 -0
  522. pycforge_sdk/gui/visual/resources/icons/96/primary-module.png +0 -0
  523. pycforge_sdk/gui/visual/resources/icons/96/redo.png +0 -0
  524. pycforge_sdk/gui/visual/resources/icons/96/remove-document.png +0 -0
  525. pycforge_sdk/gui/visual/resources/icons/96/replace.png +0 -0
  526. pycforge_sdk/gui/visual/resources/icons/96/save-as.png +0 -0
  527. pycforge_sdk/gui/visual/resources/icons/96/save.png +0 -0
  528. pycforge_sdk/gui/visual/resources/icons/96/select-all.png +0 -0
  529. pycforge_sdk/gui/visual/resources/icons/96/settings.png +0 -0
  530. pycforge_sdk/gui/visual/resources/icons/96/show-c.png +0 -0
  531. pycforge_sdk/gui/visual/resources/icons/96/split-view.png +0 -0
  532. pycforge_sdk/gui/visual/resources/icons/96/summary.png +0 -0
  533. pycforge_sdk/gui/visual/resources/icons/96/telemetry.png +0 -0
  534. pycforge_sdk/gui/visual/resources/icons/96/toggle-comment.png +0 -0
  535. pycforge_sdk/gui/visual/resources/icons/96/toggle-fold.png +0 -0
  536. pycforge_sdk/gui/visual/resources/icons/96/tools.png +0 -0
  537. pycforge_sdk/gui/visual/resources/icons/96/undo.png +0 -0
  538. pycforge_sdk/gui/visual/resources/icons/96/whitespace.png +0 -0
  539. pycforge_sdk/gui/visual/theme.py +204 -0
  540. pycforge_sdk/gui/visual/theme_steel_stylesheet.py +901 -0
  541. pycforge_sdk/gui/visual/theme_stylesheet.py +597 -0
  542. pycforge_sdk/gui/visual/theme_workspace_stylesheet.py +169 -0
  543. pycforge_sdk/gui/visual/visual_tokens.py +146 -0
  544. pycforge_sdk/gui/widgets.py +68 -0
  545. pycforge_sdk/gui/window.py +559 -0
  546. pycforge_sdk/native.py +42 -0
  547. pycforge_sdk/package_metadata.py +21 -0
  548. pycforge_sdk/process.py +58 -0
  549. pycforge_sdk/project.py +143 -0
  550. pycforge_sdk/readiness.py +106 -0
  551. pycforge_sdk/results.py +45 -0
  552. pycforge_sdk/service.py +454 -0
  553. pycforge_sdk/workbench.py +88 -0
  554. pycforge_sdk-0.6.3.dist-info/METADATA +221 -0
  555. pycforge_sdk-0.6.3.dist-info/RECORD +559 -0
  556. pycforge_sdk-0.6.3.dist-info/WHEEL +5 -0
  557. pycforge_sdk-0.6.3.dist-info/entry_points.txt +5 -0
  558. pycforge_sdk-0.6.3.dist-info/licenses/LICENSE +674 -0
  559. pycforge_sdk-0.6.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,559 @@
1
+ """Desktop workflow coordinator; conversion and readiness live in the SDK."""
2
+ from pathlib import Path
3
+ import copy
4
+ import json
5
+ import sys
6
+ from PySide6.QtCore import Qt, QSize, QTimer, QFileSystemWatcher, QSettings
7
+ from PySide6.QtGui import QAction, QKeySequence
8
+ from PySide6.QtWidgets import (QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
9
+ QLineEdit, QSplitter, QStackedWidget, QListWidget, QToolBar, QPlainTextEdit,
10
+ QDialog, QFrame, QLabel, QComboBox, QScrollArea)
11
+ from .. import __version__
12
+ from .visual.qt_window_chrome import install_frameless_window_chrome
13
+ from .visual.qt_icons import load_pycforge_icon
14
+ from .identity import SDK_QSS, sdk_icon, brand_panel, PathLabel
15
+ from .startup import StartupDialog
16
+ from .widgets import label, button, SafeBrowser, MenuIconStyle
17
+ from .dialogs import Dialog, Prompt, PathDialog, NewProjectDialog, SourceDialog
18
+ from .editor import ProjectEditor
19
+ from .scanner import ScannerPanel
20
+ from .reference import ReferencePanel
21
+ from .jobs import JobRunner
22
+
23
+
24
+ class MainWindow(QMainWindow):
25
+ def __init__(self, *, engine_python=None, cache=None, compiler=None, timeout=30,
26
+ settings=None, parent=None):
27
+ super().__init__(parent)
28
+ self.settings = settings if settings is not None else QSettings("PyCForge", "SDK")
29
+ self.cache = str(cache or Path.home()/".pycforge-sdk"/"cache")
30
+ self.timeout = timeout
31
+ self.snapshot = None
32
+ self.dirty = self.external_change = False
33
+ self.revision = 0
34
+ self.ready = False
35
+ self.connection = "unavailable"
36
+ self.execution_available = False
37
+ self.templates = []; self.methods = []; self.operations = []
38
+ self.workbench = None; self.help_documents = []; self.help_engine = ""
39
+ self.close_pending = self.close_after_save = False
40
+ self.after_connect = None
41
+ self.explicit_engine = str(engine_python) if engine_python else None
42
+ self.startup_dialog = None
43
+ self.offline_pending = False
44
+ self.catalogue_offline = False
45
+ self.setWindowTitle(f"PyCForge SDK {__version__}")
46
+ self.setWindowIcon(sdk_icon())
47
+ self.setStyleSheet(SDK_QSS)
48
+ self.setMinimumSize(920, 640); self.resize(1320, 860)
49
+ self.runner = JobRunner(self)
50
+ self.runner.busy_changed.connect(self.busy_changed)
51
+ self.runner.finished.connect(self.finished)
52
+ self.runner.progress.connect(self.progress)
53
+ self.watcher = QFileSystemWatcher(self)
54
+ self.watcher.fileChanged.connect(self.disk_changed)
55
+ self._make_actions()
56
+ self._make_menus()
57
+ central = QWidget(); layout = QVBoxLayout(central); layout.setContentsMargins(10, 7, 10, 8)
58
+ self.link_panel = QFrame(); self.link_panel.setObjectName("SDKEngineLink")
59
+ link_layout = QHBoxLayout(self.link_panel); link_layout.setContentsMargins(13, 10, 12, 10)
60
+ mark = QLabel(); mark.setFixedSize(40, 40)
61
+ mark.setPixmap(sdk_icon().pixmap(mark.size(), self.devicePixelRatioF())); link_layout.addWidget(mark)
62
+ link_text = QVBoxLayout(); link_text.setSpacing(3)
63
+ tag = label("ENGINE CONNECTION"); tag.setObjectName("SDKConnectionTag"); link_text.addWidget(tag)
64
+ self.connection_label = label("Find your PyCForge installation")
65
+ self.connection_label.setObjectName("SDKConnectionTitle"); link_text.addWidget(self.connection_label)
66
+ self.connection_path = PathLabel(); link_text.addWidget(self.connection_path)
67
+ self.connection_summary = label("Conversion capabilities and reference come from PyCForge.", muted=True)
68
+ link_text.addWidget(self.connection_summary); link_layout.addLayout(link_text, 1)
69
+ self.locate_button = button("Locate PyCForge…", "open", self.browse_engine)
70
+ link_layout.addWidget(self.locate_button)
71
+ self.connect_button = button("Refresh", "history", self.refresh_engine,
72
+ "Reconnect to PyCForge and refresh its conversion capabilities and reference.")
73
+ self.connect_button.setProperty("sdkAccent", True); link_layout.addWidget(self.connect_button)
74
+ self.settings_button = button("Engine and tools", "settings", self.toggle_connection_settings)
75
+ link_layout.addWidget(self.settings_button); layout.addWidget(self.link_panel)
76
+ self.connection_settings = QWidget(); self.connection_settings.setObjectName("SDKConnectionSettings")
77
+ settings_layout = QVBoxLayout(self.connection_settings); settings_layout.setContentsMargins(0, 0, 0, 0)
78
+ engine_row = QHBoxLayout()
79
+ self.engine = QLineEdit(str(engine_python or self.settings.value("last_engine", self.settings.value("engine", sys.executable))))
80
+ self.engine.setAccessibleName("PyCForge Python interpreter")
81
+ self.engine.setToolTip("Choose the Python interpreter containing your PyCForge installation. The SDK uses it in a separate process.")
82
+ engine_row.addWidget(label("PyCForge Python")); engine_row.addWidget(self.engine, 1)
83
+ self.engine_browse = button("Locate…", "open", self.browse_engine)
84
+ self.search_button = button("Find installed", "diagnostics", self.begin_startup)
85
+ engine_row.addWidget(self.engine_browse); engine_row.addWidget(self.search_button)
86
+ self.current_python = button("This Python", "module", lambda: self.engine.setText(sys.executable),
87
+ "Use the SDK's Python interpreter if PyCForge is installed in this same environment; then Refresh.")
88
+ engine_row.addWidget(self.current_python)
89
+ settings_layout.addLayout(engine_row)
90
+ compiler_row = QHBoxLayout()
91
+ self.compiler = QLineEdit(str(compiler or self.settings.value("compiler", "gcc")))
92
+ self.compiler.setAccessibleName("Native verification compiler")
93
+ self.compiler.setToolTip("External compiler used only when you request native verification. The engine supplies its supported test profile.")
94
+ compiler_row.addWidget(label("Verify with")); compiler_row.addWidget(self.compiler, 1)
95
+ self.compiler_browse = button("Browse", "open", self.browse_compiler)
96
+ compiler_row.addWidget(self.compiler_browse)
97
+ settings_layout.addLayout(compiler_row)
98
+ layout.addWidget(self.connection_settings); self.connection_settings.hide()
99
+ self.identity = label("No extension project open", muted=True); layout.addWidget(self.identity)
100
+ split = QSplitter(); layout.addWidget(split, 1)
101
+ self.navigation = QListWidget(); self.navigation.setObjectName("DocumentList")
102
+ self.navigation.setAccessibleName("SDK workspace")
103
+ self.navigation.addItems(["Scanner", "Edit extension", "Engine reference", "Activity"])
104
+ for index in range(self.navigation.count()):
105
+ self.navigation.item(index).setSizeHint(QSize(170, 44))
106
+ self.navigation.item(index).setIcon(load_pycforge_icon(("diagnostics", "module", "help", "history")[index]))
107
+ self.navigation.setIconSize(QSize(24, 24))
108
+ sidebar = QWidget()
109
+ sidebar.setMinimumWidth(220); sidebar.setMaximumWidth(250)
110
+ sidebar_layout = QVBoxLayout(sidebar)
111
+ sidebar_layout.setContentsMargins(0, 0, 0, 0)
112
+ sidebar_layout.addWidget(brand_panel(sidebar))
113
+ sidebar_layout.addWidget(self.navigation, 1)
114
+ self.pages = QStackedWidget()
115
+ self.scanner = ScannerPanel(); self.editor = ProjectEditor(); self.reference = ReferencePanel()
116
+ self.activity = QPlainTextEdit(); self.activity.setReadOnly(True)
117
+ self.activity.setMaximumBlockCount(4000); self.activity.setAccessibleName("SDK activity and diagnostics")
118
+ self.scanner_scroll = QScrollArea(); self.scanner_scroll.setWidgetResizable(True)
119
+ self.scanner_scroll.setFrameShape(QFrame.Shape.NoFrame)
120
+ self.scanner.setMinimumHeight(max(580, 48 * self.font().pointSize()))
121
+ self.scanner_scroll.setWidget(self.scanner)
122
+ for page in (self.scanner_scroll, self.editor, self.reference, self.activity): self.pages.addWidget(page)
123
+ split.addWidget(sidebar); split.addWidget(self.pages); split.setSizes([220, 1100])
124
+ self.navigation.currentRowChanged.connect(self.pages.setCurrentIndex); self.navigation.setCurrentRow(0)
125
+ self.editor.changed.connect(self.edited)
126
+ self.editor.source_requested.connect(self.edit_source)
127
+ self.scanner.field_requested.connect(self.open_finding)
128
+ self.engine.textChanged.connect(self.engine_changed)
129
+ self.compiler.textChanged.connect(self.compiler_changed)
130
+ self.setCentralWidget(central)
131
+ self.chrome = install_frameless_window_chrome(self, self.menuBar())
132
+ self.statusBar().showMessage("Find PyCForge, then choose or open an extension project.")
133
+ self.update_actions()
134
+
135
+ def _action(self, key, text, icon, callback, shortcut=None, tip=""):
136
+ action = QAction(load_pycforge_icon(icon), text, self)
137
+ action.setIconVisibleInMenu(True)
138
+ action.setObjectName("sdkAction_" + key); action.setToolTip(tip or text)
139
+ if shortcut: action.setShortcut(QKeySequence(shortcut))
140
+ action.triggered.connect(callback); self.actions[key] = action
141
+ return action
142
+
143
+ def _make_actions(self):
144
+ self.actions = {}
145
+ self._action("new", "New", "add-document", self.new_project, "Ctrl+N")
146
+ self._action("open", "Open", "open", self.open_project, "Ctrl+O")
147
+ self._action("save", "Save", "save-python", self.save_project, "Ctrl+S")
148
+ self._action("reload", "Reload from disk", "history", self.reload_project)
149
+ self._action("scan", "Scan", "diagnostics", self.scan, "F5", "Check current packaging readiness without running fixture programs.")
150
+ self._action("verify", "Verify fixtures", "check", self.verify, "F6", "Explicitly convert, compile and run the complete saved fixture suite.")
151
+ self._action("build", "Package", "save-c", self.build, "Ctrl+B", "Package only the exact project approved by current verification.")
152
+ self._action("cancel", "Cancel", "cancel", self.cancel)
153
+ self._action("document", "Add missing Help fields", "help", self.prepare_documentation)
154
+ self._action("register", "Register a package in PyCForge…", "module", self.register)
155
+ self._action("help", "SDK Help", "help", self.help, "F1")
156
+ self._action("exit", "Exit", "exit", self.close, "Ctrl+Q")
157
+ bar = QToolBar("Extension actions", self); bar.setMovable(False)
158
+ bar.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon); bar.setIconSize(QSize(24, 24))
159
+ for key in ("new", "open", "save", "scan", "verify", "build", "cancel"):
160
+ if key == "scan": bar.addSeparator()
161
+ bar.addAction(self.actions[key])
162
+ if key in ("scan", "verify", "build"):
163
+ bar.widgetForAction(self.actions[key]).setProperty("sdkAccent", True)
164
+ self.addToolBar(bar)
165
+
166
+ def _make_menus(self):
167
+ for title, keys in (("&File", ("new", "open", "save", "reload", "exit")),
168
+ ("&Extension", ("scan", "verify", "build", "cancel")),
169
+ ("&Tools", ("document", "register")), ("&Help", ("help",))):
170
+ menu = self.menuBar().addMenu(title)
171
+ menu.setObjectName("PyCForgeMenu")
172
+ menu.setStyle(MenuIconStyle(menu))
173
+ menu.setStyleSheet("QMenu::icon { width: 24px; height: 24px; } QMenu::item { min-height: 30px; }")
174
+ for key in keys: menu.addAction(self.actions[key])
175
+ self.menuBar().setNativeMenuBar(False)
176
+
177
+ def configuration(self):
178
+ return {"engine_python": self.engine.text().strip(), "cache": self.cache, "timeout": self.timeout}
179
+
180
+ def update_actions(self):
181
+ busy = self.runner.busy
182
+ project = self.snapshot is not None
183
+ saved = project and not self.dirty and not self.external_change
184
+ connected = self.connection == "connected" and self.execution_available
185
+ for key in ("open", "reload", "new", "register", "document", "save", "scan", "verify", "build"):
186
+ allowed = {"open": True, "reload": project, "new": bool(self.templates),
187
+ "register": connected, "document": saved and connected,
188
+ "save": project and self.dirty and not self.external_change,
189
+ "scan": saved, "verify": saved and connected,
190
+ "build": saved and connected and self.ready}[key]
191
+ self.actions[key].setEnabled(bool(allowed and not busy))
192
+ self.actions["cancel"].setEnabled(busy)
193
+ for widget in (self.engine, self.compiler, self.engine_browse, self.compiler_browse, self.connect_button,
194
+ self.current_python, self.locate_button, self.search_button):
195
+ widget.setEnabled(not busy)
196
+ self.editor.setEnabled(project and (not busy or self.runner.method in ("readiness", "verify")))
197
+ self.setWindowTitle((self.snapshot["document"]["declaration"].get("namespace", "Extension") +
198
+ (" •" if self.dirty else "") + " — " if project else "") + f"PyCForge SDK {__version__}")
199
+
200
+ def invalidate(self, message):
201
+ self.ready = False; self.revision += 1
202
+ self.scanner.invalidate(message); self.update_actions()
203
+
204
+ def edited(self):
205
+ self.dirty = True
206
+ self.invalidate("Unsaved edits. Save the project, then scan and verify its new contents.")
207
+
208
+ def engine_changed(self):
209
+ self.connection = "unavailable"
210
+ self.execution_available = False
211
+ self.templates = []; self.methods = []; self.operations = []
212
+ self.connection_label.setText("Engine selection changed. Connect to refresh the reference.")
213
+ self.connection_path.setText(self.engine.text())
214
+ self.connection_summary.setText("Refresh to check this installation and load its capabilities.")
215
+ self.locate_button.show()
216
+ self.reference.state.setText("Previous engine reference — connect to refresh. Execution approval cleared.")
217
+ self.invalidate("Engine selection changed. Connect, then scan the saved project.")
218
+
219
+ def compiler_changed(self):
220
+ self.invalidate("Compiler selection changed. Scan to check whether its identity matches verification.")
221
+
222
+ def browse_engine(self):
223
+ path = PathDialog.choose(self.startup_dialog or self, "Locate PyCForge", initial=self.engine.text(), mode="engine")
224
+ if path:
225
+ self.begin_startup(locations=[path])
226
+
227
+ def toggle_connection_settings(self):
228
+ self.connection_settings.setVisible(not self.connection_settings.isVisible())
229
+
230
+ def refresh_engine(self):
231
+ if self.connection == "connected" or self.engine.text().strip():
232
+ self.connect_engine()
233
+ else:
234
+ self.begin_startup()
235
+
236
+ def begin_startup(self, _checked=False, *, locations=None):
237
+ if self.runner.busy:
238
+ return
239
+ self.offline_pending = False
240
+ if self.startup_dialog is None:
241
+ self.startup_dialog = StartupDialog(self)
242
+ self.startup_dialog.offline_requested.connect(self.continue_offline)
243
+ self.startup_dialog.locate_requested.connect(self.browse_engine)
244
+ self.startup_dialog.retry_requested.connect(self.begin_startup)
245
+ self.startup_dialog.engine_selected.connect(self.select_discovered_engine)
246
+ self.startup_dialog.search_started(); self.startup_dialog.show()
247
+ self.ready = False
248
+ preferred = None
249
+ if locations is None:
250
+ if self.explicit_engine:
251
+ locations = [self.explicit_engine]
252
+ self.explicit_engine = None
253
+ else:
254
+ preferred = self.settings.value("last_engine", self.settings.value("engine", None))
255
+ self.start("discover", kwargs={"preferred": preferred, "locations": locations,
256
+ "timeout": min(self.timeout, 3), "search_timeout": min(self.timeout, 20)})
257
+
258
+ def select_discovered_engine(self, path):
259
+ if self.close_pending or self.offline_pending:
260
+ return
261
+ self.engine.setText(path)
262
+ if self.startup_dialog:
263
+ self.startup_dialog.connecting(path)
264
+ self.connect_engine()
265
+
266
+ def continue_offline(self):
267
+ self.offline_pending = True
268
+ if self.runner.busy:
269
+ self.runner.cancel()
270
+ if self.startup_dialog:
271
+ self.startup_dialog.status.setText("Stopping the connection search…")
272
+ self.startup_dialog.offline.setEnabled(False)
273
+ else:
274
+ self._load_offline()
275
+
276
+ def _load_offline(self):
277
+ self.offline_pending = False
278
+ self.catalogue_offline = True
279
+ self.start("catalogue", kwargs={"offline": True})
280
+
281
+ def _reveal_workspace(self):
282
+ self.show()
283
+ if self.startup_dialog:
284
+ dialog, self.startup_dialog = self.startup_dialog, None
285
+ dialog.accept(); dialog.deleteLater()
286
+
287
+ def browse_compiler(self):
288
+ path = PathDialog.choose(self, "Choose a native verification compiler", initial=self.compiler.text())
289
+ if path: self.compiler.setText(path)
290
+
291
+ def connect_engine(self):
292
+ self.ready = False
293
+ self.catalogue_offline = False
294
+ self.start("catalogue")
295
+
296
+ def start(self, method, args=(), kwargs=None):
297
+ if self.runner.busy: return False
298
+ if method in ("save_project", "prepare_documentation") and self.watcher.files():
299
+ self.watcher.removePaths(self.watcher.files())
300
+ if method in ("readiness", "verify", "build"):
301
+ self.ready = False; self.navigation.setCurrentRow(0)
302
+ self.scanner.running({"readiness": "Scanning extension…", "verify": "Verifying fixtures…", "build": "Checking and packaging…"}[method])
303
+ self.statusBar().showMessage("Working: " + method.replace("_", " "))
304
+ return self.runner.start(self.configuration(), method, args, kwargs, revision=self.revision)
305
+
306
+ def allow_discard(self):
307
+ if not self.dirty: return True
308
+ return Prompt.ask(self, "Unsaved project", "Discard your unsaved project edits and continue?",
309
+ [("discard", "Discard edits"), ("cancel", "Keep editing")]) == "discard"
310
+
311
+ def new_project(self):
312
+ if not self.allow_discard(): return
313
+ dlg = NewProjectDialog(self.templates, self)
314
+ if dlg.exec() == QDialog.DialogCode.Accepted:
315
+ self.start("create_project", (dlg.directory.text(),), {"author": dlg.author.text(), "template": dlg.template.currentData()})
316
+
317
+ def open_project(self, path=None):
318
+ if not self.allow_discard(): return
319
+ path = path if isinstance(path, str) else PathDialog.choose(self, "Open extension project", mode="directory",
320
+ description="Select the SDK extension folder containing project.json.")
321
+ if path: self.start("open_project", (path,))
322
+
323
+ def reload_project(self):
324
+ if self.snapshot and self.allow_discard(): self.start("open_project", (self.snapshot["directory"],))
325
+
326
+ def load_snapshot(self, data):
327
+ self.snapshot = data; self.dirty = self.external_change = False
328
+ self.editor.directory = data["directory"]
329
+ self.editor.expected_sha256 = data["project_sha256"]
330
+ self.editor.configuration = self.configuration()
331
+ self.editor.load(data["document"], methods=self.methods, operations=self.operations, workbench=self.workbench)
332
+ self.identity.setText(data["directory"])
333
+ self.rewatch()
334
+ self.invalidate("Saved project loaded. Run Scan to check what is ready for verification or packaging.")
335
+
336
+ def rewatch(self):
337
+ if self.watcher.files(): self.watcher.removePaths(self.watcher.files())
338
+ if self.snapshot:
339
+ root = Path(self.snapshot["directory"])
340
+ paths = [str(root/"project.json"), *[str(root/name) for name in self.snapshot["sources"]]]
341
+ paths.append(str(root/".pycforge-sdk/verification.json"))
342
+ self.watcher.addPaths([p for p in paths if Path(p).is_file()])
343
+
344
+ def disk_changed(self, path):
345
+ if path.endswith("/.pycforge-sdk/verification.json") or path.endswith("\\.pycforge-sdk\\verification.json"):
346
+ if not (self.runner.busy and self.runner.method == "verify"):
347
+ self.invalidate("Verification evidence changed. Run Scan to check it again.")
348
+ QTimer.singleShot(0, self.rewatch)
349
+ return
350
+ if self.runner.busy and self.runner.method in ("save_project", "save_source", "prepare_documentation"):
351
+ return
352
+ self.external_change = True
353
+ self.invalidate("A project file changed outside this editor. Reload from disk before scanning or saving.")
354
+ QTimer.singleShot(0, self.rewatch)
355
+
356
+ def save_project(self):
357
+ if self.snapshot:
358
+ self.start("save_project", (self.snapshot["directory"], copy.deepcopy(self.editor.draft)),
359
+ {"expected_sha256": self.snapshot["project_sha256"]})
360
+
361
+ def scan(self):
362
+ if self.snapshot and not self.dirty and not self.external_change:
363
+ self.start("readiness", (self.snapshot["directory"],), {"compiler": self.compiler.text().strip()})
364
+
365
+ def verify(self):
366
+ if self.snapshot and not self.dirty and not self.external_change:
367
+ self.start("verify", (self.snapshot["directory"],), {"compiler": self.compiler.text().strip()})
368
+
369
+ def build(self):
370
+ if not self.ready or not self.snapshot or self.dirty or self.external_change: return
371
+ namespace = self.snapshot["document"]["declaration"].get("namespace", "extension")
372
+ path = PathDialog.choose(self, "Save verified conversion package", mode="save",
373
+ suffix=".pcfplugin",
374
+ initial=str(Path(self.snapshot["directory"]).parent/(namespace+".pcfplugin")))
375
+ if not path: return
376
+ if Path(path).exists() and Prompt.ask(self, "Replace package", "Replace the existing package after the evidence checks pass?",
377
+ [("replace", "Replace"), ("cancel", "Cancel")]) != "replace": return
378
+ self.start("build", (self.snapshot["directory"], path), {"compiler": self.compiler.text().strip()})
379
+
380
+ def prepare_documentation(self):
381
+ if self.snapshot:
382
+ self.start("prepare_documentation", (self.snapshot["directory"],))
383
+
384
+ def edit_source(self, filename):
385
+ if self.dirty or self.external_change:
386
+ self.statusBar().showMessage("Save or reload the project before editing a fixture source."); return
387
+ if self.runner.busy: return
388
+ dlg = SourceDialog(self.snapshot, filename, self.configuration(), self)
389
+ # Its explicit one-file save owns the only mutation while the modal
390
+ # dialog is open; re-arm watchers after loading the resulting snapshot.
391
+ self.watcher.blockSignals(True)
392
+ try: dlg.exec()
393
+ finally: self.watcher.blockSignals(False)
394
+ if dlg.saved_snapshot: self.load_snapshot(dlg.saved_snapshot)
395
+ else:
396
+ # Re-read to detect external changes while the dialog was open.
397
+ self.start("open_project", (self.snapshot["directory"],))
398
+
399
+ def register(self):
400
+ path = PathDialog.choose(self, "Register a conversion package", suffix=".pcfplugin")
401
+ if not path: return
402
+ if Prompt.ask(self, "Register in PyCForge", "Add this package to the selected PyCForge installation's import registry? This is separate from packaging and does not publish a release.\n\n" + path,
403
+ [("register", "Register package"), ("cancel", "Cancel")]) == "register":
404
+ self.start("register", (path,))
405
+
406
+ def open_finding(self, path):
407
+ if self.editor.focus_path(path): self.navigation.setCurrentRow(1)
408
+ else: self.statusBar().showMessage("Field " + path + ": review the project data or add missing Help fields.")
409
+
410
+ def busy_changed(self, busy):
411
+ self.update_actions()
412
+
413
+ def progress(self, event):
414
+ if self.startup_dialog and event.get("phase") == "discovery":
415
+ self.startup_dialog.update_progress(event)
416
+ self.scanner.update_progress(event)
417
+ self.statusBar().showMessage(event.get("name", event.get("phase", "Working")))
418
+
419
+ def cancel(self):
420
+ self.runner.cancel(); self.ready = False
421
+ self.statusBar().showMessage("Canceling; waiting for the supervised operation to finish.")
422
+
423
+ def finished(self, method, result, revision):
424
+ self.activity.appendPlainText(method + " — " + result["status"])
425
+ if result["diagnostics"]: self.activity.appendPlainText(json.dumps(result["diagnostics"], indent=2))
426
+ data = result["data"]; success = result["status"] == "succeeded"
427
+ self.statusBar().showMessage(method.replace("_", " ").title() + ": " + result["status"])
428
+ if method in ("discover", "catalogue") and self.offline_pending and not self.close_pending:
429
+ self._load_offline()
430
+ return
431
+ if method == "discover":
432
+ self.activity.appendPlainText(json.dumps(data, indent=2))
433
+ if not self.close_pending:
434
+ engines = data.get("engines", [])
435
+ if len(engines) == 1:
436
+ self.select_discovered_engine(engines[0]["engine_python"])
437
+ elif engines and self.startup_dialog:
438
+ self.startup_dialog.show_choices(engines, limited=data.get("limited", False))
439
+ elif self.startup_dialog:
440
+ message = (result["diagnostics"][0]["message"] if result["diagnostics"] else
441
+ "No working installation was found in the checked locations.")
442
+ if data.get("limited"):
443
+ message += " The search reached its limit."
444
+ self.startup_dialog.failed(message)
445
+ elif method == "catalogue":
446
+ connection = data.get("connection", {})
447
+ self.connection = connection.get("state", "unavailable")
448
+ self.execution_available = connection.get("execution_available") is True
449
+ identity = connection.get("engine") or {}
450
+ self.connection_label.setText("PyCForge " + identity.get("engine_version", "") + " · " + self.connection)
451
+ self.connection_path.setText(self.engine.text() if self.connection == "connected" else "Cached reference only" if self.connection == "offline" else "No live engine connection")
452
+ if self.connection == "connected" and not self.execution_available:
453
+ self.connection_label.setText(self.connection_label.text() + " · authoring unavailable")
454
+ self.templates, self.methods, self.operations = data.get("templates", []), data.get("methods", []), data.get("array_operations", [])
455
+ self.workbench, self.help_documents = data.get("workbench"), data.get("documents", [])
456
+ self.help_engine = identity.get("engine_version", "")
457
+ imports = f"{len(self.workbench['imports'])} imports · " if self.workbench else ""
458
+ self.connection_summary.setText((imports + f"{len(self.templates)} starters · {len(self.methods)} API entries"
459
+ if self.connection == "connected" else "Local projects and cached reference remain available. Connect to use engine operations."))
460
+ self.locate_button.setVisible(self.connection != "connected")
461
+ if self.connection == "connected":
462
+ # Store only a validated live selection, immediately after success.
463
+ self.settings.setValue("last_engine", self.engine.text())
464
+ self.settings.setValue("engine", self.engine.text()); self.settings.sync()
465
+ self.reference.load(self.methods, self.connection, data.get("limits", []))
466
+ if self.snapshot:
467
+ self.editor.load(self.editor.draft, methods=self.methods, operations=self.operations, workbench=self.workbench)
468
+ self.editor.configuration = self.configuration()
469
+ self.ready = False; self.revision += 1
470
+ reveal = self.connection == "connected" or self.catalogue_offline
471
+ if self.startup_dialog and not self.close_pending:
472
+ if reveal:
473
+ self._reveal_workspace()
474
+ else:
475
+ self.startup_dialog.failed(connection.get("problem") or "The installation could not be connected.")
476
+ if self.after_connect and (reveal or self.startup_dialog is None) and not self.close_pending:
477
+ path = self.after_connect; self.after_connect = None
478
+ self.start("open_project", (path,))
479
+ elif method in ("open_project", "save_project") and success:
480
+ self.load_snapshot(data)
481
+ if method == "save_project" and self.close_after_save:
482
+ self.close_after_save = False; QTimer.singleShot(0, self.close)
483
+ elif method == "create_project" and success:
484
+ self.start("open_project", (data["directory"],))
485
+ elif method == "prepare_documentation" and success:
486
+ self.start("open_project", (self.snapshot["directory"],))
487
+ elif method in ("readiness", "verify", "build"):
488
+ returned_hash = data.get("readiness", data.get("report", data)).get("project_sha256")
489
+ if returned_hash and self.snapshot and returned_hash != self.snapshot["project_sha256"]:
490
+ self.external_change = True
491
+ if revision != self.revision or self.dirty or self.external_change:
492
+ self.ready = False
493
+ self.scanner.invalidate("Project or selection changed during work. This result cannot approve the current editor contents.")
494
+ elif method == "readiness" and "readiness" in data:
495
+ self.scanner.show_readiness(data["readiness"])
496
+ self.ready = success and data["readiness"].get("ready") is True
497
+ elif method == "verify":
498
+ if "report" in data: self.scanner.show_report(data["report"])
499
+ if success and not self.close_pending: self.scan()
500
+ else:
501
+ self.scanner.verdict.setText("Verification did not pass")
502
+ self.scanner.explanation.setText("Review the fixture results and Activity. Correct the problem, then verify again.")
503
+ self.scanner.progress.setRange(0, 1); self.scanner.progress.setValue(0)
504
+ elif method == "build" and success:
505
+ self.scanner.verdict.setText("Package created")
506
+ self.scanner.explanation.setText(data["package"] + "\nRegistration is a separate action in Tools.")
507
+ self.scanner.progress.setRange(0, 1); self.scanner.progress.setValue(1)
508
+ self.activity.appendPlainText(json.dumps(data, indent=2))
509
+ self.scanner.evidence.setPlainText(json.dumps(data, indent=2))
510
+ self.ready = True
511
+ else:
512
+ self.scanner.invalidate("Operation did not complete. Review Activity for the reason.")
513
+ elif method == "register":
514
+ self.activity.appendPlainText(json.dumps(data, indent=2)); self.navigation.setCurrentRow(3)
515
+ if not success and method == "save_project": self.close_after_save = False
516
+ if method in ("save_project", "prepare_documentation", "verify"): self.rewatch()
517
+ if not success and result["diagnostics"]:
518
+ self.statusBar().showMessage(result["diagnostics"][0]["message"])
519
+ self.update_actions()
520
+ if self.close_pending and not self.runner.busy:
521
+ self.close_pending = False; QTimer.singleShot(0, self.close)
522
+
523
+ def help(self):
524
+ dlg = Dialog("PyCForge SDK Help", self)
525
+ search = QLineEdit(); search.setPlaceholderText("Find in this guide…"); search.setAccessibleName("Find in SDK Help")
526
+ browser = SafeBrowser(); browser.setStyleSheet("QScrollBar:vertical { width: 28px; }")
527
+ documents = [{"title":"Getting started", "html":(Path(__file__).parent/"help/index.html").read_text(encoding="utf-8")}]
528
+ documents.extend(self.help_documents)
529
+ choice = QComboBox(); choice.setAccessibleName("SDK and import Help topics")
530
+ for document in documents:
531
+ choice.addItem(load_pycforge_icon("module" if document.get("id", "").startswith("import:") else "help"), document["title"])
532
+ choice.currentIndexChanged.connect(lambda index: browser.setHtml(documents[index]["html"]))
533
+ browser.setHtml(documents[0]["html"])
534
+ if self.help_documents: choice.setCurrentIndex(1)
535
+ dlg.body.addWidget(label("Help from PyCForge " + self.help_engine + " · " + self.connection if self.help_documents else
536
+ "Getting started — connect to PyCForge for its SDK guide and conversion-import Help.", muted=True))
537
+ dlg.body.addWidget(choice)
538
+ def find():
539
+ if not browser.find(search.text()):
540
+ cursor = browser.textCursor(); cursor.movePosition(cursor.MoveOperation.Start)
541
+ browser.setTextCursor(cursor); browser.find(search.text())
542
+ search.returnPressed.connect(find)
543
+ dlg.body.addWidget(search); dlg.body.addWidget(browser, 1)
544
+ dlg.body.addWidget(button("Close", "close", dlg.accept)); dlg.resize(900, 690); dlg.exec()
545
+
546
+ def closeEvent(self, event):
547
+ if self.runner.busy:
548
+ self.close_pending = True; self.cancel(); event.ignore(); return
549
+ if self.dirty:
550
+ choice = Prompt.ask(self, "Unsaved project", "Save your project edits before closing?",
551
+ [("save", "Save"), ("discard", "Discard edits"), ("cancel", "Keep editing")])
552
+ if choice == "cancel": event.ignore(); return
553
+ if choice == "save":
554
+ self.close_after_save = True; self.save_project(); event.ignore(); return
555
+ if self.startup_dialog:
556
+ dialog, self.startup_dialog = self.startup_dialog, None
557
+ dialog.accept(); dialog.deleteLater()
558
+ self.settings.setValue("compiler", self.compiler.text())
559
+ event.accept()
pycforge_sdk/native.py ADDED
@@ -0,0 +1,42 @@
1
+ """Generic fixture orchestration using the selected engine's build profile."""
2
+ from pathlib import Path
3
+ import platform
4
+ import shutil
5
+ from .process import run
6
+ from .project import digest, read
7
+ from .results import SDKError
8
+
9
+
10
+ def compiler_identity(compiler, profile, *, timeout, cancel):
11
+ if type(profile) is not dict or platform.system() not in profile.get("hosts", []):
12
+ raise SDKError("SDK_TARGET", "Native verification is not qualified for this host by the selected engine profile.")
13
+ for key in ("compile_args", "link_args", "compiler_version_args"):
14
+ if type(profile.get(key)) is not list or any(type(a) is not str or len(a) > 4096 for a in profile[key]):
15
+ raise SDKError("SDK_CAPABILITY", "Malformed native toolchain profile.")
16
+ executable = shutil.which(str(compiler))
17
+ if not executable:
18
+ raise SDKError("SDK_COMPILER", "C compiler not found: " + str(compiler), status="incomplete")
19
+ executable = str(Path(executable).resolve())
20
+ binary = read(executable, 128*1024*1024)
21
+ result = run([executable, *profile["compiler_version_args"]], timeout=timeout, cancel=cancel)
22
+ if result.returncode:
23
+ raise SDKError("SDK_COMPILER", "Cannot identify the selected C compiler.", status="incomplete")
24
+ return {"executable": executable, "sha256": digest(binary), "version": result.stdout.decode("utf-8", "replace"),
25
+ "system": platform.system(), "machine": platform.machine(), "profile": profile}
26
+
27
+
28
+ def execute_fixture(c_text, expected, compiler, work, *, timeout, cancel):
29
+ source, executable = Path(work)/"program.c", Path(work)/"program"
30
+ source.write_text(c_text, encoding="utf-8", newline="\n")
31
+ profile = compiler["profile"]
32
+ build = run([compiler["executable"], *profile["compile_args"], str(source), *profile["link_args"], "-o", str(executable)],
33
+ cwd=work, timeout=timeout, cancel=cancel)
34
+ result = {"compiler_exit_code": build.returncode, "compiler_stderr": build.stderr.decode("utf-8", "replace")}
35
+ if build.returncode or not executable.is_file():
36
+ return {**result, "passed": False, "reason": "Native compilation did not produce an executable."}
37
+ native = run([executable], cwd=work, timeout=timeout, cancel=cancel)
38
+ actual = {"stdout": native.stdout.decode("utf-8", "replace"), "stderr": native.stderr.decode("utf-8", "replace"), "exit_code": native.returncode}
39
+ passed = (native.stdout == expected["stdout"].encode("utf-8") and native.stderr == expected["stderr"].encode("utf-8")
40
+ and native.returncode == expected["exit_code"])
41
+ return {**result, "actual": actual, "passed": passed,
42
+ "reason": "Fixture matched." if passed else "Native result differs from the fixture expectation."}
@@ -0,0 +1,21 @@
1
+ """Report the engine-produced archive's identity without guessing its family."""
2
+ import io
3
+ import json
4
+ import zipfile
5
+ from .results import SDKError
6
+
7
+
8
+ def archive_format(content):
9
+ """Read inert metadata after engine admission; this is not package admission."""
10
+ try:
11
+ with zipfile.ZipFile(io.BytesIO(content)) as archive:
12
+ member=archive.getinfo('manifest.json')
13
+ if member.file_size > 65536:
14
+ raise ValueError('Manifest exceeds the reporting budget')
15
+ data=json.loads(archive.read(member))
16
+ value=data['format']
17
+ if type(value) is not str or not value.strip() or len(value)>200:
18
+ raise ValueError('Missing package format')
19
+ return value
20
+ except (ValueError,TypeError,KeyError,zipfile.BadZipFile,RuntimeError) as error:
21
+ raise SDKError('SDK_ENGINE_ERROR','Cannot read the engine-produced package identity: '+str(error)) from error