npcsh 1.1.19__py3-none-any.whl → 1.1.21__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 (173) hide show
  1. npcsh/_state.py +16 -78
  2. npcsh/diff_viewer.py +3 -3
  3. npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
  4. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +18 -7
  5. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +18 -7
  6. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +20 -9
  7. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +53 -15
  8. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  9. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +393 -317
  10. npcsh/npc_team/jinxs/lib/utils/models.jinx +343 -0
  11. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +8 -7
  12. npcsh/npc_team/jinxs/modes/alicanto.jinx +1573 -296
  13. npcsh/npc_team/jinxs/modes/arxiv.jinx +6 -6
  14. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  15. npcsh/npc_team/jinxs/modes/corca.jinx +4 -4
  16. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  17. npcsh/npc_team/jinxs/modes/guac.jinx +4 -4
  18. npcsh/npc_team/jinxs/modes/kg.jinx +941 -0
  19. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  20. npcsh/npc_team/jinxs/modes/nql.jinx +460 -0
  21. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  22. npcsh/npc_team/jinxs/modes/plonk.jinx +490 -304
  23. npcsh/npc_team/jinxs/modes/pti.jinx +1 -1
  24. npcsh/npc_team/jinxs/modes/reattach.jinx +4 -4
  25. npcsh/npc_team/jinxs/modes/spool.jinx +4 -4
  26. npcsh/npc_team/jinxs/modes/team.jinx +504 -0
  27. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  28. npcsh/npc_team/jinxs/modes/wander.jinx +455 -182
  29. npcsh/npc_team/jinxs/modes/yap.jinx +10 -3
  30. npcsh/npcsh.py +112 -47
  31. npcsh/routes.py +12 -3
  32. npcsh/salmon_simulation.py +0 -0
  33. npcsh-1.1.21.data/data/npcsh/npc_team/alicanto.jinx +1633 -0
  34. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/arxiv.jinx +6 -6
  35. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  36. npcsh-1.1.21.data/data/npcsh/npc_team/compress.jinx +428 -0
  37. npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  38. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.jinx +4 -4
  39. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/db_search.jinx +18 -7
  40. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/file_search.jinx +18 -7
  41. npcsh-1.1.21.data/data/npcsh/npc_team/git.jinx +795 -0
  42. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.jinx +4 -4
  43. npcsh-1.1.21.data/data/npcsh/npc_team/jinxs.jinx +407 -0
  44. npcsh-1.1.21.data/data/npcsh/npc_team/kg.jinx +941 -0
  45. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kg_search.jinx +20 -9
  46. npcsh-1.1.21.data/data/npcsh/npc_team/memories.jinx +414 -0
  47. npcsh-1.1.21.data/data/npcsh/npc_team/models.jinx +343 -0
  48. npcsh-1.1.21.data/data/npcsh/npc_team/nql.jinx +460 -0
  49. npcsh-1.1.21.data/data/npcsh/npc_team/papers.jinx +578 -0
  50. npcsh-1.1.21.data/data/npcsh/npc_team/plonk.jinx +565 -0
  51. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/pti.jinx +1 -1
  52. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/reattach.jinx +4 -4
  53. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/setup.jinx +8 -7
  54. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.jinx +4 -4
  55. npcsh-1.1.21.data/data/npcsh/npc_team/team.jinx +504 -0
  56. npcsh-1.1.21.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  57. npcsh-1.1.21.data/data/npcsh/npc_team/wander.jinx +728 -0
  58. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/web_search.jinx +53 -15
  59. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.jinx +10 -3
  60. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/METADATA +2 -2
  61. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/RECORD +147 -148
  62. npcsh-1.1.21.dist-info/entry_points.txt +11 -0
  63. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -299
  64. npcsh/npc_team/jinxs/bin/memories.jinx +0 -316
  65. npcsh/npc_team/jinxs/bin/nql.jinx +0 -141
  66. npcsh/npc_team/jinxs/bin/team_tui.jinx +0 -327
  67. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  68. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  69. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  70. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  71. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  72. npcsh/npc_team/plonkjr.npc +0 -23
  73. npcsh-1.1.19.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  74. npcsh-1.1.19.data/data/npcsh/npc_team/compress.jinx +0 -140
  75. npcsh-1.1.19.data/data/npcsh/npc_team/config_tui.jinx +0 -299
  76. npcsh-1.1.19.data/data/npcsh/npc_team/jinxs.jinx +0 -331
  77. npcsh-1.1.19.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  78. npcsh-1.1.19.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  79. npcsh-1.1.19.data/data/npcsh/npc_team/memories.jinx +0 -316
  80. npcsh-1.1.19.data/data/npcsh/npc_team/nql.jinx +0 -141
  81. npcsh-1.1.19.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  82. npcsh-1.1.19.data/data/npcsh/npc_team/plonk.jinx +0 -379
  83. npcsh-1.1.19.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  84. npcsh-1.1.19.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  85. npcsh-1.1.19.data/data/npcsh/npc_team/team_tui.jinx +0 -327
  86. npcsh-1.1.19.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  87. npcsh-1.1.19.data/data/npcsh/npc_team/wander.jinx +0 -455
  88. npcsh-1.1.19.dist-info/entry_points.txt +0 -22
  89. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  90. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  91. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  92. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  93. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  94. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  95. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  96. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.png +0 -0
  97. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  98. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  99. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/build.jinx +0 -0
  100. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/chat.jinx +0 -0
  101. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/click.jinx +0 -0
  102. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  103. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  104. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  105. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  106. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/compile.jinx +0 -0
  107. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  108. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/convene.jinx +0 -0
  109. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.npc +0 -0
  110. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.png +0 -0
  111. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca_example.png +0 -0
  112. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  113. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
  114. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  115. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic.npc +0 -0
  116. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic4.png +0 -0
  117. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.npc +0 -0
  118. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.png +0 -0
  119. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/help.jinx +0 -0
  120. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  121. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/init.jinx +0 -0
  122. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  123. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  124. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  125. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  126. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  127. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  128. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  129. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/notify.jinx +0 -0
  130. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  131. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  132. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  133. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  134. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/ots.jinx +0 -0
  135. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/paste.jinx +0 -0
  136. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.npc +0 -0
  137. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.png +0 -0
  138. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  139. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/python.jinx +0 -0
  140. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  141. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/roll.jinx +0 -0
  142. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  143. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sample.jinx +0 -0
  144. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  145. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/search.jinx +0 -0
  146. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  147. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/serve.jinx +0 -0
  148. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/set.jinx +0 -0
  149. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sh.jinx +0 -0
  150. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/shh.jinx +0 -0
  151. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  152. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.png +0 -0
  153. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  154. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  155. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.png +0 -0
  156. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sql.jinx +0 -0
  157. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch.jinx +0 -0
  158. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  159. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  160. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switches.jinx +0 -0
  161. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sync.jinx +0 -0
  162. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  163. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  164. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  165. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/usage.jinx +0 -0
  166. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  167. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/wait.jinx +0 -0
  168. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  169. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.png +0 -0
  170. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  171. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/WHEEL +0 -0
  172. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/licenses/LICENSE +0 -0
  173. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,388 @@
