machineconfig 6.82__py3-none-any.whl → 8.51__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 (461) hide show
  1. machineconfig/cluster/remote/cloud_manager.py +1 -1
  2. machineconfig/cluster/remote/run_cluster.py +1 -1
  3. machineconfig/cluster/remote/run_remote.py +1 -1
  4. machineconfig/cluster/sessions_managers/utils/maker.py +29 -15
  5. machineconfig/cluster/sessions_managers/wt_local.py +17 -222
  6. machineconfig/cluster/sessions_managers/wt_local_manager.py +56 -194
  7. machineconfig/cluster/sessions_managers/wt_remote_manager.py +42 -198
  8. machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
  9. machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
  10. machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
  11. machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
  12. machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
  13. machineconfig/cluster/sessions_managers/zellij_local_manager.py +4 -2
  14. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +3 -2
  15. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +2 -2
  16. machineconfig/jobs/installer/checks/check_installations.py +133 -0
  17. machineconfig/jobs/installer/checks/install_utils.py +132 -0
  18. machineconfig/jobs/installer/checks/report_utils.py +39 -0
  19. machineconfig/jobs/installer/checks/vt_utils.py +89 -0
  20. machineconfig/jobs/installer/installer_data.json +1500 -310
  21. machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
  22. machineconfig/jobs/installer/linux_scripts/q.sh +10 -7
  23. machineconfig/jobs/installer/linux_scripts/redis.sh +1 -0
  24. machineconfig/jobs/installer/package_groups.py +62 -91
  25. machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
  26. machineconfig/jobs/installer/{custom → python_scripts}/boxes.py +2 -3
  27. machineconfig/jobs/installer/{custom_dev → python_scripts}/brave.py +5 -3
  28. machineconfig/jobs/installer/python_scripts/cloudflare_warp_cli.py +23 -0
  29. machineconfig/jobs/installer/{custom_dev → python_scripts}/code.py +14 -9
  30. machineconfig/jobs/installer/{custom_dev → python_scripts}/dubdb_adbc.py +1 -1
  31. machineconfig/jobs/installer/python_scripts/hx.py +214 -0
  32. machineconfig/jobs/installer/{custom_dev → python_scripts}/nerdfont.py +2 -2
  33. machineconfig/jobs/installer/{custom_dev → python_scripts}/nerfont_windows_helper.py +32 -26
  34. machineconfig/jobs/installer/python_scripts/sysabc.py +145 -0
  35. machineconfig/jobs/installer/{custom_dev → python_scripts}/wezterm.py +2 -19
  36. machineconfig/jobs/installer/{custom_dev → python_scripts}/winget.py +10 -14
  37. machineconfig/jobs/installer/python_scripts/yazi.py +139 -0
  38. machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nfs +0 -1
  39. machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
  40. machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +13 -0
  41. machineconfig/jobs/scripts/powershell_scripts/obs.ps1 +4 -0
  42. machineconfig/jobs/scripts_dynamic/a.py +428 -0
  43. machineconfig/logger.py +1 -2
  44. machineconfig/profile/create_helper.py +56 -18
  45. machineconfig/profile/create_links.py +79 -21
  46. machineconfig/profile/create_links_export.py +87 -36
  47. machineconfig/profile/create_shell_profile.py +92 -127
  48. machineconfig/profile/mapper_data.toml +45 -0
  49. machineconfig/profile/mapper_dotfiles.toml +249 -0
  50. machineconfig/scripts/__init__.py +0 -4
  51. machineconfig/scripts/linux/wrap_mcfg +46 -0
  52. machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
  53. machineconfig/scripts/python/agents.py +85 -165
  54. machineconfig/scripts/python/ai/initai.py +4 -2
  55. machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
  56. machineconfig/scripts/python/ai/scripts/command_runner.ps1 +33 -0
  57. machineconfig/scripts/python/ai/{command_runner → scripts}/command_runner.sh +1 -1
  58. machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
  59. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +8 -6
  60. machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
  61. machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
  62. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Thinking-Beast-Mode.chatmode.md → agents/Thinking-Beast-Mode.agent.md} +0 -1
  63. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md → agents/Ultimate-Transparent-Thinking-Beast-Mode.agent.md} +0 -1
  64. machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/deepResearch.chatmode.md → agents/deepResearch.agent.md} +2 -2
  65. machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +6 -6
  66. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +33 -0
  67. machineconfig/scripts/python/ai/solutions/copilot/instructions/python/watch_exec.prompt.md +20 -0
  68. machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
  69. machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
  70. machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
  71. machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
  72. machineconfig/scripts/python/ai/{generate_files.py → utils/generate_files.py} +2 -2
  73. machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
  74. machineconfig/scripts/python/ai/{vscode_tasks.py → utils/vscode_tasks.py} +13 -5
  75. machineconfig/scripts/python/cloud.py +58 -11
  76. machineconfig/scripts/python/croshell.py +10 -162
  77. machineconfig/scripts/python/devops.py +73 -36
  78. machineconfig/scripts/python/devops_navigator.py +16 -6
  79. machineconfig/scripts/python/fire_jobs.py +8 -222
  80. machineconfig/scripts/python/ftpx.py +7 -200
  81. machineconfig/scripts/python/graph/cli_graph.json +8743 -0
  82. machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
  83. machineconfig/scripts/python/helpers/helper_env/env_manager_tui.py +204 -0
  84. machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
  85. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +1 -1
  86. machineconfig/scripts/python/helpers/helpers_agents/agentic_frameworks/fire_crush.py +39 -0
  87. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +3 -4
  88. machineconfig/scripts/python/helpers/helpers_agents/agentic_frameworks/fire_gemini.py +55 -0
  89. machineconfig/scripts/python/helpers/helpers_agents/agentic_frameworks/fire_qwen.py +30 -0
  90. machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
  91. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_help_launch.py +38 -16
  92. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_helper_types.py +11 -14
  93. machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/aichat/config.yaml +5 -0
  94. machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/aider/.aider.conf.yml +2 -0
  95. machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/copilot/config.yml +1 -0
  96. machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/crush/crush.json +10 -0
  97. machineconfig/scripts/python/helpers/helpers_agents/privacy/configs/gemini/settings.json +12 -0
  98. machineconfig/scripts/python/helpers/helpers_agents/privacy/privacy.py +109 -0
  99. machineconfig/scripts/python/helpers/helpers_agents/templates/prompt.txt +10 -0
  100. machineconfig/scripts/python/helpers/helpers_agents/templates/template.sh +34 -0
  101. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +32 -25
  102. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +29 -22
  103. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +9 -8
  104. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
  105. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +3 -3
  106. machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
  107. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
  108. machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +12 -12
  109. machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
  110. machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +262 -0
  111. machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
  112. machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
  113. machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +67 -0
  114. machineconfig/scripts/python/helpers/helpers_devops/cli_nw.py +201 -0
  115. machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
  116. machineconfig/scripts/python/helpers/helpers_devops/cli_self.py +197 -0
  117. machineconfig/scripts/python/helpers/helpers_devops/cli_share_file.py +151 -0
  118. machineconfig/scripts/python/helpers/helpers_devops/cli_share_server.py +125 -0
  119. machineconfig/scripts/python/{helpers_devops/cli_terminal.py → helpers/helpers_devops/cli_share_terminal.py} +26 -22
  120. machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
  121. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +17 -23
  122. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
  123. machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +78 -71
  124. machineconfig/scripts/python/helpers/helpers_devops/run_script.py +197 -0
  125. machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
  126. machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
  127. machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +4 -4
  128. machineconfig/scripts/python/{helpers_fire/helpers4.py → helpers/helpers_fire_command/file_wrangler.py} +57 -20
  129. machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +1 -0
  130. machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
  131. machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +26 -16
  132. machineconfig/scripts/python/helpers/helpers_msearch/__init__.py +5 -0
  133. machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
  134. machineconfig/scripts/{linux → python/helpers/helpers_msearch/scripts_linux}/fzfg +6 -5
  135. machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
  136. machineconfig/scripts/python/helpers/helpers_msearch/scripts_windows/fzfg.ps1 +59 -0
  137. machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
  138. machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
  139. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
  140. machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
  141. machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
  142. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
  143. machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
  144. machineconfig/scripts/python/helpers/helpers_network/address.py +174 -0
  145. machineconfig/scripts/python/helpers/helpers_network/address_switch.py +78 -0
  146. machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
  147. machineconfig/scripts/python/{nw → helpers/helpers_network}/mount_nfs.py +2 -2
  148. machineconfig/scripts/python/{nw → helpers/helpers_network}/mount_ssh.py +3 -3
  149. machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
  150. machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
  151. machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
  152. machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
  153. machineconfig/scripts/python/{nw → helpers/helpers_network}/wifi_conn.py +1 -53
  154. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
  155. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
  156. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -1
  157. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +159 -48
  158. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +4 -3
  159. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -12
  160. machineconfig/scripts/python/helpers/helpers_repos/repo_analyzer_1.py +160 -0
  161. machineconfig/scripts/python/{helpers_repos/count_lines.py → helpers/helpers_repos/repo_analyzer_2.py} +156 -191
  162. machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
  163. machineconfig/scripts/python/helpers/helpers_search/ast_search.py +74 -0
  164. machineconfig/scripts/python/helpers/helpers_search/qr_code.py +166 -0
  165. machineconfig/scripts/python/helpers/helpers_search/repo_rag.py +325 -0
  166. machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
  167. machineconfig/scripts/python/helpers/helpers_search/symantic_search.py +25 -0
  168. machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
  169. machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +186 -0
  170. machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +20 -14
  171. machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
  172. machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
  173. machineconfig/scripts/python/helpers/helpers_utils/download.py +150 -0
  174. machineconfig/scripts/python/helpers/helpers_utils/pdf.py +96 -0
  175. machineconfig/scripts/python/helpers/helpers_utils/python.py +210 -0
  176. machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
  177. machineconfig/scripts/python/mcfg_entry.py +143 -0
  178. machineconfig/scripts/python/msearch.py +26 -0
  179. machineconfig/scripts/python/sessions.py +69 -135
  180. machineconfig/scripts/python/terminal.py +58 -0
  181. machineconfig/scripts/python/utils.py +115 -38
  182. machineconfig/scripts/windows/wrap_mcfg.ps1 +63 -0
  183. machineconfig/settings/atuin/config.toml +294 -0
  184. machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
  185. machineconfig/settings/broot/conf.toml +1 -1
  186. machineconfig/settings/helix/config.toml +16 -0
  187. machineconfig/settings/helix/languages.toml +13 -4
  188. machineconfig/settings/helix/yazi-picker.sh +12 -0
  189. machineconfig/settings/lf/linux/exe/lfcd.sh +1 -0
  190. machineconfig/settings/lf/linux/exe/previewer.sh +3 -2
  191. machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
  192. machineconfig/settings/lf/windows/lfrc +14 -16
  193. machineconfig/settings/linters/.ruff.toml +2 -1
  194. machineconfig/settings/marimo/marimo.toml +1 -1
  195. machineconfig/settings/marimo/snippets/globalize.py +34 -0
  196. machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
  197. machineconfig/settings/shells/bash/init.sh +47 -12
  198. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +1 -1
  199. machineconfig/settings/shells/nushell/config.nu +25 -33
  200. machineconfig/settings/shells/nushell/env.nu +21 -8
  201. machineconfig/settings/shells/nushell/init.nu +138 -0
  202. machineconfig/settings/shells/pwsh/init.ps1 +111 -17
  203. machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
  204. machineconfig/settings/shells/starship/starship.toml +16 -0
  205. machineconfig/settings/shells/wezterm/wezterm.lua +6 -1
  206. machineconfig/settings/shells/wt/settings.json +27 -18
  207. machineconfig/settings/shells/zsh/init.sh +42 -23
  208. machineconfig/settings/television/cable_unix/alias.toml +8 -0
  209. machineconfig/settings/television/cable_unix/aws-buckets.toml +14 -0
  210. machineconfig/settings/television/cable_unix/aws-instances.toml +13 -0
  211. machineconfig/settings/television/cable_unix/bash-history.toml +8 -0
  212. machineconfig/settings/television/cable_unix/channels.toml +19 -0
  213. machineconfig/settings/television/cable_unix/dirs.toml +13 -0
  214. machineconfig/settings/television/cable_unix/distrobox-list.toml +42 -0
  215. machineconfig/settings/television/cable_unix/docker-images.toml +13 -0
  216. machineconfig/settings/television/cable_unix/dotfiles.toml +11 -0
  217. machineconfig/settings/television/cable_unix/env.toml +17 -0
  218. machineconfig/settings/television/cable_unix/files.toml +11 -0
  219. machineconfig/settings/television/cable_unix/fish-history.toml +8 -0
  220. machineconfig/settings/television/cable_unix/git-branch.toml +11 -0
  221. machineconfig/settings/television/cable_unix/git-diff.toml +10 -0
  222. machineconfig/settings/television/cable_unix/git-log.toml +12 -0
  223. machineconfig/settings/television/cable_unix/git-reflog.toml +12 -0
  224. machineconfig/settings/television/cable_unix/git-repos.toml +16 -0
  225. machineconfig/settings/television/cable_unix/guix.toml +20 -0
  226. machineconfig/settings/television/cable_unix/just-recipes.toml +18 -0
  227. machineconfig/settings/television/cable_unix/k8s-deployments.toml +36 -0
  228. machineconfig/settings/television/cable_unix/k8s-pods.toml +50 -0
  229. machineconfig/settings/television/cable_unix/k8s-services.toml +36 -0
  230. machineconfig/settings/television/cable_unix/man-pages.toml +24 -0
  231. machineconfig/settings/television/cable_unix/nu-history.toml +7 -0
  232. machineconfig/settings/television/cable_unix/procs.toml +20 -0
  233. machineconfig/settings/television/cable_unix/text.toml +17 -0
  234. machineconfig/settings/television/cable_unix/tldr.toml +18 -0
  235. machineconfig/settings/television/cable_unix/zsh-history.toml +9 -0
  236. machineconfig/settings/television/cable_windows/alias.toml +7 -0
  237. machineconfig/settings/television/cable_windows/dirs.toml +13 -0
  238. machineconfig/settings/television/cable_windows/docker-images.toml +13 -0
  239. machineconfig/settings/television/cable_windows/dotfiles.toml +11 -0
  240. machineconfig/settings/television/cable_windows/env.toml +17 -0
  241. machineconfig/settings/television/cable_windows/files.toml +14 -0
  242. machineconfig/settings/television/cable_windows/git-branch.toml +11 -0
  243. machineconfig/settings/television/cable_windows/git-diff.toml +10 -0
  244. machineconfig/settings/television/cable_windows/git-log.toml +11 -0
  245. machineconfig/settings/television/cable_windows/git-reflog.toml +11 -0
  246. machineconfig/settings/television/cable_windows/git-repos.toml +15 -0
  247. machineconfig/settings/television/cable_windows/nu-history.toml +7 -0
  248. machineconfig/settings/television/cable_windows/pwsh-history.toml +6 -0
  249. machineconfig/settings/television/cable_windows/text.toml +17 -0
  250. machineconfig/settings/tv/config.toml +234 -0
  251. machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
  252. machineconfig/settings/wsl/.wslconfig +5 -30
  253. machineconfig/settings/wt/__init__.py +0 -0
  254. machineconfig/settings/yazi/init.lua +61 -0
  255. machineconfig/settings/yazi/keymap_linux.toml +94 -0
  256. machineconfig/settings/yazi/keymap_windows.toml +78 -0
  257. machineconfig/settings/yazi/shell/yazi_cd.ps1 +33 -0
  258. machineconfig/settings/yazi/shell/yazi_cd.sh +8 -0
  259. machineconfig/settings/yazi/theme.toml +4 -0
  260. machineconfig/settings/yazi/yazi_linux.toml +94 -0
  261. machineconfig/settings/yazi/yazi_windows.toml +58 -0
  262. machineconfig/settings/zellij/layouts/st.kdl +40 -9
  263. machineconfig/settings/zellij/layouts/st2.kdl +1 -1
  264. machineconfig/setup_linux/__init__.py +2 -2
  265. machineconfig/setup_linux/apps_desktop.sh +8 -27
  266. machineconfig/setup_linux/web_shortcuts/interactive.sh +27 -12
  267. machineconfig/setup_linux/web_shortcuts/live_from_github.sh +34 -0
  268. machineconfig/setup_mac/__init__.py +1 -4
  269. machineconfig/setup_mac/apps_gui.sh +248 -0
  270. machineconfig/setup_windows/__init__.py +2 -5
  271. machineconfig/setup_windows/uv.ps1 +8 -1
  272. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +28 -12
  273. machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +31 -0
  274. machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +17 -0
  275. machineconfig/type_hinting/sql/__init__.py +1 -0
  276. machineconfig/type_hinting/sql/base.py +216 -0
  277. machineconfig/type_hinting/sql/core_schema.py +64 -0
  278. machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
  279. machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
  280. machineconfig/type_hinting/typedict/__init__.py +1 -0
  281. machineconfig/type_hinting/typedict/ast_utils.py +130 -0
  282. machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
  283. machineconfig/type_hinting/typedict/generators.py +231 -0
  284. machineconfig/type_hinting/typedict/polars_schema.py +24 -0
  285. machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
  286. machineconfig/utils/accessories.py +31 -4
  287. machineconfig/utils/code.py +163 -51
  288. machineconfig/utils/files/ascii_art.py +11 -15
  289. machineconfig/utils/files/headers.py +6 -7
  290. machineconfig/utils/files/read.py +8 -1
  291. machineconfig/utils/installer_utils/github_release_bulk.py +95 -138
  292. machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
  293. machineconfig/utils/installer_utils/install_from_url.py +183 -0
  294. machineconfig/utils/installer_utils/installer_class.py +53 -102
  295. machineconfig/utils/installer_utils/installer_cli.py +161 -0
  296. machineconfig/utils/installer_utils/installer_helper.py +129 -0
  297. machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +42 -91
  298. machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +20 -65
  299. machineconfig/utils/io.py +94 -9
  300. machineconfig/utils/links.py +56 -38
  301. machineconfig/utils/meta.py +38 -21
  302. machineconfig/utils/options.py +81 -23
  303. machineconfig/utils/options_utils/__init__.py +0 -0
  304. machineconfig/utils/options_utils/options_tv_linux.py +211 -0
  305. machineconfig/utils/options_utils/options_tv_windows.py +88 -0
  306. machineconfig/utils/options_utils/tv_options.py +37 -0
  307. machineconfig/utils/path_extended.py +52 -102
  308. machineconfig/utils/path_helper.py +76 -23
  309. machineconfig/utils/procs.py +1 -1
  310. machineconfig/utils/scheduler.py +26 -53
  311. machineconfig/utils/scheduling.py +0 -2
  312. machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
  313. machineconfig/utils/schemas/layouts/layout_types.py +1 -1
  314. machineconfig/utils/source_of_truth.py +6 -1
  315. machineconfig/utils/ssh.py +216 -419
  316. machineconfig/utils/ssh_utils/abc.py +5 -0
  317. machineconfig/utils/ssh_utils/copy_from_here.py +116 -0
  318. machineconfig/utils/ssh_utils/copy_to_here.py +303 -0
  319. machineconfig/utils/ssh_utils/utils.py +158 -0
  320. machineconfig/utils/ssh_utils/wsl.py +147 -0
  321. machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
  322. machineconfig/utils/terminal.py +1 -0
  323. machineconfig/utils/upgrade_packages.py +107 -35
  324. machineconfig/utils/ve.py +12 -4
  325. machineconfig-8.51.dist-info/METADATA +140 -0
  326. machineconfig-8.51.dist-info/RECORD +543 -0
  327. {machineconfig-6.82.dist-info → machineconfig-8.51.dist-info}/entry_points.txt +4 -1
  328. machineconfig/jobs/installer/check_installations.py +0 -248
  329. machineconfig/jobs/installer/custom/hx.py +0 -140
  330. machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
  331. machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -71
  332. machineconfig/jobs/installer/powershell_scripts/archive_pygraphviz.ps1 +0 -12
  333. machineconfig/jobs/installer/powershell_scripts/openssh-server_add_key.ps1 +0 -7
  334. machineconfig/jobs/installer/powershell_scripts/openssh-server_copy-ssh-id.ps1 +0 -14
  335. machineconfig/profile/backup.toml +0 -49
  336. machineconfig/profile/mapper.toml +0 -256
  337. machineconfig/scripts/linux/fzf2g +0 -21
  338. machineconfig/scripts/linux/fzfag +0 -17
  339. machineconfig/scripts/linux/fzffg +0 -25
  340. machineconfig/scripts/linux/fzfrga +0 -21
  341. machineconfig/scripts/linux/mcfgs +0 -38
  342. machineconfig/scripts/linux/other/share_smb +0 -1
  343. machineconfig/scripts/linux/other/switch_ip +0 -20
  344. machineconfig/scripts/linux/skrg +0 -4
  345. machineconfig/scripts/linux/warp-cli.sh +0 -122
  346. machineconfig/scripts/linux/z_ls +0 -104
  347. machineconfig/scripts/python/ai/command_runner/prompt.txt +0 -9
  348. machineconfig/scripts/python/helpers_devops/cli_config.py +0 -120
  349. machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -77
  350. machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
  351. machineconfig/scripts/python/helpers_devops/cli_nw.py +0 -73
  352. machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -181
  353. machineconfig/scripts/python/helpers_devops/cli_self.py +0 -122
  354. machineconfig/scripts/python/helpers_devops/cli_share_server.py +0 -104
  355. machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -221
  356. machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
  357. machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
  358. machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_crush.py +0 -37
  359. machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_gemini.py +0 -44
  360. machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_qwen.py +0 -43
  361. machineconfig/scripts/python/helpers_fire/prompt.txt +0 -2
  362. machineconfig/scripts/python/helpers_fire/template.sh +0 -15
  363. machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
  364. machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
  365. machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -588
  366. machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +0 -17
  367. machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -76
  368. machineconfig/scripts/python/helpers_repos/secure_repo.py +0 -15
  369. machineconfig/scripts/python/mcfg.py +0 -48
  370. machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
  371. machineconfig/scripts/python/nw/devops_add_identity.py +0 -82
  372. machineconfig/scripts/python/nw/devops_add_ssh_key.py +0 -134
  373. machineconfig/scripts/python/nw/ssh_debug_linux.py +0 -391
  374. machineconfig/scripts/python/nw/ssh_debug_windows.py +0 -338
  375. machineconfig/scripts/python/nw/wsl_windows_transfer.py +0 -66
  376. machineconfig/scripts/windows/fzfb.ps1 +0 -3
  377. machineconfig/scripts/windows/fzfg.ps1 +0 -2
  378. machineconfig/scripts/windows/fzfrga.bat +0 -20
  379. machineconfig/scripts/windows/mcfgs.ps1 +0 -17
  380. machineconfig/scripts/windows/mounts/mount_ssh.ps1 +0 -13
  381. machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
  382. machineconfig/settings/lf/windows/fzf_edit.ps1 +0 -6
  383. machineconfig/settings/lf/windows/tst.ps1 +0 -1
  384. machineconfig/settings/yazi/yazi.toml +0 -4
  385. machineconfig/setup_linux/apps.sh +0 -66
  386. machineconfig/setup_linux/others/cli_installation.sh +0 -137
  387. machineconfig/setup_linux/others/mint_keyboard_shortcuts.sh +0 -30
  388. machineconfig/setup_linux/ssh/openssh_all.sh +0 -25
  389. machineconfig/setup_linux/ssh/openssh_wsl.sh +0 -38
  390. machineconfig/setup_mac/apps.sh +0 -73
  391. machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
  392. machineconfig/setup_windows/apps.ps1 +0 -62
  393. machineconfig/setup_windows/others/obs.ps1 +0 -4
  394. machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
  395. machineconfig/setup_windows/ssh/add_identity.ps1 +0 -11
  396. machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
  397. machineconfig/utils/installer_utils/installer.py +0 -225
  398. machineconfig/utils/tst.py +0 -20
  399. machineconfig-6.82.dist-info/METADATA +0 -82
  400. machineconfig-6.82.dist-info/RECORD +0 -441
  401. machineconfig/jobs/installer/{custom_dev → checks}/__init__.py +0 -0
  402. machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
  403. machineconfig/{scripts/python/helpers_cloud → jobs/installer/python_scripts}/__init__.py +0 -0
  404. machineconfig/jobs/installer/{custom_dev → python_scripts}/alacritty.py +0 -0
  405. machineconfig/jobs/installer/{custom_dev → python_scripts}/bypass_paywall.py +0 -0
  406. machineconfig/jobs/installer/{custom_dev → python_scripts}/cursor.py +0 -0
  407. machineconfig/jobs/installer/{custom_dev → python_scripts}/espanso.py +0 -0
  408. machineconfig/jobs/installer/{custom → python_scripts}/gh.py +0 -0
  409. machineconfig/jobs/installer/{custom_dev → python_scripts}/goes.py +0 -0
  410. machineconfig/jobs/installer/{custom_dev → python_scripts}/lvim.py +0 -0
  411. machineconfig/jobs/installer/{custom_dev → python_scripts}/redis.py +0 -0
  412. machineconfig/{setup_linux/others → jobs/scripts/bash_scripts}/android.sh +0 -0
  413. machineconfig/jobs/{installer/linux_scripts → scripts/bash_scripts}/lid.sh +0 -0
  414. machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_drive +0 -0
  415. machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nw_drive +0 -0
  416. machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_smb +0 -0
  417. machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_cloud.sh +0 -0
  418. machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_nfs +0 -0
  419. machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/start_docker +0 -0
  420. machineconfig/{scripts → jobs/scripts/powershell_scripts}/Restore-ThunderbirdProfile.ps1 +0 -0
  421. machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/docker.ps1 +0 -0
  422. machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nfs.ps1 +0 -0
  423. machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nw.ps1 +0 -0
  424. machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_smb.ps1 +0 -0
  425. machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/power_options.ps1 +0 -0
  426. machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_cloud.cmd +0 -0
  427. machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_smb.ps1 +0 -0
  428. machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/unlock_bitlocker.ps1 +0 -0
  429. machineconfig/scripts/python/{helpers_croshell → ai/utils}/__init__.py +0 -0
  430. machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
  431. machineconfig/scripts/python/{helpers_devops → graph}/__init__.py +0 -0
  432. machineconfig/scripts/python/{helpers_devops/themes → helpers}/__init__.py +0 -0
  433. machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
  434. machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
  435. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/__init__.py +0 -0
  436. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/agentic_frameworks/__init__.py +0 -0
  437. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
  438. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
  439. machineconfig/scripts/python/{helpers_fire → helpers/helpers_agents/templates}/template.ps1 +0 -0
  440. machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_cloud}/__init__.py +0 -0
  441. machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +1 -1
  442. /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
  443. /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_croshell}/__init__.py +0 -0
  444. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
  445. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
  446. /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
  447. /machineconfig/scripts/python/{nw → helpers/helpers_devops}/__init__.py +0 -0
  448. /machineconfig/{setup_windows/wt_and_pwsh → scripts/python/helpers/helpers_devops/themes}/__init__.py +0 -0
  449. /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
  450. /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
  451. /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
  452. /machineconfig/scripts/python/{helpers_fire_command/fire_jobs_streamlit_helper.py → helpers/helpers_fire_command/f.py} +0 -0
  453. /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers/helpers_fire_command/fire_jobs_streamlit_helper.py} +0 -0
  454. /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
  455. /machineconfig/{settings/yazi/keymap.toml → scripts/python/helpers/helpers_network/__init__.py} +0 -0
  456. /machineconfig/scripts/python/{nw → helpers/helpers_network}/mount_nw_drive.py +0 -0
  457. /machineconfig/scripts/python/{nw → helpers/helpers_network}/onetimeshare.py +0 -0
  458. /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
  459. /machineconfig/{setup_windows/wt_and_pwsh → settings/wt}/set_wt_settings.py +0 -0
  460. {machineconfig-6.82.dist-info → machineconfig-8.51.dist-info}/WHEEL +0 -0
  461. {machineconfig-6.82.dist-info → machineconfig-8.51.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
 
7
7
  def get_import_module_string(py_file: str) -> str:
8
- from machineconfig.scripts.python.helpers_fire.helpers4 import get_import_module_code
8
+ from machineconfig.scripts.python.helpers.helpers_fire_command.file_wrangler import get_import_module_code
9
9
  from machineconfig.utils.accessories import get_repo_root
10
10
  from pathlib import Path
11
11
  repo_root = get_repo_root(Path(py_file))
@@ -29,7 +29,8 @@ except (ImportError, ModuleNotFoundError) as ex:
29
29
  return txt
30
30
 
31
31
 
32
- def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_module: bool) -> str:
32
+ def lambda_to_python_script(lmb: Callable[[], Any],
33
+ in_global: bool, import_module: bool) -> str:
33
34
  """
34
35
  caveats: always use keyword arguments in the lambda call for best results.
35
36
  return statement not allowed in the wrapped function (otherwise it can be put in the global space)
@@ -55,9 +56,19 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
55
56
  import types as _types
56
57
  from pathlib import Path as _Path
57
58
 
59
+ def _stringify_annotation(annotation: Any) -> Any:
60
+ if annotation is _inspect.Signature.empty or annotation is _inspect.Parameter.empty:
61
+ return annotation
62
+ if isinstance(annotation, str):
63
+ return annotation
64
+ try:
65
+ return _inspect.formatannotation(annotation)
66
+ except Exception:
67
+ return str(annotation)
68
+
58
69
  # sanity checks
59
70
  if not (callable(lmb) and isinstance(lmb, _types.LambdaType)):
60
- raise TypeError("Expected a lambda function object")
71
+ raise TypeError(f"Expected a lambda function object, got {type(lmb)}")
61
72
 
62
73
  src = _inspect.getsource(lmb)
63
74
  src = _textwrap.dedent(src)
@@ -70,7 +81,7 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
70
81
  lambda_node = n
71
82
  break
72
83
  if lambda_node is None:
73
- raise ValueError("Could not find a lambda expression in source")
84
+ raise ValueError(f"""Could not find a lambda expression in source, got:\n{lmb}\nwhich is not a lambda, its a {src}""")
74
85
 
75
86
  body = lambda_node.body
76
87
  if not isinstance(body, _ast.Call):
@@ -102,7 +113,7 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
102
113
  if not callable(func_obj):
103
114
  raise TypeError("Resolved object is not callable")
104
115
 
105
- func_name = getattr(func_obj, "__name__", "<unknown>")
116
+ func_name = getattr(getattr(func_obj, "__code__", None), "co_name", None) or getattr(func_obj, "__name__", "<unknown>")
106
117
 
107
118
  import_prefix: str = ""
108
119
  if import_module:
@@ -174,16 +185,18 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
174
185
  else:
175
186
  new_default = param.default
176
187
 
177
- # Recreate the Parameter (keeping annotation and kind)
188
+ normalized_annotation = _stringify_annotation(param.annotation)
189
+
178
190
  if new_default is _inspect.Parameter.empty:
179
- new_param = _inspect.Parameter(name, param.kind, annotation=param.annotation)
191
+ new_param = _inspect.Parameter(name, param.kind, annotation=normalized_annotation)
180
192
  else:
181
193
  new_param = _inspect.Parameter(
182
- name, param.kind, default=new_default, annotation=param.annotation
194
+ name, param.kind, default=new_default, annotation=normalized_annotation
183
195
  )
184
196
  new_params.append(new_param)
185
197
 
186
- new_sig = _inspect.Signature(parameters=new_params, return_annotation=sig.return_annotation)
198
+ return_annotation = _stringify_annotation(sig.return_annotation)
199
+ new_sig = _inspect.Signature(parameters=new_params, return_annotation=return_annotation)
187
200
 
188
201
  # If in_global mode, return kwargs as global assignments + dedented body
189
202
  if in_global:
@@ -200,15 +213,11 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
200
213
 
201
214
  # Build type annotation string if available
202
215
  if param.annotation is not _inspect.Parameter.empty:
203
- # Try to get a nice string representation of the annotation
204
- try:
205
- if hasattr(param.annotation, "__name__"):
206
- type_str = param.annotation.__name__
207
- else:
208
- type_str = str(param.annotation)
209
- except Exception:
210
- type_str = str(param.annotation)
211
- global_assignments.append(f"{name}: {type_str} = {repr(value)}")
216
+ annotation_literal = _stringify_annotation(param.annotation)
217
+ if isinstance(annotation_literal, str):
218
+ global_assignments.append(f"{name}: {repr(annotation_literal)} = {repr(value)}")
219
+ else:
220
+ global_assignments.append(f"{name} = {repr(value)}")
212
221
  else:
213
222
  global_assignments.append(f"{name} = {repr(value)}")
214
223
 
@@ -230,12 +239,20 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
230
239
  if lines[-1].startswith("return "):
231
240
  lines[-1] = lines[-1].replace("return ", "# return ", 1)
232
241
  result_text = "\n".join(lines)
233
- if "Optional[" in result_text or "Any" in result_text or "Union[" in result_text or "Literal[" in result_text:
234
- result_text = "from typing import *\n\n" + result_text
235
242
 
243
+ if "Optional" in result_text or "Any" in result_text or "Union" in result_text or "Literal" in result_text:
244
+ result_text = "from typing import Optional, Any, Union, Literal\n\n" + result_text
236
245
  if import_prefix:
237
246
  result_text = f"{import_prefix}{result_text}"
238
247
  return result_text
239
248
 
240
249
  if __name__ == "__main__":
241
- pass
250
+ from machineconfig.utils.code import print_code
251
+ import_code_robust = "<import_code_robust>"
252
+ print_code.__name__ = "blah"
253
+ res = lambda_to_python_script(
254
+ lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
255
+ in_global=True, import_module=False
256
+ )
257
+
258
+ print(res)
@@ -1,40 +1,99 @@
1
+
1
2
  from pathlib import Path
2
- from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
3
3
  from rich.text import Text
4
4
  from rich.panel import Panel
5
5
  from rich.console import Console
6
6
  import subprocess
7
- from typing import Optional, Union, Iterable, overload, Literal
7
+ from typing import Optional, Union, Iterable, overload, Literal, cast
8
8
 
9
9
  @overload
10
- def choose_from_options[T](msg: str, options: Iterable[T], multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False) -> T: ...
10
+ def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, preview: Optional[Literal["bat"]]=None) -> T: ...
11
11
  @overload
12
- def choose_from_options[T](msg: str, options: Iterable[T], multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False, ) -> list[T]: ...
13
- def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, fzf: bool = False, ) -> Union[T, list[T]]:
12
+ def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, preview: Optional[Literal["bat"]]=None) -> list[T]: ...
13
+ def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, preview: Optional[Literal["bat"]]=None, ) -> Union[T, list[T]]:
14
14
  # TODO: replace with https://github.com/tmbo/questionary
15
15
  # # also see https://github.com/charmbracelet/gum
16
16
  options_strings: list[str] = [str(x) for x in options]
17
17
  default_string = str(default) if default is not None else None
18
18
  console = Console()
19
- fzf_exists = check_tool_exists("fzf")
20
- # print("\n" * 10, f"{fzf=}, {fzf_exists=}", "\n" * 10)
21
- if fzf and fzf_exists:
22
- from pyfzf.pyfzf import FzfPrompt
23
- fzf_prompt = FzfPrompt()
24
- nl = "\n"
25
- choice_string_multi: list[str] = fzf_prompt.prompt(choices=options_strings, fzf_options=("--multi" if multi else "") + f' --prompt "{prompt.replace(nl, " ")}" ') # --border-label={msg.replace(nl, ' ')}")
26
- # --border=rounded doens't work on older versions of fzf installed at Ubuntu 20.04
19
+ from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
20
+ # from machineconfig.utils.installer_utils.installer_cli import check_tool_exists
21
+ # print("ch1")
22
+ if tv and check_tool_exists("tv"):
23
+ from machineconfig.utils.accessories import randstr
24
+ options_txt_path = Path.home().joinpath("tmp_results/tmp_files/choices_" + randstr(6) + ".txt")
25
+ options_txt_path.parent.mkdir(parents=True, exist_ok=True)
26
+ options_txt_path.write_text("\n".join(options_strings), encoding="utf-8")
27
+
28
+ # Run `tv` interactively so the user can make selections. We redirect tv's
29
+ # stdout to a temporary output file so we can read the chosen lines after
30
+ # the interactive session completes. Do not capture_output or redirect
31
+ # stdin/stderr here so `tv` stays attached to the terminal.
32
+ tv_out_path = options_txt_path.with_name(options_txt_path.stem + "_out.txt")
33
+ if preview is None:
34
+ preview_line = ""
35
+ elif preview == "bat":
36
+ preview_line = r"""--preview-command "bat -n --color=always {}" --preview-size 70 """
37
+
38
+ import platform
39
+ if platform.system() == "Windows":
40
+ # PowerShell + TUI apps can be finicky when stdin is a pipe. Avoid piping into `tv` and
41
+ # instead provide a `--source-command` so `tv` can keep stdin attached to the console.
42
+ # Also: `tv --ansi` is a flag (no value). Passing `true` makes it a positional argument
43
+ # (channel/path/command), which can lead to confusing behavior.
44
+ source_cmd = f"cmd /C type \"{options_txt_path}\""
45
+ tv_cmd = f"""
46
+ $OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
47
+ tv {preview_line} --ansi --source-command '{source_cmd}' --source-output "{{}}" | Out-File -Encoding utf8 -FilePath "{tv_out_path}" """
48
+ else:
49
+ source_cmd = f'cat "{options_txt_path}"'
50
+ tv_cmd = f"""tv {preview_line} --ansi --source-command "{source_cmd}" --source-output "{{}}" > "{tv_out_path}" """
51
+
52
+ # print(f"Running tv command: {tv_cmd}")
53
+ # print(f"Options file: {options_txt_path}")
54
+ # print(f"Content:\n{options_txt_path.read_text(encoding='utf-8')}")
55
+ # print(f"tv output file: {tv_out_path}")
56
+ from machineconfig.utils.code import run_shell_script
57
+ res = run_shell_script(tv_cmd, display_script=False, clean_env=False)
58
+
59
+ # If tv returned a non-zero code and there is no output file, treat it as an error.
60
+ if res.returncode != 0 and not tv_out_path.exists():
61
+ raise RuntimeError(f"Got error running tv command: {tv_cmd}\nreturncode: {res.returncode}")
62
+
63
+ # Read selections (if any) from the output file created by tv.
64
+ print(f"Reading tv output from: {tv_out_path}")
65
+ out_text = tv_out_path.read_text(encoding="utf-8-sig")
66
+ choice_string_multi = [x for x in out_text.splitlines() if x.strip() != ""]
67
+
68
+ # if len(choice_string_multi) == 0: # e.g. user pressed escape
69
+ # console.print(Panel("❓ No option selected!", title="Error", expand=False))
70
+ # return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
71
+
72
+ # Cleanup temporary files
73
+ options_txt_path.unlink(missing_ok=True)
74
+ tv_out_path.unlink(missing_ok=True)
27
75
  if not multi:
28
76
  try:
29
77
  choice_one_string = choice_string_multi[0]
78
+ if isinstance(list(options)[0], str):
79
+ print(f"✅ Selected option: {choice_one_string}")
80
+ return cast(T, choice_one_string)
30
81
  choice_idx = options_strings.index(choice_one_string)
31
- return list(options)[choice_idx]
82
+ choice_made = list(options)[choice_idx]
83
+ print(f"✅ Selected option: {choice_made}")
84
+ return choice_made
32
85
  except IndexError as ie:
33
- print(f"❌ Error: {options=}, {choice_string_multi=}")
34
- print(f"🔍 Available choices: {choice_string_multi}")
86
+ # print(f"❌ Error: {options=}, {choice_string_multi=}")
87
+ print(f"🔍 Available choices: {len(choice_string_multi)}")
35
88
  raise ie
89
+ if isinstance(list(options)[0], str):
90
+ result2 = cast(list[T], choice_string_multi)
91
+ print(f"✅ Selected options: {result2}")
92
+ return result2
36
93
  choice_idx_s = [options_strings.index(x) for x in choice_string_multi]
37
- return [list(options)[x] for x in choice_idx_s]
94
+ result = [list(options)[x] for x in choice_idx_s]
95
+ print(f"✅ Selected options: {result}")
96
+ return result
38
97
  else:
39
98
  if default is not None:
40
99
  assert default in options, f"Default `{default}` option not in options `{list(options)}`"
@@ -55,7 +114,7 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
55
114
  if choice_string == "":
56
115
  if default_string is None:
57
116
  console.print(Panel("🧨 Default option not available!", title="Error", expand=False))
58
- return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
117
+ return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
59
118
  choice_idx = options_strings.index(default_string)
60
119
  assert default is not None, "🧨 Default option not available!"
61
120
  choice_one: T = default
@@ -73,7 +132,7 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
73
132
  _ = ie
74
133
  # raise ValueError(f"Unknown choice. {choice_string}") from ie
75
134
  console.print(Panel(f"❓ Unknown choice: '{choice_string}'", title="Error", expand=False))
76
- return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
135
+ return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
77
136
  except (TypeError, ValueError) as te: # int(choice_string) failed due to # either the number is invalid, or the input is custom.
78
137
  if choice_string in options_strings: # string input
79
138
  choice_idx = options_strings.index(choice_one) # type: ignore
@@ -84,7 +143,7 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
84
143
  _ = te
85
144
  # raise ValueError(f"Unknown choice. {choice_string}") from te
86
145
  console.print(Panel(f"❓ Unknown choice: '{choice_string}'", title="Error", expand=False))
87
- return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
146
+ return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
88
147
  console.print(Panel(f"✅ Selected option {choice_idx}: {choice_one}", title="Selected", expand=False))
89
148
  if multi:
90
149
  return [choice_one]
@@ -103,14 +162,13 @@ def choose_cloud_interactively() -> str:
103
162
  raise ValueError(f"Got {tmp} from rclone listremotes")
104
163
  if len(remotes) == 0:
105
164
  raise RuntimeError("You don't have remotes. Configure your rclone first to get cloud services access.")
106
- cloud: str = choose_from_options(msg="WHICH CLOUD?", multi=False, options=list(remotes), default=remotes[0], fzf=True)
165
+ cloud: str = choose_from_options(msg="WHICH CLOUD?", multi=False, options=list(remotes), default=remotes[0], tv=True)
107
166
  console.print(Panel(f"✅ SELECTED CLOUD | {cloud}", border_style="bold blue", expand=False))
108
167
  return cloud
109
168
 
110
169
 
111
170
  def get_ssh_hosts() -> list[str]:
112
171
  from paramiko import SSHConfig
113
-
114
172
  c = SSHConfig()
115
173
  c.parse(open(Path.home().joinpath(".ssh/config"), encoding="utf-8"))
116
174
  return list(c.get_hostnames())
@@ -121,4 +179,4 @@ def choose_ssh_host(multi: Literal[False]) -> str: ...
121
179
  @overload
122
180
  def choose_ssh_host(multi: Literal[True]) -> list[str]: ...
123
181
  def choose_ssh_host(multi: bool):
124
- return choose_from_options(msg="", options=get_ssh_hosts(), multi=multi, fzf=True)
182
+ return choose_from_options(msg="", options=get_ssh_hosts(), multi=multi, tv=True)
File without changes
@@ -0,0 +1,211 @@
1
+
2
+ #!/usr/bin/env python3
3
+ import base64
4
+ import pathlib
5
+ import pprint
6
+ import shutil
7
+ import subprocess
8
+ import tempfile
9
+ import os
10
+ from typing import Any, overload, Literal, Union
11
+
12
+ from git import Optional
13
+
14
+
15
+ def _format_preview_value(value: Any) -> str:
16
+ if isinstance(value, str):
17
+ return value
18
+ return pprint.pformat(value, width=88, sort_dicts=True)
19
+
20
+
21
+ def _toml_inline_table(values: dict[str, str]) -> str:
22
+ if not values:
23
+ return ""
24
+ parts: list[str] = []
25
+ for key in sorted(values.keys()):
26
+ raw_value = values[key]
27
+ escaped = raw_value.replace("\\", "\\\\").replace('"', '\\"')
28
+ parts.append(f'{key} = "{escaped}"')
29
+ return "env = { " + ", ".join(parts) + " }\n"
30
+
31
+
32
+ def _normalize_extension(extension: str | None) -> str | None:
33
+ if extension is None:
34
+ return None
35
+ trimmed = extension.strip()
36
+ if trimmed.startswith("."):
37
+ trimmed = trimmed[1:]
38
+ if not trimmed:
39
+ return None
40
+ return trimmed
41
+
42
+
43
+ def _infer_extension_from_key(key: Optional[str]) -> str | None:
44
+ if not isinstance(key, str):
45
+ return None
46
+ candidate = key.strip()
47
+ if not candidate or any(char.isspace() for char in candidate):
48
+ return None
49
+ suffix = pathlib.Path(candidate).suffix
50
+ if not suffix:
51
+ return None
52
+ return _normalize_extension(suffix)
53
+
54
+
55
+ @overload
56
+ def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[True], preview_size_percent: float) -> list[str]: ...
57
+ @overload
58
+ def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[False], preview_size_percent: float) -> Union[str, None]: ...
59
+ def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: bool, preview_size_percent: float) -> Union[Union[str, None], list[str]]:
60
+ keys = list(options_to_preview_mapping.keys())
61
+ if not keys:
62
+ return [] if multi else None
63
+ normalized_extension = _normalize_extension(extension)
64
+ preview_panel_size = max(10, min(90, int(preview_size_percent)))
65
+ terminal_width = shutil.get_terminal_size(fallback=(120, 40)).columns
66
+ preview_width = max(20, int(terminal_width * preview_panel_size / 100) - 4)
67
+ with tempfile.TemporaryDirectory(prefix="tv_channel_") as tmpdir:
68
+ tempdir = pathlib.Path(tmpdir)
69
+ entries: list[str] = []
70
+ index_map: dict[int, str] = {}
71
+ preview_map_path = tempdir / "previews.tsv"
72
+ preview_rows: list[str] = []
73
+ for idx, key in enumerate(keys):
74
+ display_key = key.replace("\t", " ").replace("\n", " ")
75
+ entries.append(f"{idx}\t{display_key}")
76
+ index_map[idx] = key
77
+ preview_value = _format_preview_value(options_to_preview_mapping[key])
78
+ encoded_preview = base64.b64encode(preview_value.encode("utf-8")).decode("ascii")
79
+ entry_extension = normalized_extension or _infer_extension_from_key(key) or ""
80
+ preview_rows.append(f"{idx}\t{encoded_preview}\t{entry_extension}")
81
+ preview_map_path.write_text("\n".join(preview_rows), encoding="utf-8")
82
+ entries_path = tempdir / "entries.tsv"
83
+ entries_path.write_text("\n".join(entries), encoding="utf-8")
84
+ preview_script = tempdir / "preview.sh"
85
+ preview_script.write_text(
86
+ """#!/usr/bin/env bash
87
+ set -euo pipefail
88
+
89
+ idx="$1"
90
+ script_dir="$(cd -- "$(dirname -- "$0")" && pwd)"
91
+ previews_file="${script_dir}/previews.tsv"
92
+
93
+ if [[ ! -f "${previews_file}" ]]; then
94
+ exit 0
95
+ fi
96
+
97
+ encoded_preview="$(awk -F '\t' -v idx="${idx}" '($1==idx){print $2; exit}' "${previews_file}" || true)"
98
+
99
+ if [[ -z "${encoded_preview}" ]]; then
100
+ exit 0
101
+ fi
102
+
103
+ preview_content="$(printf '%s' "${encoded_preview}" | base64 --decode)"
104
+
105
+ preview_ext_from_row="$(awk -F '\t' -v idx="${idx}" '($1==idx){print $3; exit}' "${previews_file}" || true)"
106
+ preview_ext="${MCFG_PREVIEW_EXT:-${preview_ext_from_row}}"
107
+ preview_width="${MCFG_PREVIEW_WIDTH:-}"
108
+ preview_size_pct="${MCFG_PREVIEW_SIZE_PCT:-}"
109
+
110
+ if [[ -z "${preview_width}" && -n "${COLUMNS:-}" ]]; then
111
+ if [[ "${preview_size_pct}" =~ ^[0-9]+$ ]]; then
112
+ preview_width=$((COLUMNS * preview_size_pct / 100))
113
+ else
114
+ preview_width="${COLUMNS}"
115
+ fi
116
+ fi
117
+
118
+ if command -v bat >/dev/null 2>&1; then
119
+ bat_args=(--force-colorization --style=plain --paging=never --wrap=character)
120
+ if [[ -n "${preview_ext}" ]]; then
121
+ bat_args+=(--language "${preview_ext}")
122
+ fi
123
+ if [[ "${preview_width}" =~ ^[0-9]+$ ]]; then
124
+ bat_args+=(--terminal-width "${preview_width}")
125
+ fi
126
+ printf '%s' "${preview_content}" | bat "${bat_args[@]}"
127
+ elif command -v glow >/dev/null 2>&1; then
128
+ printf '%s' "${preview_content}" | glow -
129
+ elif command -v fold >/dev/null 2>&1 && [[ "${preview_width}" =~ ^[0-9]+$ ]]; then
130
+ printf '%s' "${preview_content}" | fold -s -w "${preview_width}"
131
+ else
132
+ printf '%s' "${preview_content}"
133
+ fi
134
+ """,
135
+ encoding="utf-8"
136
+ )
137
+ preview_script.chmod(0o755)
138
+ preview_env: dict[str, str] = {
139
+ "BAT_THEME": "ansi",
140
+ "MCFG_PREVIEW_SIZE_PCT": str(preview_panel_size),
141
+ }
142
+ if normalized_extension is not None:
143
+ preview_env["MCFG_PREVIEW_EXT"] = normalized_extension
144
+ if preview_width > 0:
145
+ preview_env["MCFG_PREVIEW_WIDTH"] = str(preview_width)
146
+ preview_env_line = _toml_inline_table(preview_env)
147
+ channel_config = f"""[metadata]
148
+ name = "temp_options"
149
+ description = "Temporary channel for selecting options"
150
+
151
+ [source]
152
+ command = "bat '{entries_path}'"
153
+ display = "{{split:\\t:1}}"
154
+ output = "{{split:\\t:0}}"
155
+
156
+ [preview]
157
+ command = "{preview_script} {{split:\\t:0}}"
158
+ {preview_env_line}
159
+
160
+ [ui.preview_panel]
161
+ size = {preview_panel_size}
162
+ """
163
+ channel_path = tempdir / "temp_options.toml"
164
+ channel_path.write_text(channel_config, encoding="utf-8")
165
+ env = os.environ.copy()
166
+ tv_config_dir = pathlib.Path.home() / ".config" / "television"
167
+ if not tv_config_dir.exists():
168
+ tv_config_dir = pathlib.Path(os.getenv("XDG_CONFIG_HOME", str(pathlib.Path.home() / ".config"))) / "television"
169
+ cable_dir = tv_config_dir / "cable"
170
+ cable_dir.mkdir(parents=True, exist_ok=True)
171
+ temp_channel_link = cable_dir / "temp_options.toml"
172
+ if temp_channel_link.exists() or temp_channel_link.is_symlink():
173
+ temp_channel_link.unlink()
174
+ temp_channel_link.symlink_to(channel_path)
175
+ output_file = tempdir / "selection.txt"
176
+ try:
177
+ result = subprocess.run(["tv", "temp_options"], check=False, stdout=output_file.open("w"), text=True, env=env)
178
+ finally:
179
+ if temp_channel_link.exists() or temp_channel_link.is_symlink():
180
+ temp_channel_link.unlink()
181
+ if result.returncode not in (0, 130):
182
+ raise SystemExit(result.returncode)
183
+ if result.returncode == 130:
184
+ return [] if multi else None
185
+ if not output_file.exists():
186
+ return [] if multi else None
187
+ selected_lines = [line.strip() for line in output_file.read_text().splitlines() if line.strip()]
188
+ if not selected_lines:
189
+ return [] if multi else None
190
+ selected_keys: list[str] = []
191
+ for line in selected_lines:
192
+ try:
193
+ index = int(line)
194
+ key = index_map.get(index)
195
+ if key is not None:
196
+ selected_keys.append(key)
197
+ except ValueError:
198
+ continue
199
+ if multi:
200
+ return selected_keys
201
+ return selected_keys[0] if selected_keys else None
202
+
203
+
204
+ if __name__ == "__main__":
205
+ demo_mapping: dict[str, str] = {
206
+ "Option 1": "# Option 1\nThis is the preview for option 1.",
207
+ "Option 2": "# Option 2\nThis is the preview for option 2.",
208
+ "Option 3": "# Option 3\nThis is the preview for option 3."
209
+ }
210
+ result = select_from_options(demo_mapping, multi=True, extension="md", preview_size_percent=50)
211
+ print(f"Selected: {result}")
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env python3
2
+ import pathlib
3
+ import pprint
4
+ import shutil
5
+ from typing import Any, Literal, Union, overload
6
+
7
+
8
+ def _format_preview_value(value: Any) -> str:
9
+ if isinstance(value, str):
10
+ return value
11
+ return pprint.pformat(value, width=88, sort_dicts=True)
12
+
13
+
14
+ def _normalize_extension(extension: str | None) -> str | None:
15
+ if extension is None:
16
+ return None
17
+ trimmed = extension.strip()
18
+ if trimmed.startswith("."):
19
+ trimmed = trimmed[1:]
20
+ if not trimmed:
21
+ return None
22
+ return trimmed
23
+
24
+
25
+ @overload
26
+ def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[True], preview_size_percent: float) -> list[str]: ...
27
+ @overload
28
+ def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[False], preview_size_percent: float) -> str | None: ...
29
+ def select_from_options(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: bool, preview_size_percent: float) -> Union[list[str], Union[str, None]]:
30
+ keys = list(options_to_preview_mapping.keys())
31
+ if not keys:
32
+ return [] if multi else None
33
+ normalized_extension = _normalize_extension(extension)
34
+ preview_panel_size = max(10, min(90, int(preview_size_percent)))
35
+ from machineconfig.utils.accessories import randstr
36
+ tempdir = pathlib.Path.home() / "tmp_results" / "tmp_files" / f"tv_channel_{randstr(6)}"
37
+ tempdir.mkdir(parents=True, exist_ok=True)
38
+ try:
39
+ index_map: dict[str, str] = {}
40
+ ext_for_preview = normalized_extension or "txt"
41
+ entries_lines: list[str] = []
42
+ for idx, key in enumerate(keys):
43
+ display_key = key.replace("\n", " ")
44
+ entries_lines.append(f"{idx}|{display_key}")
45
+ index_map[str(idx)] = key
46
+ preview_value = _format_preview_value(options_to_preview_mapping[key])
47
+ preview_file = tempdir / f"{idx}.{ext_for_preview}"
48
+ preview_file.write_text(preview_value, encoding="utf-8")
49
+ entries_path = tempdir / "entries.txt"
50
+ entries_path.write_text("\n".join(entries_lines), encoding="utf-8")
51
+ output_file = tempdir / "selection.txt"
52
+ tempdir_fwd = str(tempdir).replace("\\", "/")
53
+ source_cmd = f"cmd /C type \"{entries_path}\""
54
+ preview_cmd = f"bat --force-colorization --style=plain --paging=never {tempdir_fwd}/{{split:|:0}}.{ext_for_preview}"
55
+ tv_cmd = f'''$OutputEncoding = [Console]::InputEncoding = [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
56
+ tv --ansi --source-command '{source_cmd}' --source-display '{{split:|:1}}' --source-output '{{split:|:0}}' --preview-command '{preview_cmd}' --preview-size {preview_panel_size} --no-remote | Out-File -Encoding utf8 -FilePath "{output_file}"
57
+ '''
58
+ from machineconfig.utils.code import run_shell_script
59
+ result = run_shell_script(tv_cmd, display_script=False, clean_env=False)
60
+ if result.returncode not in (0, 130) and not output_file.exists():
61
+ raise SystemExit(result.returncode)
62
+ if result.returncode == 130:
63
+ return [] if multi else None
64
+ if not output_file.exists():
65
+ return [] if multi else None
66
+ selected_lines = [line.strip() for line in output_file.read_text(encoding="utf-8-sig").splitlines() if line.strip()]
67
+ if not selected_lines:
68
+ return [] if multi else None
69
+ selected_keys: list[str] = []
70
+ for line in selected_lines:
71
+ key = index_map.get(line)
72
+ if key is not None:
73
+ selected_keys.append(key)
74
+ if multi:
75
+ return selected_keys
76
+ return selected_keys[0] if selected_keys else None
77
+ finally:
78
+ shutil.rmtree(tempdir, ignore_errors=True)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ demo_mapping: dict[str, str] = {
83
+ "Option 1": "# Option 1\nThis is the preview for option 1.",
84
+ "Option 2": "# Option 2\nThis is the preview for option 2.",
85
+ "Option 3": "# Option 3\nThis is the preview for option 3."
86
+ }
87
+ result = select_from_options(demo_mapping, multi=True, extension="md", preview_size_percent=50)
88
+ print(f"Selected: {result}")
@@ -0,0 +1,37 @@
1
+ from typing import Any, Literal, overload
2
+ import platform
3
+
4
+
5
+ @overload
6
+ def choose_from_dict_with_preview(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[False], preview_size_percent: float) -> str | None: ...
7
+ @overload
8
+ def choose_from_dict_with_preview(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: Literal[True], preview_size_percent: float) -> list[str]: ...
9
+ def choose_from_dict_with_preview(options_to_preview_mapping: dict[str, Any], extension: str | None, multi: bool, preview_size_percent: float) -> str | list[str] | None:
10
+ if not options_to_preview_mapping:
11
+ return [] if multi else None
12
+ system = platform.system()
13
+ if system == "Windows":
14
+ from machineconfig.utils.options_utils.options_tv_windows import select_from_options
15
+ return select_from_options(options_to_preview_mapping, extension=extension, multi=multi, preview_size_percent=preview_size_percent)
16
+ else:
17
+ from machineconfig.utils.options_utils.options_tv_linux import select_from_options
18
+ return select_from_options(options_to_preview_mapping, extension=extension, multi=multi, preview_size_percent=preview_size_percent)
19
+
20
+
21
+ if __name__ == "__main__":
22
+ demo_mapping: dict[str, str] = {
23
+ "config.py": """from pathlib import Path
24
+
25
+ CONFIG_DIR = Path.home() / ".config"
26
+ DEBUG = True
27
+ """,
28
+ "utils.py": """def greet(name: str) -> str:
29
+ return f"Hello, {name}!"
30
+ """,
31
+ "main.rs": """fn main() {
32
+ println!("Hello, world!");
33
+ }
34
+ """,
35
+ }
36
+ result = choose_from_dict_with_preview(demo_mapping, extension="py", multi=True, preview_size_percent=50)
37
+ print(f"Selected: {result}")