npcsh 1.1.20__py3-none-any.whl → 1.1.22__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 (186) hide show
  1. npcsh/_state.py +15 -76
  2. npcsh/benchmark/npcsh_agent.py +22 -14
  3. npcsh/benchmark/templates/install-npcsh.sh.j2 +2 -2
  4. npcsh/diff_viewer.py +3 -3
  5. npcsh/mcp_server.py +9 -1
  6. npcsh/npc_team/alicanto.npc +12 -6
  7. npcsh/npc_team/corca.npc +0 -1
  8. npcsh/npc_team/frederic.npc +2 -3
  9. npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
  10. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +83 -61
  11. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +17 -6
  12. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +17 -6
  13. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +52 -14
  14. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  15. npcsh/npc_team/jinxs/{bin → lib/utils}/jinxs.jinx +12 -12
  16. npcsh/npc_team/jinxs/{bin → lib/utils}/models.jinx +7 -7
  17. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +6 -6
  18. npcsh/npc_team/jinxs/modes/alicanto.jinx +1633 -295
  19. npcsh/npc_team/jinxs/modes/arxiv.jinx +5 -5
  20. npcsh/npc_team/jinxs/modes/build.jinx +378 -0
  21. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  22. npcsh/npc_team/jinxs/modes/convene.jinx +597 -0
  23. npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
  24. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  25. {npcsh-1.1.20.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/kg.jinx +82 -15
  26. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  27. npcsh/npc_team/jinxs/{bin → modes}/nql.jinx +10 -21
  28. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  29. npcsh/npc_team/jinxs/modes/plonk.jinx +503 -308
  30. npcsh/npc_team/jinxs/modes/reattach.jinx +3 -3
  31. npcsh/npc_team/jinxs/modes/spool.jinx +3 -3
  32. npcsh/npc_team/jinxs/{bin → modes}/team.jinx +12 -12
  33. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  34. npcsh/npc_team/jinxs/modes/wander.jinx +454 -181
  35. npcsh/npc_team/jinxs/modes/yap.jinx +630 -182
  36. npcsh/npc_team/kadiefa.npc +2 -1
  37. npcsh/npc_team/sibiji.npc +3 -3
  38. npcsh/npcsh.py +112 -47
  39. npcsh/routes.py +4 -1
  40. npcsh/salmon_simulation.py +0 -0
  41. npcsh-1.1.22.data/data/npcsh/npc_team/alicanto.jinx +1694 -0
  42. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.npc +12 -6
  43. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/arxiv.jinx +5 -5
  44. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  45. npcsh-1.1.22.data/data/npcsh/npc_team/build.jinx +378 -0
  46. npcsh-1.1.22.data/data/npcsh/npc_team/compress.jinx +428 -0
  47. npcsh-1.1.22.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  48. npcsh-1.1.22.data/data/npcsh/npc_team/corca.jinx +820 -0
  49. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.npc +0 -1
  50. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/db_search.jinx +17 -6
  51. npcsh-1.1.22.data/data/npcsh/npc_team/edit_file.jinx +119 -0
  52. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/file_search.jinx +17 -6
  53. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic.npc +2 -3
  54. npcsh-1.1.22.data/data/npcsh/npc_team/git.jinx +795 -0
  55. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/jinxs.jinx +12 -12
  56. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.npc +2 -1
  57. {npcsh/npc_team/jinxs/bin → npcsh-1.1.22.data/data/npcsh/npc_team}/kg.jinx +82 -15
  58. npcsh-1.1.22.data/data/npcsh/npc_team/memories.jinx +414 -0
  59. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/models.jinx +7 -7
  60. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/nql.jinx +10 -21
  61. npcsh-1.1.22.data/data/npcsh/npc_team/papers.jinx +578 -0
  62. npcsh-1.1.22.data/data/npcsh/npc_team/plonk.jinx +574 -0
  63. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/reattach.jinx +3 -3
  64. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/setup.jinx +6 -6
  65. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.npc +3 -3
  66. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.jinx +3 -3
  67. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/team.jinx +12 -12
  68. npcsh-1.1.22.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  69. npcsh-1.1.22.data/data/npcsh/npc_team/wander.jinx +728 -0
  70. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/web_search.jinx +52 -14
  71. npcsh-1.1.22.data/data/npcsh/npc_team/yap.jinx +716 -0
  72. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/METADATA +246 -281
  73. npcsh-1.1.22.dist-info/RECORD +240 -0
  74. npcsh-1.1.22.dist-info/entry_points.txt +11 -0
  75. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -300
  76. npcsh/npc_team/jinxs/bin/memories.jinx +0 -317
  77. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  78. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -418
  79. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  80. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  81. npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
  82. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  83. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  84. npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
  85. npcsh/npc_team/plonkjr.npc +0 -23
  86. npcsh-1.1.20.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  87. npcsh-1.1.20.data/data/npcsh/npc_team/build.jinx +0 -65
  88. npcsh-1.1.20.data/data/npcsh/npc_team/compress.jinx +0 -140
  89. npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +0 -300
  90. npcsh-1.1.20.data/data/npcsh/npc_team/corca.jinx +0 -430
  91. npcsh-1.1.20.data/data/npcsh/npc_team/edit_file.jinx +0 -97
  92. npcsh-1.1.20.data/data/npcsh/npc_team/kg_search.jinx +0 -418
  93. npcsh-1.1.20.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  94. npcsh-1.1.20.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  95. npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +0 -317
  96. npcsh-1.1.20.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  97. npcsh-1.1.20.data/data/npcsh/npc_team/plonk.jinx +0 -379
  98. npcsh-1.1.20.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  99. npcsh-1.1.20.data/data/npcsh/npc_team/search.jinx +0 -54
  100. npcsh-1.1.20.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  101. npcsh-1.1.20.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  102. npcsh-1.1.20.data/data/npcsh/npc_team/wander.jinx +0 -455
  103. npcsh-1.1.20.data/data/npcsh/npc_team/yap.jinx +0 -268
  104. npcsh-1.1.20.dist-info/RECORD +0 -248
  105. npcsh-1.1.20.dist-info/entry_points.txt +0 -25
  106. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  107. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  108. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  109. /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
  110. /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
  111. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  112. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  113. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  114. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.png +0 -0
  115. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  116. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  117. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/chat.jinx +0 -0
  118. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/click.jinx +0 -0
  119. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  120. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  121. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  122. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  123. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compile.jinx +0 -0
  124. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  125. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/convene.jinx +0 -0
  126. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.png +0 -0
  127. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca_example.png +0 -0
  128. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  129. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  130. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic4.png +0 -0
  131. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.jinx +0 -0
  132. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.npc +0 -0
  133. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.png +0 -0
  134. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/help.jinx +0 -0
  135. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  136. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/init.jinx +0 -0
  137. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  138. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  139. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  140. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  141. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  142. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  143. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/notify.jinx +0 -0
  144. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  145. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  146. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  147. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  148. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/ots.jinx +0 -0
  149. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/paste.jinx +0 -0
  150. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.npc +0 -0
  151. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.png +0 -0
  152. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  153. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/pti.jinx +0 -0
  154. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/python.jinx +0 -0
  155. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  156. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/roll.jinx +0 -0
  157. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  158. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sample.jinx +0 -0
  159. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  160. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  161. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/serve.jinx +0 -0
  162. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/set.jinx +0 -0
  163. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sh.jinx +0 -0
  164. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/shh.jinx +0 -0
  165. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.png +0 -0
  166. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  167. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  168. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.png +0 -0
  169. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sql.jinx +0 -0
  170. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch.jinx +0 -0
  171. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  172. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  173. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switches.jinx +0 -0
  174. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sync.jinx +0 -0
  175. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  176. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  177. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  178. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/usage.jinx +0 -0
  179. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  180. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wait.jinx +0 -0
  181. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  182. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/yap.png +0 -0
  183. {npcsh-1.1.20.data → npcsh-1.1.22.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  184. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/WHEEL +0 -0
  185. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/licenses/LICENSE +0 -0
  186. {npcsh-1.1.20.dist-info → npcsh-1.1.22.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  jinx_name: web_search
2
2
  description: Search the web with interactive TUI
3
+ interactive: true
3
4
  inputs:
4
5
  - query: ""
5
6
  - provider: ""
@@ -54,18 +55,50 @@ steps:
54
55
  try:
55
56
  raw_results = search_web(query, provider=provider, num_results=num_results)
56
57
 
57
- # Normalize results
58
+ # Normalize results - handle different provider formats
58
59
  results = []
59
60
  if raw_results:
60
- for res in raw_results:
61
- if isinstance(res, dict):
61
+ # Perplexity returns [answer_text, [url1, url2, ...]]
62
+ if (len(raw_results) == 2
63
+ and isinstance(raw_results[0], str)
64
+ and isinstance(raw_results[1], (list, tuple))):
65
+ answer_text = raw_results[0]
66
+ citations = raw_results[1]
67
+ # First result: the AI answer
68
+ results.append({
69
+ 'title': 'AI Answer',
70
+ 'url': '',
71
+ 'snippet': answer_text[:500]
72
+ })
73
+ # Then each citation as a result
74
+ for url in citations:
75
+ url_str = str(url).strip()
76
+ try:
77
+ from urllib.parse import urlparse
78
+ domain = urlparse(url_str).netloc
79
+ except:
80
+ domain = url_str[:40]
62
81
  results.append({
63
- 'title': res.get('title', 'No title'),
64
- 'url': res.get('url', res.get('link', '')),
65
- 'snippet': res.get('snippet', res.get('description', ''))
82
+ 'title': domain or url_str[:60],
83
+ 'url': url_str,
84
+ 'snippet': ''
66
85
  })
67
- else:
68
- results.append({'title': str(res)[:50], 'url': str(res), 'snippet': ''})
86
+ else:
87
+ for res in raw_results:
88
+ if isinstance(res, dict):
89
+ results.append({
90
+ 'title': res.get('title', 'No title'),
91
+ 'url': res.get('url', res.get('link', '')),
92
+ 'snippet': res.get('snippet', res.get('description', res.get('content', '')))
93
+ })
94
+ elif isinstance(res, str):
95
+ # Could be a URL or text
96
+ if res.startswith('http'):
97
+ results.append({'title': res[:60], 'url': res, 'snippet': ''})
98
+ else:
99
+ results.append({'title': res[:60], 'url': '', 'snippet': res[:200]})
100
+ else:
101
+ results.append({'title': str(res)[:60], 'url': str(res), 'snippet': ''})
69
102
 
70
103
  if not results:
71
104
  context['output'] = "No web results found."
@@ -128,7 +161,7 @@ steps:
128
161
  header = f" WEB SEARCH ({len(results)} results): '{query}' "
129
162
  else:
130
163
  header = f" PREVIEW: {results[selected]['title'][:width-12]} "
131
- sys.stdout.write(f'\033[44;37;1m{header.ljust(width)}\033[0m\n')
164
+ sys.stdout.write(f'\033[7;1m{header.ljust(width)}\033[0m\n')
132
165
 
133
166
  # Column headers
134
167
  if mode == 'list':
@@ -162,7 +195,7 @@ steps:
162
195
  sel = results[selected] if results else {}
163
196
  snippet = (sel.get('snippet') or '')[:width-2]
164
197
  sys.stdout.write(f'\033[{height-1};1H\033[K {snippet}'.ljust(width))
165
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Nav p:Preview o:Open i:Incog c:Copy q:Quit [{selected+1}/{len(results)}] \033[0m')
198
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Nav p:Preview o:Open i:Incog c:Copy q:Quit [{selected+1}/{len(results)}] \033[0m')
166
199
 
167
200
  else: # preview mode
168
201
  for i in range(list_height):
@@ -173,16 +206,21 @@ steps:
173
206
 
174
207
  sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
175
208
  sys.stdout.write(f'\033[{height-1};1H\033[K [{preview_scroll+1}/{len(preview_content)} lines]')
176
- sys.stdout.write(f'\033[{height};1H\033[K\033[44;37m j/k:Scroll b:Back o:Open c:Copy q:Quit \033[0m')
209
+ sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Scroll b:Back o:Open c:Copy q:Quit \033[0m')
177
210
 
178
211
  sys.stdout.flush()
179
212
 
180
- c = sys.stdin.read(1)
213
+ c = os.read(fd, 1).decode('latin-1')
181
214
 
182
215
  if c == '\x1b':
183
- c2 = sys.stdin.read(1)
216
+ import select as _sel
217
+ if _sel.select([fd], [], [], 0.05)[0]:
218
+ c2 = os.read(fd, 1).decode('latin-1')
219
+ else:
220
+ context['output'] = "Cancelled."
221
+ break
184
222
  if c2 == '[':
185
- c3 = sys.stdin.read(1)
223
+ c3 = os.read(fd, 1).decode('latin-1')
186
224
  if c3 == 'A':
187
225
  if mode == 'list' and selected > 0:
188
226
  selected -= 1