vappman 0.5__tar.gz → 0.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vappman
3
- Version: 0.5
3
+ Version: 0.7
4
4
  Summary: A visual wrapper for appman
5
5
  Author-email: Joe Defen <joedef@google.com>
6
6
  License: MIT
@@ -47,12 +47,25 @@ But it does NOT cover:
47
47
  * It presents some keys available on the top line.
48
48
  * Use '?' to learn the navigation keys (e.g., you can use the mouse wheel,
49
49
  arrow keys, and many `vi`-like keys)
50
+ * '?' also elaborates the meaning of the available keys for operations.
51
+ * NOTE: `ENTER` acts differently based on context:
52
+ * In help, it returns to the main menu.
53
+ * On an uninstalled app, it installs it.
54
+ * On an installed app, it uninstalls it.
50
55
  * Then `vappman` presents a list of installed apps, followed by available/uninstalled apps.
56
+ * Installed apps have prefix '✔✔✔' (i.e., three checks).
57
+ * Uninstalled apps have prefix '◆' (i.e., a solid diamond).
51
58
  * Enter `/` to enter a "filter" for installed/uninstalled apps, if you wish.
52
- * If you enter plain old "words", then those words must match the beginning of words
53
- of the apps or descriptions (in order, but not contiguously).
54
- * Or you can enter an regular expression acceptable to python (e.g., `\b` means word
55
- boundary, etc.)
59
+ * If you enter plain ole "words", then those words must match:
60
+ * the start of words on the apps line (in order, but not contiguously) and/or
61
+ * the start of the remainder of the previous word match
62
+ (i.e., `/bit fight` matches `bitfighter`).
63
+ * Or you can enter an regular expression acceptable to python; e.g.,
64
+ * `^` matches the line starting with the app name
65
+ * `\b` matches a word boundary; and so forth.
66
+ * NOTES:
67
+ * `ESC` clears the filter and jumps to the top of the listing.
68
+ * Each time the filter is changed, the position jumps to the top of the listing.
56
69
  * Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
57
70
 
58
71
  ## Example Screenshot
@@ -61,9 +74,13 @@ But it does NOT cover:
61
74
  ---
62
75
 
63
76
  NOTES: in this example:
64
- * the filter is `card` so it shows apps with words starting with `card`.
65
- * the current position is on `glabels`; thus if `i` is typed, `appman install glabels` is run.
77
+ * the filter is `card` so it shows app lines with words starting with `card`.
78
+ * the reverse video, current position is on `glabels`;
79
+ thus if `i` (or ENTER) is typed, `appman install glabels` is run.
66
80
  * if the horizontal line (second line show) has no decorations, then you are looking
67
81
  all the filtered apps; otherwise, the decoration suggests where you are in the
68
82
  partial view of the filtered apps.
83
+ * the matching installed app has the '✔✔✔' prefix.
84
+ * the fixed top line shows some of the available action keys (e.g., `q` quits the app)
85
+ * use `?` to open the help screen describing all keys (including navigation)
69
86
 
@@ -29,12 +29,25 @@ But it does NOT cover:
29
29
  * It presents some keys available on the top line.
30
30
  * Use '?' to learn the navigation keys (e.g., you can use the mouse wheel,
31
31
  arrow keys, and many `vi`-like keys)
32
+ * '?' also elaborates the meaning of the available keys for operations.
33
+ * NOTE: `ENTER` acts differently based on context:
34
+ * In help, it returns to the main menu.
35
+ * On an uninstalled app, it installs it.
36
+ * On an installed app, it uninstalls it.
32
37
  * Then `vappman` presents a list of installed apps, followed by available/uninstalled apps.
38
+ * Installed apps have prefix '✔✔✔' (i.e., three checks).
39
+ * Uninstalled apps have prefix '◆' (i.e., a solid diamond).
33
40
  * Enter `/` to enter a "filter" for installed/uninstalled apps, if you wish.