1
+ jinx_name: "vixynt"
2
+ description: "Image creation studio TUI - generate and manage images with parameter controls"
3
+ interactive: true
4
+ inputs:
5
+ - prompt: null
6
+ - model: null
7
+ - provider: null
8
+ - output_name: null
9
+ - attachments: null
10
+ - n_images: null
11
+ - height: null
12
+ - width: null
13
+ steps:
14
+ - name: "vixynt_tui"
15
+ engine: "python"
16
+ code: |
17
+ import os
18
+ import sys
19
+ import tty
20
+ import termios
21
+ import select
22
+ from datetime import datetime
23
+ from PIL import Image
24
+
25
+ from npcpy.llm_funcs import gen_image
26
+
27
+ npc = context.get('npc')
28
+ if isinstance(npc, str):
29
+ npc = None
30
+
31
+ # ========== State ==========
32
+ class VixyntState:
33
+ def __init__(self):
34
+ self.prompt = ""
35
+ self.model = ""
36
+ self.provider = ""
37
+ self.width_val = 1024
38
+ self.height_val = 1024
39
+ self.n_images = 1
40
+ self.output_name = ""
41
+ self.attachments = ""
42
+ # UI state
43
+ self.params = ['prompt', 'model', 'provider', 'width', 'height', 'n_images', 'output', 'attachments']
44
+ self.sel = 0
45
+ self.scroll = 0
46
+ self.mode = 'params' # params, editing, gallery, generating
47
+ self.edit_buf = ""
48
+ self.edit_cursor = 0
49
+ self.status = "Ready"
50
+ self.gallery = [] # [{"path": str, "prompt": str, "timestamp": str}]
51
+ self.gallery_sel = 0
52
+ self.gallery_scroll = 0
53
+
54
+ ui = VixyntState()
55
+
56
+ # Load from context
57
+ ui.prompt = str(context.get('prompt') or '')
58
+ ui.model = str(context.get('model') or os.getenv('NPCSH_IMAGE_GEN_MODEL', ''))
59
+ ui.provider = str(context.get('provider') or os.getenv('NPCSH_IMAGE_GEN_PROVIDER', ''))
60
+ try:
61
+ ui.width_val = int(context.get('width') or 1024)
62
+ except:
63
+ pass
64
+ try:
65
+ ui.height_val = int(context.get('height') or 1024)
66
+ except:
67
+ pass
68
+ try:
69
+ ui.n_images = int(context.get('n_images') or 1)
70
+ except:
71
+ pass
72
+ ui.output_name = str(context.get('output_name') or '')
73
+ ui.attachments = str(context.get('attachments') or '')
74
+
75
+ # Load existing gallery
76
+ img_dir = os.path.expanduser("~/.npcsh/images/")
77
+ if os.path.isdir(img_dir):
78
+ for f in sorted(os.listdir(img_dir), reverse=True)[:50]:
79
+ if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
80
+ ui.gallery.append({"path": os.path.join(img_dir, f), "prompt": "", "timestamp": f})
81
+
82
+ def get_size():
83
+ try:
84
+ s = os.get_terminal_size()
85
+ return s.columns, s.lines
86
+ except:
87
+ return 80, 24
88
+
89
+ def get_param_value(idx):
90
+ p = ui.params[idx]
91
+ if p == 'prompt': return ui.prompt
92
+ if p == 'model': return ui.model or '(env default)'
93
+ if p == 'provider': return ui.provider or '(env default)'
94
+ if p == 'width': return str(ui.width_val)
95
+ if p == 'height': return str(ui.height_val)
96
+ if p == 'n_images': return str(ui.n_images)
97
+ if p == 'output': return ui.output_name or '(auto)'
98
+ if p == 'attachments': return ui.attachments or '(none)'
99
+ return ''
100
+
101
+ def set_param_value(idx, val):
102
+ p = ui.params[idx]
103
+ if p == 'prompt': ui.prompt = val
104
+ elif p == 'model': ui.model = val
105
+ elif p == 'provider': ui.provider = val
106
+ elif p == 'width':
107
+ try: ui.width_val = int(val)
108
+ except: pass
109
+ elif p == 'height':
110
+ try: ui.height_val = int(val)
111
+ except: pass
112
+ elif p == 'n_images':
113
+ try: ui.n_images = max(1, int(val))
114
+ except: pass
115
+ elif p == 'output': ui.output_name = val
116
+ elif p == 'attachments': ui.attachments = val
117
+
118
+ def generate_images():
119
+ ui.mode = 'generating'
120
+ ui.status = "Generating..."
121
+ render_screen()
122
+
123
+ if not ui.prompt:
124
+ ui.status = "Error: No prompt provided"
125
+ ui.mode = 'params'
126
+ return
127
+
128
+ input_images = []
129
+ if ui.attachments.strip():
130
+ input_images = [p.strip() for p in ui.attachments.split(',')]
131
+
132
+ try:
133
+ result = gen_image(
134
+ prompt=ui.prompt,
135
+ model=ui.model or None,
136
+ provider=ui.provider or None,
137
+ npc=npc,
138
+ height=ui.height_val,
139
+ width=ui.width_val,
140
+ n_images=ui.n_images,
141
+ input_images=input_images if input_images else None
142
+ )
143
+
144
+ if not isinstance(result, list):
145
+ images_list = [result] if result is not None else []
146
+ else:
147
+ images_list = result
148
+
149
+ saved = []
150
+ for i, image in enumerate(images_list):
151
+ if image is None:
152
+ continue
153
+ if ui.output_name and ui.output_name.strip():
154
+ base, ext = os.path.splitext(os.path.expanduser(ui.output_name))
155
+ if not ext:
156
+ ext = ".png"
157
+ out_file = base + ("_" + str(i) if len(images_list) > 1 else "") + ext
158
+ else:
159
+ os.makedirs(img_dir, exist_ok=True)
160
+ out_file = os.path.join(img_dir, "vixynt_" + datetime.now().strftime('%Y%m%d_%H%M%S') + "_" + str(i) + ".png")
161
+ image.save(out_file)
162
+ saved.append(out_file)
163
+ ui.gallery.insert(0, {"path": out_file, "prompt": ui.prompt, "timestamp": os.path.basename(out_file)})
164
+
165
+ ui.status = "Generated " + str(len(saved)) + " image(s): " + ", ".join(os.path.basename(f) for f in saved)
166
+ except Exception as e:
167
+ ui.status = "Error: " + str(e)[:60]
168
+
169
+ ui.mode = 'params'
170
+
171
+ # ========== Rendering ==========
172
+ def render_screen():
173
+ width, height = get_size()
174
+ out = []
175
+ out.append("\033[H")
176
+
177
+ header = " VIXYNT - Image Creation Studio "
178
+ out.append("\033[1;1H\033[7;1m" + header.ljust(width) + "\033[0m")
179
+
180
+ if ui.mode in ('params', 'editing', 'generating'):
181
+ # Parameter panel
182
+ out.append("\033[3;1H\033[36;1m Parameters \033[90m" + ("-" * (width - 13)) + "\033[0m")
183
+
184
+ for i, p in enumerate(ui.params):
185
+ row = 4 + i
186
+ out.append("\033[" + str(row) + ";1H\033[K")
187
+ label = p.capitalize() + ":"
188
+ val = get_param_value(i)
189
+
190
+ if ui.mode == 'editing' and i == ui.sel:
191
+ line = " " + label.ljust(14) + "\033[7m " + ui.edit_buf + " \033[0m"
192
+ else:
193
+ line = " " + label.ljust(14) + val[:width - 18]
194
+
195
+ if i == ui.sel and ui.mode != 'editing':
196
+ out.append("\033[7m>" + line + "\033[0m")
197
+ else:
198
+ out.append(" " + line)
199
+
200
+ # Gallery preview
201
+ gallery_row = 4 + len(ui.params) + 1
202
+ out.append("\033[" + str(gallery_row) + ";1H\033[33;1m Gallery (" + str(len(ui.gallery)) + ") \033[90m" + ("-" * (width - 20)) + "\033[0m")
203
+
204
+ gallery_h = height - gallery_row - 4
205
+ for i in range(gallery_h):
206
+ idx = ui.gallery_scroll + i
207
+ row = gallery_row + 1 + i
208
+ out.append("\033[" + str(row) + ";1H\033[K")
209
+ if idx >= len(ui.gallery):
210
+ continue
211
+ g = ui.gallery[idx]
212
+ fname = os.path.basename(g['path'])[:width - 6]
213
+ out.append(" " + fname)
214
+
215
+ elif ui.mode == 'gallery':
216
+ out.append("\033[3;1H\033[33;1m Gallery (" + str(len(ui.gallery)) + ") \033[90m" + ("-" * (width - 20)) + "\033[0m")
217
+
218
+ gallery_h = height - 6
219
+ for i in range(gallery_h):
220
+ idx = ui.gallery_scroll + i
221
+ row = 4 + i
222
+ out.append("\033[" + str(row) + ";1H\033[K")
223
+ if idx >= len(ui.gallery):
224
+ continue
225
+ g = ui.gallery[idx]
226
+ fname = os.path.basename(g['path'])
227
+ if idx == ui.gallery_sel:
228
+ out.append("\033[7m> " + fname[:width-4] + "\033[0m")
229
+ else:
230
+ out.append(" " + fname[:width-4])
231
+
232
+ # Status + footer
233
+ out.append("\033[" + str(height-2) + ";1H\033[K\033[90m" + ("-" * width) + "\033[0m")
234
+ out.append("\033[" + str(height-1) + ";1H\033[K " + ui.status[:width-2])
235
+
236
+ if ui.mode == 'editing':
237
+ footer = " Type value, Enter:Confirm Esc:Cancel "
238
+ elif ui.mode == 'gallery':
239
+ footer = " j/k:Nav o:Open Enter:Select b:Back q:Quit "
240
+ elif ui.mode == 'generating':
241
+ footer = " Generating... "
242
+ else:
243
+ footer = " j/k:Nav e:Edit Enter:Generate g:Gallery q:Quit "
244
+ out.append("\033[" + str(height) + ";1H\033[K\033[7m" + footer.ljust(width) + "\033[0m")
245
+
246
+ sys.stdout.write(''.join(out))
247
+ sys.stdout.flush()
248
+
249
+ # ========== Input Handling ==========
250
+ def handle_input(c, fd):
251
+ if ui.mode == 'editing':
252
+ return handle_edit(c, fd)
253
+ if ui.mode == 'gallery':
254
+ return handle_gallery(c, fd)
255
+
256
+ if c == '\x1b':
257
+ if select.select([fd], [], [], 0.05)[0]:
258
+ c2 = os.read(fd, 1).decode('latin-1')
259
+ if c2 == '[':
260
+ c3 = os.read(fd, 1).decode('latin-1')
261
+ if c3 == 'A':
262
+ ui.sel = max(0, ui.sel - 1)
263
+ elif c3 == 'B':
264
+ ui.sel = min(len(ui.params) - 1, ui.sel + 1)
265
+ return True
266
+
267
+ if c == 'q':
268
+ return False
269
+ elif c == 'j':
270
+ ui.sel = min(len(ui.params) - 1, ui.sel + 1)
271
+ elif c == 'k':
272
+ ui.sel = max(0, ui.sel - 1)
273
+ elif c == 'e' or c in ('\r', '\n'):
274
+ if c in ('\r', '\n') and ui.sel == 0 and ui.prompt:
275
+ # Enter on prompt with existing prompt = generate
276
+ generate_images()
277
+ else:
278
+ ui.mode = 'editing'
279
+ ui.edit_buf = get_param_value(ui.sel)
280
+ if ui.edit_buf in ('(env default)', '(auto)', '(none)'):
281
+ ui.edit_buf = ""
282
+ ui.edit_cursor = len(ui.edit_buf)
283
+ elif c == 'g':
284
+ ui.mode = 'gallery'
285
+ ui.gallery_sel = 0
286
+ ui.gallery_scroll = 0
287
+ elif c == 'G':
288
+ generate_images()
289
+
290
+ return True
291
+
292
+ def handle_edit(c, fd):
293
+ if c == '\x1b':
294
+ if select.select([fd], [], [], 0.05)[0]:
295
+ os.read(fd, 2)
296
+ ui.mode = 'params'
297
+ return True
298
+
299
+ if c in ('\r', '\n'):
300
+ set_param_value(ui.sel, ui.edit_buf)
301
+ ui.mode = 'params'
302
+ return True
303
+
304
+ if c == '\x7f' or c == '\x08':
305
+ if ui.edit_cursor > 0:
306
+ ui.edit_buf = ui.edit_buf[:ui.edit_cursor-1] + ui.edit_buf[ui.edit_cursor:]
307
+ ui.edit_cursor -= 1
308
+ elif c >= ' ' and c <= '~':
309
+ ui.edit_buf = ui.edit_buf[:ui.edit_cursor] + c + ui.edit_buf[ui.edit_cursor:]
310
+ ui.edit_cursor += 1
311
+
312
+ return True
313
+
314
+ def handle_gallery(c, fd):
315
+ if c == '\x1b':
316
+ if select.select([fd], [], [], 0.05)[0]:
317
+ c2 = os.read(fd, 1).decode('latin-1')
318
+ if c2 == '[':
319
+ c3 = os.read(fd, 1).decode('latin-1')
320
+ if c3 == 'A':
321
+ ui.gallery_sel = max(0, ui.gallery_sel - 1)
322
+ elif c3 == 'B':
323
+ ui.gallery_sel = min(max(0, len(ui.gallery) - 1), ui.gallery_sel + 1)
324
+ else:
325
+ ui.mode = 'params'
326
+ return True
327
+
328
+ if c == 'q':
329
+ return False
330
+ elif c == 'b':
331
+ ui.mode = 'params'
332
+ elif c == 'j':
333
+ ui.gallery_sel = min(max(0, len(ui.gallery) - 1), ui.gallery_sel + 1)
334
+ elif c == 'k':
335
+ ui.gallery_sel = max(0, ui.gallery_sel - 1)
336
+ elif c == 'o' and ui.gallery:
337
+ import subprocess
338
+ path = ui.gallery[ui.gallery_sel]['path']
339
+ try:
340
+ subprocess.Popen(['xdg-open', path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
341
+ ui.status = "Opened: " + os.path.basename(path)
342
+ except:
343
+ ui.status = "Could not open image"
344
+
345
+ # Keep gallery_scroll in range
346
+ _, height = get_size()
347
+ gallery_h = height - 6
348
+ if ui.gallery_sel < ui.gallery_scroll:
349
+ ui.gallery_scroll = ui.gallery_sel
350
+ elif ui.gallery_sel >= ui.gallery_scroll + gallery_h:
351
+ ui.gallery_scroll = ui.gallery_sel - gallery_h + 1
352
+
353
+ return True
354
+
355
+ # ========== One-shot mode ==========
356
+ if ui.prompt and context.get('prompt'):
357
+ # If prompt provided via CLI args, generate immediately
358
+ generate_images()
359
+ context['output'] = ui.status
360
+ context['messages'] = context.get('messages', [])
361
+
362
+ # ========== Main TUI Loop ==========
363
+ elif not sys.stdin.isatty():
364
+ context['output'] = "Vixynt requires an interactive terminal."
365
+ else:
366
+ fd = sys.stdin.fileno()
367
+ old_settings = termios.tcgetattr(fd)
368
+
369
+ try:
370
+ tty.setcbreak(fd)
371
+ sys.stdout.write('\033[?25l')
372
+ sys.stdout.write('\033[2J')
373
+ render_screen()
374
+
375
+ running = True
376
+ while running:
377
+ c = os.read(fd, 1).decode('latin-1')
378
+ running = handle_input(c, fd)
379
+ render_screen()
380
+
381
+ finally:
382
+ termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
383
+ sys.stdout.write('\033[?25h')
384
+ sys.stdout.write('\033[2J\033[H')
385
+ sys.stdout.flush()
386
+
387
+ context['output'] = ui.status
388
+ context['messages'] = context.get('messages', [])