@ssebastien/tcmd 0.1.2 → 0.1.4

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 (79) hide show
  1. package/README.md +121 -38
  2. package/bin/tcmd +11 -3
  3. package/bin/tcmd.js +15 -0
  4. package/package.json +4 -3
  5. package/public/index.html +14 -0
  6. package/public/main.js +17501 -0
  7. package/public/style.css +901 -0
  8. package/scripts/serve-web.js +61 -0
  9. package/src/js/tcmd/components/App.html +28 -12
  10. package/src/js/tcmd/components/App.js +135 -85
  11. package/src/js/tcmd/components/App.xml +64 -15
  12. package/src/js/tcmd/components/BookmarkList.html +3 -2
  13. package/src/js/tcmd/components/BookmarkList.js +1 -1
  14. package/src/js/tcmd/components/Breadcrumbs.js +1 -1
  15. package/src/js/tcmd/components/CommandArgRow.html +17 -0
  16. package/src/js/tcmd/components/CommandArgRow.js +86 -26
  17. package/src/js/tcmd/components/CommandArgRow.xml +31 -4
  18. package/src/js/tcmd/components/CommandBar.html +1 -1
  19. package/src/js/tcmd/components/CommandBar.js +3 -3
  20. package/src/js/tcmd/components/CommandItem.html +1 -1
  21. package/src/js/tcmd/components/CommandItem.js +1 -0
  22. package/src/js/tcmd/components/CommandPanel.html +29 -0
  23. package/src/js/tcmd/components/CommandPanel.js +54 -52
  24. package/src/js/tcmd/components/CommandPanel.xml +38 -24
  25. package/src/js/tcmd/components/Confirm.html +1 -1
  26. package/src/js/tcmd/components/EntryDetail.html +7 -7
  27. package/src/js/tcmd/components/EntryDetail.js +1 -1
  28. package/src/js/tcmd/components/FileList.html +5 -1
  29. package/src/js/tcmd/components/FileList.js +2 -1
  30. package/src/js/tcmd/components/FilePreview.html +14 -3
  31. package/src/js/tcmd/components/FilePreview.js +73 -10
  32. package/src/js/tcmd/components/FilePreview.xml +19 -6
  33. package/src/js/tcmd/components/FileRow.html +1 -1
  34. package/src/js/tcmd/components/FileRow.js +10 -3
  35. package/src/js/tcmd/components/FilterBar.html +1 -1
  36. package/src/js/tcmd/components/FilterBar.js +14 -4
  37. package/src/js/tcmd/components/FilterBar.xml +1 -1
  38. package/src/js/tcmd/{tui/frame.js → components/Frame.js} +120 -91
  39. package/src/js/tcmd/components/Help.html +7 -0
  40. package/src/js/tcmd/components/Help.js +109 -0
  41. package/src/js/tcmd/components/Summary.html +1 -1
  42. package/src/js/tcmd/components/Summary.js +1 -6
  43. package/src/js/tcmd/components/icons.js +39 -5
  44. package/src/js/tcmd/components/index.js +3 -4
  45. package/src/js/tcmd/components/rows.js +37 -33
  46. package/src/js/tcmd/core/commands.js +422 -180
  47. package/src/js/tcmd/core/directory.js +145 -95
  48. package/src/js/tcmd/core/fs.js +29 -2
  49. package/src/js/tcmd/core/history.js +58 -0
  50. package/src/js/tcmd/core/index.js +3 -9
  51. package/src/js/tcmd/core/keymap.js +3 -3
  52. package/src/js/tcmd/core/operations.js +108 -15
  53. package/src/js/tcmd/core/pane.js +31 -8
  54. package/src/js/tcmd/core/panel.js +172 -82
  55. package/src/js/tcmd/core/script.js +7 -95
  56. package/src/js/tcmd/core/selection.js +9 -1
  57. package/src/js/tcmd/core/session.js +280 -152
  58. package/src/js/tcmd/index.js +0 -1
  59. package/src/js/tcmd/tui/app.js +26 -5
  60. package/src/js/tcmd/tui/catalogue.js +6 -19
  61. package/src/js/tcmd/tui/index.js +1 -16
  62. package/src/js/tcmd/tui/theme.js +5 -0
  63. package/src/js/tcmd/web/app.js +158 -32
  64. package/src/js/tcmd/web/bookmarks.js +36 -0
  65. package/src/js/tcmd/web/catalogue.js +45 -39
  66. package/src/js/tcmd/web/effects.js +143 -0
  67. package/src/js/tcmd/web/header.html +1 -1
  68. package/src/js/tcmd/web/header.js +15 -11
  69. package/src/js/tcmd/web/main.js +6 -66
  70. package/src/js/tcmd/web/seed.js +3 -3
  71. package/src/js/tcmd/web/server.js +6 -4
  72. package/src/js/tcmd/web/theme.js +1 -0
  73. package/src/js/tcmd/components/CommandPalette.html +0 -7
  74. package/src/js/tcmd/components/CommandPalette.js +0 -58
  75. package/src/js/tcmd/components/CommandPalette.xml +0 -16
  76. package/src/js/tcmd/components/PaletteRow.html +0 -3
  77. package/src/js/tcmd/components/PaletteRow.js +0 -43
  78. package/src/js/tcmd/components/PaletteRow.xml +0 -4
  79. package/src/js/tcmd/tui/help.js +0 -32