34
- * If you enter plain old "words", then those words must match the beginning of words
35
- of the apps or descriptions (in order, but not contiguously).
36
- * Or you can enter an regular expression acceptable to python (e.g., `\b` means word
37
- boundary, etc.)
41
+ * If you enter plain ole "words", then those words must match:
42
+ * the start of words on the apps line (in order, but not contiguously) and/or
43
+ * the start of the remainder of the previous word match
44
+ (i.e., `/bit fight` matches `bitfighter`).
45
+ * Or you can enter an regular expression acceptable to python; e.g.,
46
+ * `^` matches the line starting with the app name
47
+ * `\b` matches a word boundary; and so forth.
48
+ * NOTES:
49
+ * `ESC` clears the filter and jumps to the top of the listing.
50
+ * Each time the filter is changed, the position jumps to the top of the listing.
38
51
  * Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
39
52
 
40
53
  ## Example Screenshot
@@ -43,9 +56,13 @@ But it does NOT cover:
43
56
  ---
44
57
 
45
58
  NOTES: in this example:
46
- * the filter is `card` so it shows apps with words starting with `card`.
47
- * the current position is on `glabels`; thus if `i` is typed, `appman install glabels` is run.
59
+ * the filter is `card` so it shows app lines with words starting with `card`.
60
+ * the reverse video, current position is on `glabels`;
61
+ thus if `i` (or ENTER) is typed, `appman install glabels` is run.
48
62
  * if the horizontal line (second line show) has no decorations, then you are looking
49
63
  all the filtered apps; otherwise, the decoration suggests where you are in the
50
64
  partial view of the filtered apps.
65
+ * the matching installed app has the '✔✔✔' prefix.
66
+ * the fixed top line shows some of the available action keys (e.g., `q` quits the app)
67
+ * use `?` to open the help screen describing all keys (including navigation)
51
68
 
@@ -35,7 +35,7 @@
35
35
  # PUBLIC Build and deploy (from project directory):
36
36
  # ## BUMP the version (below in [project])
37
37
  # rm -rf dist; python3 -m build; ls dist/.
38
- # python3 -m twine upload dist/* # keyring --disable # may be required
38
+ # python3 -m twine upload dist/* # keyring --disable # may be required
39
39
  # ## Enter __token__ and the saved TOKEN (in bitwarden)
40
40
  # pipx upgrade pwr-tray || pipx install pwr-tray # >= python3.11
41
41
  # --OR-- sudo python3 -m pip install pwr-tray # <= python3.10
@@ -55,7 +55,7 @@ build-backend = "setuptools.build_meta"
55
55
 
56
56
  [project]
57
57
  name = "vappman"
58
- version = "0.5"
58
+ version = "0.7"
59
59
  description = "A visual wrapper for appman"
