bare-tui 0.0.2 → 0.0.3

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,4 +1,4 @@
1
- // filepicker — browse a filesystem and pick a file.
1
+ // filepicker — browse a filesystem and pick a file (or a directory).
2
2
  //
3
3
  // Dependency-injected so the framework core stays filesystem-free: pass your
4
4
  // own { fs, path } to create(), or let it lazily require('bare-fs') /
@@ -6,12 +6,18 @@
6
6
  // filepicker, so consumers who don't use it never pull those modules in.
7
7
  //
8
8
  // const fp = filepicker.create({ height: 12 }) // real fs
9
+ // const fp = filepicker.create({ pick: 'dir' }) // choose a directory
9
10
  // const fp = filepicker.create({ ...filepicker.mock(tree), cwd: '/' }) // tests
10
11
  //
12
+ // `pick` ('file' default, or 'dir') decides what enter selects. In 'file' mode
13
+ // enter/→/l opens a directory or selects a file. In 'dir' mode →/l descends into
14
+ // the highlighted directory while enter SELECTS it; files are shown dimmed and
15
+ // can't be chosen.
16
+ //
11
17
  // Directory reads happen through Cmds (async), surfacing as Msgs:
12
18
  // { type: 'filepicker.entries', dir, entries }
13
19
  // { type: 'filepicker.error', dir, error }
14
- // { type: 'filepicker.select', path } // a file was chosen
20
+ // { type: 'filepicker.select', path } // a file (or directory) was chosen
15
21
  //
16
22
  // The only fs surface used is fs.readdir(dir, { withFileTypes: true }, cb); the
17
23
  // only path surface is path.join and path.dirname. The mock implements exactly
@@ -27,6 +33,8 @@ const keys = {
27
33
  up: key.binding({ keys: ['up', 'k'], help: { key: '↑/k', desc: 'up' } }),
28
34
  down: key.binding({ keys: ['down', 'j'], help: { key: '↓/j', desc: 'down' } }),
29
35
  open: key.binding({ keys: ['enter', 'right', 'l'], help: { key: '↵', desc: 'open' } }),
36
+ descend: key.binding({ keys: ['right', 'l'], help: { key: '→/l', desc: 'open dir' } }),
37
+ choose: key.binding({ keys: ['enter'], help: { key: '↵', desc: 'select' } }),
30
38
  back: key.binding({ keys: ['backspace', 'left', 'h'], help: { key: '⌫', desc: 'up dir' } })
31
39
  }
32
40
 
@@ -55,6 +63,7 @@ class FilePicker {
55
63
  this.cwd = opts.cwd
56
64
  this.height = opts.height || 12
57
65
  this.showHidden = !!opts.showHidden
66
+ this.pick = opts.pick === 'dir' ? 'dir' : 'file'
58
67
 
59
68
  this.entries = []
60
69
  this.cursor = 0
@@ -113,10 +122,32 @@ class FilePicker {
113
122
  }
114
123
 
115
124
  _key(msg) {
116
- if (key.matches(msg, keys.up)) this._move(-1)
117
- else if (key.matches(msg, keys.down)) this._move(1)
118
- else if (key.matches(msg, keys.back)) return this._open(this.path.dirname(this.cwd))
119
- else if (key.matches(msg, keys.open)) {
125
+ if (key.matches(msg, keys.up)) {
126
+ this._move(-1)
127
+ return [this, null]
128
+ }
129
+ if (key.matches(msg, keys.down)) {
130
+ this._move(1)
131
+ return [this, null]
132
+ }
133
+ if (key.matches(msg, keys.back)) return this._open(this.path.dirname(this.cwd))
134
+
135
+ // dir mode: →/l descends into the highlighted directory, enter selects it;
136
+ // files aren't selectable.
137
+ if (this.pick === 'dir') {
138
+ const entry = this.entries[this.cursor]
139
+ if (!entry || !entry.directory) return [this, null]
140
+ const full = this.path.join(this.cwd, entry.name)
141
+ if (key.matches(msg, keys.descend)) return this._open(full)
142
+ if (key.matches(msg, keys.choose)) {
143
+ this.selected = full
144
+ return [this, () => ({ type: 'filepicker.select', path: full })]
145
+ }
146
+ return [this, null]
147
+ }
148
+
149
+ // file mode: enter/→/l opens a directory or selects a file.
150
+ if (key.matches(msg, keys.open)) {
120
151
  const entry = this.entries[this.cursor]
121
152
  if (!entry) return [this, null]
122
153
  const full = this.path.join(this.cwd, entry.name)
@@ -159,7 +190,11 @@ class FilePicker {
159
190
  const entry = this.entries[p]
160
191
  const label = entry.directory ? entry.name + '/' : entry.name
161
192
  const text = (p === this.cursor ? '› ' : ' ') + label
162
- rows.push(p === this.cursor ? selectedStyle(text) : entry.directory ? dirStyle(text) : text)
193
+ // In dir mode files aren't selectable, so render them dimmed.
194
+ const plain = this.pick === 'dir' && !entry.directory ? dim(text) : text
195
+ rows.push(
196
+ p === this.cursor ? selectedStyle(text) : entry.directory ? dirStyle(text) : plain
197
+ )
163
198
  }
164
199
  }
165
200
  while (rows.length < this.height) rows.push('')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-tui",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "A little TUI framework for Bare, based on bubbletea",
5
5
  "main": "index.js",
6
6
  "exports": {