@@ -49,18 +49,13 @@ function summaryShortDisk(disk) {
49
49
  function createBehaviors(env) {
50
50
  const measure = env.measure ?? ((text) => `${text}`.length)
51
51
  return {
52
- line: (self, data) => {
53
- const text = summaryText(unwrap(data.entries), unwrap(data.disk))
54
- const width = env.width ? env.width(self) : 0
55
- return env.elide ? env.elide(text, width) : text
56
- },
57
52
  lineFg: () => env.lineFg,
58
53
  // TUI: degrade by information value instead of chopping mid-token:
59
54
  // full line -> short disk -> counts only -> elided counts.
60
55
  priorityLine: (self, data) => {
61
56
  const entries = unwrap(data.entries)
62
57
  const disk = unwrap(data.disk)
63
- const width = env.width ? env.width(self) : 0
58
+ const width = env.width ? env.width(self, data) : 0
64
59
  const full = summaryText(entries, disk)
65
60
  if (!width || measure(full) <= width) return full
66
61
  const counts = `${INDENT}${summaryCounts(entries)}`
@@ -4,11 +4,12 @@
4
4
  // Created: 2026-10-09
5
5
 
6
6
  // Module: tcmd/components/icons
7
- // Nerd Font icon for a file entry, in the spirit of yazi: a kind icon
8
- // (directory, symlink, device node) wins, then a per-extension icon, then the
9
- // generic file icon. A few basenames map directly too, so `Makefile`,
7
+ // Nerd Font icons, in the spirit of yazi. `iconFor` maps a file entry to its
8
+ // kind icon (directory, symlink, device node), then a per-extension icon, then
9
+ // the generic file icon; a few basenames map directly too, so `Makefile`,
10
10
  // `Dockerfile` and dotfiles like `.gitignore` get their icon even without a
11
- // distinguishing extension.
11
+ // distinguishing extension. `sourceMarker` maps a command-panel entry's
12
+ // namespace (a CLI operation or a TinyCmd action) to its marker.
12
13
  //
13
14
  // The glyphs are Nerd Font codepoints (from `glyphnames.json`), so they only
14
15
  // render on a terminal patched with a Nerd Font: rows show them when the
@@ -27,6 +28,20 @@ const SYMLINK_ICON = "\u{f0c1}" // nf-fa-link
27
28
  const DEVICE_ICON = "\u{e795}" // nf-dev-terminal
28
29
  const FILE_ICON = "\u{f016}" // nf-fa-file_o
29
30
 
31
+ // Constant: SOURCE_ICON / SOURCE_TAG
32
+ // The namespace marker the command panel puts in front of each entry name:
33
+ // a terminal prompt for a CLI operation, a bolt for a TinyCmd action. Browsers
34
+ // lack the Nerd Font (and `TCMD_ICONS=0` opts out), so the same marker exists
35
+ // as a three-character tag that keeps the name column aligned either way.
36
+ const SOURCE_ICON = {
37
+ cli: "\u{ebca}", // nf-cod-terminal_bash
38
+ app: "\u{f0e7}", // nf-fa-bolt
39
+ }
40
+ const SOURCE_TAG = {
41
+ cli: "cli",
42
+ app: "app",
43
+ }
44
+
30
45
  // Constant: NAME_ICONS
31
46
  // Basename (lowercase, leading dot stripped) → glyph, checked before the
32
47
  // extension table.
@@ -180,6 +195,25 @@ function iconsEnabled(environment = globalThis.process?.env ?? {}) {
180
195
  return value !== "0" && value !== "false" && value !== "off"
181
196
  }
182
197
 
183
- export { DEVICE_ICON, DIR_ICON, FILE_ICON, SYMLINK_ICON, extensionOf, iconFor, iconsEnabled }
198
+ // Function: sourceMarker
199
+ // The marker a command-panel entry carries: the Nerd Font glyph when icons are
200
+ // on, the three-character tag otherwise (browsers lack the font).
201
+ function sourceMarker(source, icons) {
202
+ if (icons) return SOURCE_ICON[source] ?? ""
203
+ return SOURCE_TAG[source] ?? ""
204
+ }
205
+
206
+ export {
207
+ DEVICE_ICON,
208
+ DIR_ICON,
209
+ FILE_ICON,
210
+ SOURCE_ICON,
211
+ SOURCE_TAG,
212
+ SYMLINK_ICON,
213
+ extensionOf,
214
+ iconFor,
215
+ iconsEnabled,
216
+ sourceMarker,
217
+ }
184
218
 
185
219
  // EOF
@@ -22,7 +22,6 @@ export { createBehaviors as commandBarBehaviors } from "./CommandBar.js"
22
22
  export { createBehaviors as commandItemBehaviors } from "./CommandItem.js"
23
23
  export { createBehaviors as commandArgRowBehaviors } from "./CommandArgRow.js"
24
24
  export { createBehaviors as commandPanelBehaviors } from "./CommandPanel.js"
25
- export { createBehaviors as commandPaletteBehaviors } from "./CommandPalette.js"
26
25
  export { createBehaviors as confirmBehaviors } from "./Confirm.js"
27
26
  export { createBehaviors as crumbBehaviors } from "./Crumb.js"
28
27
  export { createBehaviors as entryDetailBehaviors } from "./EntryDetail.js"
@@ -30,12 +29,12 @@ export { createBehaviors as fileListBehaviors } from "./FileList.js"
30
29
  export { createBehaviors as filePreviewBehaviors } from "./FilePreview.js"
31
30
  export { createBehaviors as fileRowBehaviors } from "./FileRow.js"
32
31
  export { createBehaviors as filterBarBehaviors } from "./FilterBar.js"
33
- export { createBehaviors as paletteRowBehaviors } from "./PaletteRow.js"
32
+ export { createBehaviors as helpBehaviors } from "./Help.js"
34
33
  export { createBehaviors as summaryBehaviors } from "./Summary.js"
35
34
 
36
35
  // Helpers
37
36
  export { formatCounts, formatDisk, formatMode, formatSize, formatTime } from "./format.js"
38
- export { iconFor, iconsEnabled } from "./icons.js"
39
- export { bookmarkRows, commandItemRows, crumbRows, fileRows, fileWindow, paletteRows, panelArgRows, panelOpRows } from "./rows.js"
37
+ export { iconFor, iconsEnabled, sourceMarker } from "./icons.js"
38
+ export { bookmarkRows, commandItemRows, crumbRows, fileRows, fileWindow, panelArgRows, panelOpRows } from "./rows.js"
40
39
 
41
40
  // EOF
@@ -36,7 +36,7 @@ function isCell(value) {
36
36
  }
37
37
 
38
38
  // Function: isSelected
39
- // Whether a palette/bookmark row is the current selection. The plain
39
+ // Whether a bookmark row is the current selection. The plain
40
40
  // `selectedRow` flag (snapshotted by the row builder) wins over the shared
41
41
  // `selected` cell, so a move only rewrites the rows that actually changed.
42
42
  function isSelected(data) {
@@ -119,21 +119,6 @@ function fileRows(FileRow) {
119
119
  }
120
120
  }
121
121
 
122
- // Function: paletteRows
123
- // `{ item: command, index, selectedRow }` bags for `PaletteRow` rows.
124
- // `selectedRow` is the plain per-row selection flag so a palette move rewrites
125
- // only the changed rows.
126
- function paletteRows(PaletteRow) {
127
- return (self, data) => {
128
- const selected = isCell(data.selected) ? unwrap(data.selected) : data.selected
129
- return PaletteRow.map(
130
- data.items ?? [],
131
- (item, index) => ({ item, index, selectedRow: index === selected }),
132
- (command) => command.id,
133
- )
134
- }
135
- }
136
-
137
122
  // Function: commandItemRows
138
123
  // `{ item: command, index }` bags for `CommandItem` rows.
139
124
  function commandItemRows(CommandItem) {
@@ -146,23 +131,22 @@ function commandItemRows(CommandItem) {
146
131
  }
147
132
 
148
133
  // Function: panelOpRows
149
- // `{ item: op, index, row, cursor, focus }` bags for the command panel's
150
- // operation column (`CommandArgRow`). `row` is the normalized row shape the
151
- // component reads — `name` is the operation title and `summary` its argument
152
- // list (precomputed by `session.panelItems`, the signature with the leading
153
- // name stripped); `risk` tints destructive/caution operations; `cursor` is the
154
- // column's selected index.
134
+ // `{ item, index, row, cursor, focus }` bags for the command panel's command
135
+ // list (`CommandArgRow`). The list is the flat `core/panel.panelEntryRows`: one
136
+ // operation per row. `row` is the normalized shape the component reads —
137
+ // `name` is the operation name and `summary` its argument list; `risk` tints
138
+ // destructive/caution operations.
155
139
  function panelOpRows(CommandArgRow) {
156
140
  return (self, data) =>
157
141
  CommandArgRow.map(
158
142
  data.items ?? [],
159
143
  (item, index) => ({
160
144
  item,
161
- index,
145
+ index: item.entryIndex ?? index,
162
146
  row: {
163
147
  type: "op",
164
148
  key: item.id,
165
- name: item.title,
149
+ name: item.name,
166
150
  summary: item.summary ?? "",
167
151
  risk: item.risk ?? null,
168
152
  },
@@ -174,22 +158,42 @@ function panelOpRows(CommandArgRow) {
174
158
  }
175
159
 
176
160
  // Function: panelArgRows
177
- // `{ item: row, index, row, cursor, focus }` bags for the command panel's
178
- // argument column (`CommandArgRow`). The rows are already flattened by
179
- // `core/panel.panelRowList`; `cursor` is the column's selected index.
161
+ // `{ item, index, row, cursor, focus, value, labelWidth }` bags for the command
162
+ // panel's argument pane (`CommandArgRow`). The rows are the flat
163
+ // `core/panel.panelRowList`; this wraps each selection's item run in a top and
164
+ // bottom `frame` row so the list reads as one bordered box. `index` is the
165
+ // row's flat position (the frame rows take none), so the pane cursor still
166
+ // addresses the same rows.
180
167
  function panelArgRows(CommandArgRow) {
181
- return (self, data) =>
182
- CommandArgRow.map(
183
- data.rows ?? [],
168
+ return (self, data) => {
169
+ const rows = unwrap(data.rows) ?? []
170
+ const visual = []
171
+ for (let i = 0; i < rows.length; i++) {
172
+ const row = rows[i]
173
+ if (row.type !== "item") {
174
+ visual.push(row)
175
+ continue
176
+ }
177
+ const first = i === 0 || rows[i - 1].type !== "item"
178
+ const last = i === rows.length - 1 || rows[i + 1].type !== "item"
179
+ if (first) visual.push({ ...row, type: "frame", variant: "top", key: `frame:top:${row.argIndex}` })
180
+ visual.push(row)
181
+ if (last) visual.push({ ...row, type: "frame", variant: "bottom", key: `frame:bottom:${row.argIndex}` })
182
+ }
183
+ return CommandArgRow.map(
184
+ visual,
184
185
  (item, index) => ({
185
186
  item,
186
- index,
187
+ index: item.type === "frame" ? -1 : item.rowIndex ?? index,
187
188
  row: item,
188
189
  cursor: data.argCursor,
189
190
  focus: data.focus,
191
+ value: data.value,
192
+ labelWidth: unwrap(data.labelWidth) ?? 8,
190
193
  }),
191
- (row) => row.key,
194
+ (item) => item.key,
192
195
  )
196
+ }
193
197
  }
194
198
 
195
199
  // Function: bookmarkRows
@@ -207,6 +211,6 @@ function bookmarkRows(BookmarkRow) {
207
211
  }
208
212
  }
209
213
 
210
- export { bookmarkRows, commandItemRows, crumbRows, fileRows, fileWindow, isSelected, paletteRows, panelArgRows, panelOpRows }
214
+ export { bookmarkRows, commandItemRows, crumbRows, fileRows, fileWindow, isSelected, panelArgRows, panelOpRows }
211
215
 
212
216
  // EOF