60
60
  authors = [
61
61
  { name = "Joe Defen", email = "joedef@google.com" }
@@ -6,7 +6,7 @@ Interactive, visual thin layer atop appman
6
6
  # pylint: disable=broad-exception-caught,consider-using-with
7
7
  # pylint: disable=too-many-instance-attributes,too-many-branches
8
8
  # pylint: disable=too-many-return-statements,too-many-statements
9
-
9
+ # pylint: disable=consider-using-in
10
10
  # pylint: disable=wrong-import-position,disable=wrong-import-order
11
11
  # import VirtEnv
12
12
  # VirtEnv.ensure_venv(__name__)
@@ -17,10 +17,7 @@ import re
17
17
  import shutil
18
18
  import subprocess
19
19
  import traceback
20
- import copy
21
- import shutil
22
20
  import curses as cs
23
- from types import SimpleNamespace
24
21
  from vappman.PowerWindow import Window, OptionSpinner
25
22
 
26
23
 
@@ -33,21 +30,20 @@ class Vappman:
33
30
  assert not Vappman.singleton
34
31
  Vappman.singleton = self
35
32
 
36
- self.summary_lines = []
37
- self.new_summary_lines = True
38
-
39
33
  spin = self.spin = OptionSpinner()
40
34
  spin.add_key('help_mode', '? - toggle help screen', vals=[False, True])
41
35
 
42
36
  # EXPAND
43
37
  other = 'airbou/qscU'
44
38
  other_keys = set(ord(x) for x in other)
39
+ other_keys.add(cs.KEY_ENTER)
40
+ other_keys.add(27) # ESCAPE
41
+ other_keys.add(10) # another form of ENTER
45
42
  self.opts = spin.default_obj
46
43
 
47
44
  self.actions = {} # currently available actions
48
45
  self.pick_app = '' # current picked app
49
46
  self.pick_is_installed = False
50
- # self.verbs = {'i': 'install', 'r': 'remove', 'v': 'view',}
51
47
  self.prev_filter = '' # string
52
48
  self.filter = None # compiled pattern
53
49
  self.check_preqreqs()
@@ -95,7 +91,7 @@ class Vappman:
95
91
 
96
92
  def get_installed(self):
97
93
  """ Get the list of lines of installed apps """
98
- rv = self.cmd_dict('appman files --by-name')
94
+ rv = self.cmd_dict('appman files --byname')
99
95
  return rv
100
96
 
101
97
  def main_loop(self):
@@ -116,6 +112,8 @@ class Vappman:
116
112
  ' c - clean (remove unneeded files/folters)',
117
113
  ' U - update ALL installed apps',
118
114
  ' / - filter apps',
115
+ ' ENTER = install, remove, or return from help',
116
+ ' ESC = clear filter and jump to top',
119
117
  'CONTEXT SENSITIVE:',
120
118
  ' i - install uninstalled app',
121
119
  ' r - remove installed app',
@@ -133,15 +131,15 @@ class Vappman:
133
131
  # self.win.set_pick_mode(self.opts.pick_mode, self.opts.pick_size)
134
132
  self.win.set_pick_mode(True)
135
133
  self.win.add_header(self.get_keys_line(), attr=cs.A_BOLD)
136
- for line in self.installs.values():
137
- if wanted(line):
134
+ for app, line in self.installs.items():
135
+ if app in self.apps:
136
+ line = self.apps[app]
137
+ if wanted(line[2:]):
138
+ line = '✔✔✔' + line[1:]
138
139
  self.win.add_body(line)
139
140
  for app, line in self.apps.items():
140
- if app not in self.installs and wanted(line):
141
+ if app not in self.installs and wanted(line[2:]):
141
142
  self.win.add_body(line)
142
- # if self.new_summary_lines:
143
- # self.win.pick_pos = self.win.scroll_pos = 0
144
- # self.new_summary_lines = False
145
143
  self.win.render()
146
144
 
147
145
  _ = self.do_key(self.win.prompt(seconds=300))
@@ -150,15 +148,17 @@ class Vappman:
150
148
  def get_keys_line(self):
151
149
  """ TBD """
152
150
  # EXPAND
153
- filt = self.prev_filter if self.prev_filter else '{No-Filt}'
154
- line = 'KEYS:'
151
+ line = ''
155
152
  for key, verb in self.actions.items():
156
- line += f' {key}:{verb}'
153
+ if key[0] == verb[0]:
154
+ line += f' {verb}'
155
+ else:
156
+ line += f' {key}:{verb}'
157
157
  # or EXPAND
158
- line += f' ?:help q:quit a:about s:sync c:clean U:upd /{filt} '
158
+ line += f' ?:help quit about sync clean Upd /{self.prev_filter} '
159
159
  # for action in self.actions:
160
160
  # line += f' {action[0]}:{action}'
161
- return line
161
+ return line[1:]
162
162
 
163
163
  def get_actions(self, line):
164
164
  """ Determine the type of the current line and available commands."""
@@ -186,54 +186,56 @@ class Vappman:
186
186
  """
187
187
  this = Vappman.singleton
188
188
  this.pick_app, this.actions = this.get_actions(line)
189
- keys_line = this.get_keys_line().ljust(this.win.get_pad_width())
190
- this.win.head.pad.addstr(0, 0, keys_line, cs.A_BOLD)
191
-
189
+ header = this.get_keys_line()
190
+ # ASSUME line ends in /....
191
+ parts = header.split('/', maxsplit=1)
192
+ wds = parts[0].split()
193
+ this.win.head.pad.move(0, 0)
194
+ for wd in wds:
195
+ if wd:
196
+ this.win.add_header(wd[0], attr=cs.A_BOLD|cs.A_UNDERLINE, resume=True)
197
+ if wd[1:]:
198
+ this.win.add_header(wd[1:] + ' ', resume=True)
199
+
200
+ this.win.add_header('/', attr=cs.A_BOLD+cs.A_UNDERLINE, resume=True)
201
+ if len(parts) > 1 and parts[1]:
202
+ this.win.add_header(f'{parts[1]}', resume=True)
203
+ _, col = this.win.head.pad.getyx()
204
+ pad = ' ' * (this.win.get_pad_width()-col)
205
+ this.win.add_header(pad, resume=True)
192
206
  return line
193
- # #IF WE WANT TO DO SOMETHING ON THE LINE...
194
- # suffix = ''
195
- # for action in actions:
196
- # suffix += f' {action[0]}:{action}'
197
- # block_char = "\u2588"
198
- # suffix = f'{block_char*5} {suffix}'
199
- # over = len(line) + len(suffix) - this.win.get_pad_width()
200
- # if over < 0:
201
- # line += ' '*(-over)
202
- # elif over > 0:
203
- # line = line[0:-over]
204
-
205
- # return line + suffix
207
+
206
208
  def run_appman(self, cmd):
209
+ """ Run a 'appman' command """
207
210
  Window.stop_curses()
208
211
  os.system(f'clear; stty sane; {cmd};'
209
- + ' echo -e "\n\nHit ENTER to return to menu"; read FOO')
212
+ + r' /bin/echo -e "\n\n===== Press ENTER for menu ====> \c"; read FOO')
210
213
  self.installs = self.get_installed()
211
214
  Window._start_curses()
212
215
 
213
216
  def do_key(self, key):
214
217
  """ TBD """
215
- def dashes(ns, ok=True):
216
- try:
217
- # use checkmark or circle-backslash
218
- filler = ('\u2713' if ok else '\u2342') * (ns.end - ns.start)
219
- line = self.summary_lines[self.win.pick_pos]
220
- line = line[0:ns.start] + filler + line[ns.end:]
221
- self.summary_lines[self.win.pick_pos] = line
222
- self.win.draw(self.win.pick_pos, 0, line, width=self.win.get_pad_width(), header=False)
223
- self.win.fix_positions(delta=1)
224
- self.win.render()
225
- except Exception:
226
- pass
227
-
228
218
  if not key:
229
219
  return True
220
+ if key == cs.KEY_ENTER or key == 10: # Handle ENTER
221
+ if self.opts.help_mode:
222
+ self.opts.help_mode = False
223
+ return True
224
+ if self.pick_is_installed:
225
+ key = ord('r') # removed installed app
226
+ else:
227
+ key = ord('i') # install uninstalled app
228
+
230
229
  if key in self.spin.keys:
231
230
  value = self.spin.do_key(key, self.win)
232
- if key in (ord('p'), ord('s')):
233
- self.win.set_pick_mode(on=self.opts.pick_mode, pick_size=self.opts.pick_size)
234
- elif key == ord('n'):
235
- self.win.alert(title='Info', message=f'got: {value}')
236
231
  return value
232
+
233
+ if key == 27: # ESCAPE
234
+ self.prev_filter = ''
235
+ self.filter = None
236
+ self.win.pick_pos = 0
237
+ return None
238
+
237
239
  if key == ord('q'):
238
240
  self.win.stop_curses()
239
241
  os.system('clear; stty sane')
@@ -260,11 +262,12 @@ class Vappman:
260
262
  if key == ord('u'):
261
263
  self.run_appman(f'appman update {self.pick_app}')
262
264
  if key == ord('U'):
263
- self.run_appman(f'appman update')
265
+ self.run_appman('appman update')
264
266
  # EXPAND
265
267
 
266
268
  if key == ord('/'):
267
269
  # pylint: disable=protected-access
270
+ start_filter = self.prev_filter
268
271
 
269
272
  prefix = ''
270
273
  while True:
@@ -274,18 +277,22 @@ class Vappman:
274
277
  pattern.strip()
275
278
  if not pattern:
276
279
  self.filter = None
277
- return None
280
+ break
278
281
 
279
282
  try:
280
283
  if re.match(r'^[\-\w\s]*$', pattern):
281
284
  words = pattern.split()
282
- self.filter = re.compile(r'\b' + r'\b.*'.join(words), re.IGNORECASE)
283
- return None
285
+ self.filter = re.compile(r'\b' + r'(|.*\b)'.join(words), re.IGNORECASE)
286
+ break
284
287
  self.filter = re.compile(pattern, re.IGNORECASE)
285
- return None
288
+ break
286
289
  except Exception:
287
290
  prefix = 'Bad regex: '
288
291
 
292
+ if start_filter != self.prev_filter:
293
+ # when filter changes, move to top
294
+ self.win.pick_pos = 0
295
+
289
296
  return None
290
297
  return None
291
298
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vappman
3
- Version: 0.5
3
+ Version: 0.7
4
4
  Summary: A visual wrapper for appman
5
5
  Author-email: Joe Defen <joedef@google.com>
6
6
  License: MIT
@@ -47,12 +47,25 @@ But it does NOT cover:
47
47
  * It presents some keys available on the top line.
48
48
  * Use '?' to learn the navigation keys (e.g., you can use the mouse wheel,
49
49
  arrow keys, and many `vi`-like keys)
50
+ * '?' also elaborates the meaning of the available keys for operations.
51
+ * NOTE: `ENTER` acts differently based on context:
52
+ * In help, it returns to the main menu.
53
+ * On an uninstalled app, it installs it.
54
+ * On an installed app, it uninstalls it.
50
55
  * Then `vappman` presents a list of installed apps, followed by available/uninstalled apps.
56
+ * Installed apps have prefix '✔✔✔' (i.e., three checks).
57
+ * Uninstalled apps have prefix '◆' (i.e., a solid diamond).
51
58
  * Enter `/` to enter a "filter" for installed/uninstalled apps, if you wish.
52
- * If you enter plain old "words", then those words must match the beginning of words
53
- of the apps or descriptions (in order, but not contiguously).
54
- * Or you can enter an regular expression acceptable to python (e.g., `\b` means word
55
- boundary, etc.)
59
+ * If you enter plain ole "words", then those words must match:
60
+ * the start of words on the apps line (in order, but not contiguously) and/or
61
+ * the start of the remainder of the previous word match
62
+ (i.e., `/bit fight` matches `bitfighter`).
63
+ * Or you can enter an regular expression acceptable to python; e.g.,
64
+ * `^` matches the line starting with the app name
65
+ * `\b` matches a word boundary; and so forth.
66
+ * NOTES:
67
+ * `ESC` clears the filter and jumps to the top of the listing.
68
+ * Each time the filter is changed, the position jumps to the top of the listing.
56
69
  * Use `i` to install apps, and `r` to remove apps. When you install or remove an app, `appman` drops out of `curses` mode, runs the `appman` command so you can see the result, and then prompts your to hit ENTER to return to `vappman.
57
70
 
58
71
  ## Example Screenshot
@@ -61,9 +74,13 @@ But it does NOT cover:
61
74
  ---
62
75
 
63
76
  NOTES: in this example:
64
- * the filter is `card` so it shows apps with words starting with `card`.
65
- * the current position is on `glabels`; thus if `i` is typed, `appman install glabels` is run.
77
+ * the filter is `card` so it shows app lines with words starting with `card`.
78
+ * the reverse video, current position is on `glabels`;
79
+ thus if `i` (or ENTER) is typed, `appman install glabels` is run.
66
80
  * if the horizontal line (second line show) has no decorations, then you are looking
67
81
  all the filtered apps; otherwise, the decoration suggests where you are in the
68
82
  partial view of the filtered apps.
83
+ * the matching installed app has the '✔✔✔' prefix.
84
+ * the fixed top line shows some of the available action keys (e.g., `q` quits the app)
85
+ * use `?` to open the help screen describing all keys (including navigation)
69
86
 
File without changes
File without changes
File without changes
File without changes