@ssebastien/tcmd 0.1.3 → 0.1.7
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.
- package/README.md +256 -62
- package/bin/tcmd +10 -2
- package/bin/tcmd.js +13 -2
- package/desktop/dist/index.html +13 -0
- package/desktop/dist/main.js +20298 -0
- package/desktop/dist/style.css +1515 -0
- package/desktop/frontend/index.html +13 -0
- package/package.json +19 -6
- package/public/fonts/OFL.txt +93 -0
- package/public/index.html +14 -0
- package/public/licenses/LUCIDE.txt +43 -0
- package/public/main.js +20679 -0
- package/public/style.css +1515 -0
- package/scripts/build-desktop.js +31 -0
- package/scripts/serve-web.js +61 -0
- package/src/js/tcmd/components/App.html +80 -14
- package/src/js/tcmd/components/App.js +339 -114
- package/src/js/tcmd/components/App.xml +76 -22
- package/src/js/tcmd/components/BookmarkList.html +5 -5
- package/src/js/tcmd/components/BookmarkList.js +15 -41
- package/src/js/tcmd/components/BookmarkList.xml +1 -2
- package/src/js/tcmd/components/BookmarkRow.html +2 -1
- package/src/js/tcmd/components/BookmarkRow.js +14 -9
- package/src/js/tcmd/components/BookmarkRow.xml +1 -1
- package/src/js/tcmd/components/Breadcrumbs.js +1 -1
- package/src/js/tcmd/components/CommandArgRow.html +28 -0
- package/src/js/tcmd/components/CommandArgRow.js +146 -30
- package/src/js/tcmd/components/CommandArgRow.xml +55 -4
- package/src/js/tcmd/components/CommandBar.html +2 -2
- package/src/js/tcmd/components/CommandBar.js +33 -27
- package/src/js/tcmd/components/CommandBar.xml +2 -2
- package/src/js/tcmd/components/CommandItem.html +1 -1
- package/src/js/tcmd/components/CommandItem.js +25 -25
- package/src/js/tcmd/components/CommandItem.xml +2 -2
- package/src/js/tcmd/components/CommandPanel.html +29 -0
- package/src/js/tcmd/components/CommandPanel.js +78 -52
- package/src/js/tcmd/components/CommandPanel.xml +49 -25
- package/src/js/tcmd/components/Confirm.html +2 -2
- package/src/js/tcmd/components/EntryDetail.html +7 -7
- package/src/js/tcmd/components/EntryDetail.js +22 -8
- package/src/js/tcmd/components/FileList.html +5 -1
- package/src/js/tcmd/components/FileList.js +2 -1
- package/src/js/tcmd/components/FilePicker.html +10 -0
- package/src/js/tcmd/components/FilePicker.js +43 -0
- package/src/js/tcmd/components/FilePicker.xml +13 -0
- package/src/js/tcmd/components/FilePickerRow.html +6 -0
- package/src/js/tcmd/components/FilePickerRow.js +27 -0
- package/src/js/tcmd/components/FilePickerRow.xml +10 -0
- package/src/js/tcmd/components/FilePreview.html +14 -4
- package/src/js/tcmd/components/FilePreview.js +111 -12
- package/src/js/tcmd/components/FilePreview.xml +28 -8
- package/src/js/tcmd/components/FileRow.html +2 -1
- package/src/js/tcmd/components/FileRow.js +70 -12
- package/src/js/tcmd/components/FilterBar.html +1 -1
- package/src/js/tcmd/components/FilterBar.js +14 -4
- package/src/js/tcmd/components/FilterBar.xml +1 -1
- package/src/js/tcmd/components/Frame.js +324 -0
- package/src/js/tcmd/components/Help.html +15 -0
- package/src/js/tcmd/components/Help.js +260 -0
- package/src/js/tcmd/components/Help.xml +13 -4
- package/src/js/tcmd/components/HelpRow.html +8 -0
- package/src/js/tcmd/components/HelpRow.xml +7 -0
- package/src/js/tcmd/components/OverlayList.html +8 -0
- package/src/js/tcmd/components/OverlayList.js +69 -0
- package/src/js/tcmd/components/{CommandPalette.xml → OverlayList.xml} +2 -5
- package/src/js/tcmd/components/OverlayRow.html +5 -0
- package/src/js/tcmd/components/OverlayRow.js +47 -0
- package/src/js/tcmd/components/OverlayRow.xml +9 -0
- package/src/js/tcmd/components/Summary.html +1 -1
- package/src/js/tcmd/components/Summary.js +1 -6
- package/src/js/tcmd/components/format.js +4 -23
- package/src/js/tcmd/components/icons.js +176 -5
- package/src/js/tcmd/components/index.js +9 -5
- package/src/js/tcmd/components/rows.js +176 -48
- package/src/js/tcmd/core/base64.js +64 -0
- package/src/js/tcmd/core/bookmarks.js +45 -8
- package/src/js/tcmd/core/commands.js +978 -260
- package/src/js/tcmd/core/concurrency.js +59 -0
- package/src/js/tcmd/core/decompress.js +117 -0
- package/src/js/tcmd/core/directory.js +219 -93
- package/src/js/tcmd/core/format.js +36 -0
- package/src/js/tcmd/core/fs.js +438 -54
- package/src/js/tcmd/core/history.js +58 -0
- package/src/js/tcmd/core/index.js +3 -9
- package/src/js/tcmd/core/journal.js +93 -0
- package/src/js/tcmd/core/keymap.js +42 -6
- package/src/js/tcmd/core/operations.js +813 -48
- package/src/js/tcmd/core/os.js +29 -3
- package/src/js/tcmd/core/pane.js +33 -8
- package/src/js/tcmd/core/panel.js +280 -104
- package/src/js/tcmd/core/path.js +30 -3
- package/src/js/tcmd/core/permissions.js +107 -0
- package/src/js/tcmd/core/picker.js +156 -0
- package/src/js/tcmd/core/preview.js +102 -0
- package/src/js/tcmd/core/script.js +7 -95
- package/src/js/tcmd/core/selection.js +9 -1
- package/src/js/tcmd/core/session.js +498 -200
- package/src/js/tcmd/core/watch.js +153 -0
- package/src/js/tcmd/desktop/backend/api.js +165 -0
- package/src/js/tcmd/desktop/backend/fs.js +626 -0
- package/src/js/tcmd/desktop/backend/main.ts +9 -0
- package/src/js/tcmd/desktop/bookmarks.js +23 -0
- package/src/js/tcmd/desktop/fs.js +91 -0
- package/src/js/tcmd/desktop/main.js +194 -0
- package/src/js/tcmd/desktop/os.js +33 -0
- package/src/js/tcmd/index.js +0 -1
- package/src/js/tcmd/tui/app.js +118 -60
- package/src/js/tcmd/tui/catalogue.js +238 -42
- package/src/js/tcmd/tui/index.js +18 -19
- package/src/js/tcmd/tui/opentui.js +33 -0
- package/src/js/tcmd/tui/syntax.js +91 -0
- package/src/js/tcmd/tui/theme.js +32 -2
- package/src/js/tcmd/vendor/bzip2.js +276 -0
- package/src/js/tcmd/web/app.js +225 -41
- package/src/js/tcmd/web/bookmarks.js +36 -0
- package/src/js/tcmd/web/catalogue.js +81 -38
- package/src/js/tcmd/web/effects.js +223 -0
- package/src/js/tcmd/web/header.html +1 -1
- package/src/js/tcmd/web/header.js +15 -11
- package/src/js/tcmd/web/http.js +13 -0
- package/src/js/tcmd/web/index.js +2 -2
- package/src/js/tcmd/web/main.js +6 -66
- package/src/js/tcmd/web/opfs.js +297 -30
- package/src/js/tcmd/web/seed.js +4 -4
- package/src/js/tcmd/web/server.js +124 -21
- package/src/js/tcmd/web/theme.js +4 -0
- package/tinyjs.json +16 -0
- package/src/js/tcmd/components/CommandPalette.html +0 -7
- package/src/js/tcmd/components/CommandPalette.js +0 -58
- package/src/js/tcmd/components/PaletteRow.html +0 -3
- package/src/js/tcmd/components/PaletteRow.js +0 -43
- package/src/js/tcmd/components/PaletteRow.xml +0 -4
- package/src/js/tcmd/tui/frame.js +0 -269
- package/src/js/tcmd/tui/help.js +0 -32
package/README.md
CHANGED
|
@@ -10,18 +10,78 @@
|
|
|
10
10
|
|
|
11
11
|
TinyCommand `tcmd` is a midnight-commander-like CLI and desktop tool:
|
|
12
12
|
|
|
13
|
-
- Browse files in
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
and
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
-
|
|
13
|
+
- Browse files in the three-column explorer (the opening view) — the parent
|
|
14
|
+
directory, the current directory (slightly wider) and the preview — or in
|
|
15
|
+
the two-pane commander (the left is the source, the right the destination),
|
|
16
|
+
with `t` toggling between the two views. Clicking any column, preview
|
|
17
|
+
included, gives it the focus.
|
|
18
|
+
- Named bookmarks: `b` opens one bookmarks panel. Type to filter the stored
|
|
19
|
+
bookmarks by name or path; when the filter is empty and the cursor entry (or
|
|
20
|
+
the pane directory) is not bookmarked yet, a first row adds it under a
|
|
21
|
+
basename-derived name. `Enter` opens the selected bookmark, `⌫`/`Delete`
|
|
22
|
+
removes it. A directory bookmark opens to show its contents, a file bookmark
|
|
23
|
+
is selected and previewed in its directory.
|
|
24
|
+
- Undo/redo (`⌃Z` / `⌃⇧Z` or `⌃Y`) over recorded filesystem changes (mkdir,
|
|
25
|
+
copies to new destinations, move, rename, trashed delete), with a history list
|
|
26
|
+
(`u`); a delete reports the trash directory it used and can be undone or
|
|
27
|
+
restored from the trash browser
|
|
28
|
+
(`⇧T`), which lists trashed entries by their original path. A double click on
|
|
29
|
+
a row opens it like `Enter`.
|
|
30
|
+
- Operations over the current panes: copy, move, mkdir/new, hardlink/symlink,
|
|
31
|
+
chmod/chown, delete/erase/unlink, stat, du/df, touch, selection buffers,
|
|
32
|
+
edit/view/open and shell commands; `abspath` and `realpath` show
|
|
33
|
+
copy-pasteable absolute paths and copy them to the clipboard, with the latter
|
|
34
|
+
following symlinks. `unlink` removes links only, never their targets.
|
|
35
|
+
- Command panel (`:`), the one entry point, a two-pane palette: the left pane
|
|
36
|
+
filters (`:xxx`) over CLI operations grouped by purpose: **Command**,
|
|
37
|
+
**File**, **Info**, **Create**, **Edit** (amber), and **Delete** (red). The
|
|
38
|
+
Command group also has the workspace-view toggle and Quit; File's `view`
|
|
39
|
+
previews the selected path; Info holds `stat`, `du` and `df`; amber Edit also
|
|
40
|
+
holds `copy` and the metadata changes. Bold section
|
|
41
|
+
titles head indented command names, alphabetized within each category. The right
|
|
42
|
+
pane lists the selected command's arguments under its name — editable values
|
|
43
|
+
use filled fields, while read-only values are plain and muted; `new`/`mkdir`
|
|
44
|
+
show the current `PATH` above a `NAME`, and `chmod`/`chown` list `PATH`
|
|
45
|
+
first. Selections use
|
|
46
|
+
a checkbox (multiple) / radio (single)
|
|
47
|
+
list of the selected paths wrapped in its own border, with a lone path shown
|
|
48
|
+
read-only. `chmod`'s `MODE` is a compact u/g/o grid of clickable R/W/X cells
|
|
49
|
+
(arrow keys move between cells, Space toggles), seeded from the selected
|
|
50
|
+
file's current mode. Labels and values sit one space in from the pane border
|
|
51
|
+
(` PATH`, ` [value]`), and `[Run]` follows the last field after a
|
|
52
|
+
blank line, aligned left in the command category's color. Amber (Edit) and
|
|
53
|
+
red (Delete) actions take two steps: the first Run turns the action into
|
|
54
|
+
`[Confirm]` and prints the command below it in the risk colour, the second
|
|
55
|
+
executes. A failed run shows `[Retry]` with a framed error block below it,
|
|
56
|
+
while a
|
|
57
|
+
successful result briefly replaces the command bar at the bottom. A selection
|
|
58
|
+
argument draws from the *focused* column, so in the explorer focusing the parent mirror
|
|
59
|
+
selects the directory under its cursor. `Tab` cycles the filter, arguments and
|
|
60
|
+
Run button; `Enter` runs a complete command or jumps to the next missing
|
|
61
|
+
argument, and a `[Close Esc]` close button sits in the dialog's top border,
|
|
62
|
+
upper right. `!` lands straight on the shell
|
|
63
|
+
command. A destination/path argument browses the filesystem in a file picker
|
|
64
|
+
(`⌃O`): its path is editable as a string, the matching entries sit below and
|
|
65
|
+
render like a file panel (icons, aligned sizes, scrollbar), with `..` pinned
|
|
66
|
+
at the top. Explorer mode has no destination pane, so a move or copy there
|
|
67
|
+
asks where to. A `replace` operation replaces a literal `FIND` with `REPLACE`
|
|
68
|
+
across the selection — one file replaces its whole name — previewing each
|
|
69
|
+
`old → new` mapping before it runs. Exact `rename FROM TO PATH` renames one named
|
|
70
|
+
child inside `PATH`.
|
|
71
|
+
- Keyboard and mouse navigation, inline file previews — a previewed directory
|
|
72
|
+
renders as a pane (its breadcrumbs over its own listing), in both views, a
|
|
73
|
+
source file highlights with tree-sitter (JavaScript, TypeScript, Markdown and
|
|
74
|
+
Zig today) through the same `code` renderable, and an image draws in place
|
|
75
|
+
through the terminal's graphics protocol (Kitty/sixel, half-block fallback),
|
|
76
|
+
the metadata card staying for formats with no decoder. `.gz` and `.bz2`
|
|
77
|
+
entries preview their decompressed contents: the inner name drives the kind
|
|
78
|
+
and highlighter (`notes.txt.gz` heads as `<GZ · TXT>`), and a body that
|
|
79
|
+
decompresses to binary stays a card
|
|
80
|
+
- Symlinks are shown *and* followed: a link row prints `name -> target`
|
|
81
|
+
(`notes.md -> notes.txt`, `docs/ -> ../shared`), a link to a directory is a
|
|
82
|
+
directory (it sorts, opens and previews as one), and the preview reads
|
|
83
|
+
through it — the header reads `notes.md -> notes.txt <TXT>` and the kind
|
|
84
|
+
comes from the target's extension, not the link's name
|
|
25
85
|
- Bookmarks and quick find (later milestones)
|
|
26
86
|
|
|
27
87
|
References: [Marta.sh](https://marta.sh) for the UI, [fman.io](https://fman.io)
|
|
@@ -46,39 +106,88 @@ bun run tcmd # open cwd (left) and ~ (right)
|
|
|
46
106
|
bun run tcmd /tmp ~ # open specific directories
|
|
47
107
|
```
|
|
48
108
|
|
|
49
|
-
Keys: `↑/↓` move (`^↑/^↓` jump a few lines), `Enter/→` open
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
109
|
+
Keys: `↑/↓` move (`^↑/^↓` jump a few lines), `Enter/→` open (a directory
|
|
110
|
+
re-opens with the cursor it was left with, a text file opens in `$EDITOR`, and
|
|
111
|
+
anything that cannot be edited — an image, media, a binary, or any file on a
|
|
112
|
+
host with no editor — opens the fullscreen viewer), `←/Bksp` parent (the cursor
|
|
113
|
+
lands on the directory you left), `Esc`/`⇧Esc` history back/forward, `Tab`/`⇧Tab` switch panes
|
|
114
|
+
(`⇧Tab` goes back), `t` toggle the commander/explorer view, `Space` mark, `b`
|
|
115
|
+
open the bookmarks panel (filter, add the current path, `Enter` open,
|
|
116
|
+
`⌫`/`Del` remove), `:` open the
|
|
117
|
+
command panel (the one entry point for CLI operations, grouped by risk with
|
|
118
|
+
editable arguments, an explicit Run button and the inline file picker on `⌃O`),
|
|
119
|
+
`v` preview (fullscreen viewer when
|
|
120
|
+
the terminal is narrow), `⇧V` maximize the preview over the columns and back
|
|
121
|
+
again (in either full view, `↑`/`↓` scroll a file preview and `←`/`→` step to
|
|
122
|
+
the previous/next entry), `O`/`E` open/edit the previewed file, `/` fuzzy-filter
|
|
123
|
+
the active pane (`Enter` keeps it, `Esc` clears), `!` open the panel on the
|
|
124
|
+
shell command, `F5`/`r`/`^R` reload, `?` key reference, `q` quit.
|
|
125
|
+
|
|
126
|
+
Listings refresh themselves when the filesystem changes: the TUI and desktop
|
|
127
|
+
views watch the displayed directories natively, while the web view polls them
|
|
128
|
+
every few seconds. Hidden columns pause their watcher and re-read when shown.
|
|
129
|
+
A refresh keeps the selected entry selected, so files written by other
|
|
130
|
+
programs appear without moving the cursor. The keys above (and the web toolbar
|
|
131
|
+
button) remain for a forced re-read.
|
|
132
|
+
|
|
133
|
+
The workspace opens in explorer mode with a slim identity bar (`TinyCmd -
|
|
134
|
+
HOST`) — the view mode and version trailing at its right, the mode word drawn
|
|
135
|
+
letter by letter along the logo's own gradients (`explorer` in the Tiny blues,
|
|
136
|
+
`commander` in the Cmd oranges, clicking it toggles the view) and the version
|
|
137
|
+
dim after it — over an
|
|
138
|
+
unframed tiling of pane boxes (the two middle edges sit side by side) labelled
|
|
139
|
+
**SRC** (left, source) / **DST** (right, destination) in commander, with a
|
|
62
140
|
header row (label + clickable breadcrumbs — collapsed to first letters like
|
|
63
141
|
`/h/s/W/Perso/tcmd` when the width demands — + the inline filter) separated by a
|
|
64
|
-
rule from the listing, an entry-detail line for the selected entry
|
|
65
|
-
|
|
142
|
+
rule from the listing, and an entry-detail line for the selected entry. The
|
|
143
|
+
detail line shows
|
|
66
144
|
`date · type · size · rights` with the rights right-aligned, the date and size
|
|
67
|
-
bright, and the size/rights dropping out when the pane is too narrow
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
145
|
+
bright, and the size/rights dropping out when the pane is too narrow. The active
|
|
146
|
+
pane's border and header rule take the view mode's colour — the Tiny blue in
|
|
147
|
+
explorer, the gold accent in commander — and its cursor row uses the strong
|
|
148
|
+
cursor colour, while
|
|
149
|
+
the inactive pane stays dim with a muted cursor. TUI rows lead with a yazi-style
|
|
71
150
|
Nerd Font icon (folder/link kind icons plus a per-extension icon, `Makefile`,
|
|
72
|
-
`Dockerfile` and dotfiles included), on unless `TCMD_ICONS=0
|
|
73
|
-
Nerd-Font-patched terminal font
|
|
151
|
+
`Dockerfile` and dotfiles included), on unless `TCMD_ICONS=0`; it needs a
|
|
152
|
+
Nerd-Font-patched terminal font. The web and desktop views use local Lucide SVG
|
|
153
|
+
icons for toolbar actions, file kinds, bookmarks, and Help key badges; command
|
|
154
|
+
panel entries show a chevron to indicate their argument pane. A
|
|
74
155
|
context-aware inspector sits
|
|
75
156
|
on the right when the terminal is at least 100 columns wide, and becomes a
|
|
76
|
-
fullscreen viewer below that
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
157
|
+
fullscreen viewer below that; `⇧V` expands it over the whole pane row —
|
|
158
|
+
parent and current included — and brings the previous layout back on the next
|
|
159
|
+
press. The status bar keeps its fixed bracket shorthands on one line
|
|
160
|
+
(`[Commands :]`, `[Mark Spc]`, `[Filter /]`, `[Switch ⇥]`, `[Bookmarks b]`,
|
|
161
|
+
`[Toggle Mode t]`, `[Help ?]`) and elides entries behind `…` when the width
|
|
162
|
+
runs out — the marked count trails the list, and a notice or error swaps the
|
|
163
|
+
keys for a one-line status. Below 64×16
|
|
80
164
|
the app shows a resize hint instead of the workspace.
|
|
81
165
|
|
|
166
|
+
### View modes
|
|
167
|
+
|
|
168
|
+
`t` flips the workspace between two modes:
|
|
169
|
+
|
|
170
|
+
- **explorer** (the default) leads with the parent of the current directory,
|
|
171
|
+
so the three columns read parent · current · preview — a Miller chain with
|
|
172
|
+
the current column slightly wider than its neighbours;
|
|
173
|
+
- **commander** tiles source and destination.
|
|
174
|
+
|
|
175
|
+
The parent and preview columns are mirrors: they own their cursor, filter and
|
|
176
|
+
marks, but opening an entry, stepping up, following one of their breadcrumbs or
|
|
177
|
+
clicking one of their rows moves the pane they follow, which is what keeps the
|
|
178
|
+
mirror in step (in the explorer the previewed directory *is* the current
|
|
179
|
+
column's own child, so the chain never drifts). A click on the parent column
|
|
180
|
+
makes the clicked directory the current one — or, for a file, opens its
|
|
181
|
+
directory with the file selected and previewed — while a click inside the
|
|
182
|
+
previewed directory promotes that directory to current with the clicked entry
|
|
183
|
+
selected (focus stays on the column that was clicked). The parent mirror's
|
|
184
|
+
cursor also follows the current
|
|
185
|
+
directory: it lands on the entry the current pane shows, so the chain always
|
|
186
|
+
highlights where it is. `Tab` cycles through the columns the current mode shows,
|
|
187
|
+
and clicking any column — the preview included — gives it the focus. A
|
|
188
|
+
previewed directory always renders as a pane, in both modes: its breadcrumbs
|
|
189
|
+
over its own listing, instead of a directory card.
|
|
190
|
+
|
|
82
191
|
### Web version
|
|
83
192
|
|
|
84
193
|
The web view runs the same core on select.js and offers two filesystem
|
|
@@ -89,28 +198,104 @@ backends:
|
|
|
89
198
|
- **HTTP API** (automatic fallback, or `?fs=http`): the server exposes the
|
|
90
199
|
real filesystem over `/api/*`, using the same Node adapter as the TUI.
|
|
91
200
|
|
|
201
|
+
It uses the same command panel (`:`), selection/filter and history keys as the
|
|
202
|
+
TUI, plus `?` for the key reference. Pane focus, marks,
|
|
203
|
+
entry details and command risk colours follow the terminal presentation. The
|
|
204
|
+
inspector sits beside the panes when wide and opens with `v` as a fullscreen
|
|
205
|
+
viewer when narrow (`Esc` closes it); `⇧V` maximizes it over the pane row and
|
|
206
|
+
restores the layout. Bookmarks persist in browser storage,
|
|
207
|
+
separately for each filesystem backend and HTTP root; they are not shared with
|
|
208
|
+
the TUI's bookmark file. Storage denial leaves bookmarks usable for the session.
|
|
209
|
+
|
|
92
210
|
```sh
|
|
211
|
+
tcmd --server # installed command: serve the web UI without the TUI
|
|
93
212
|
make run-local # build and serve both backends (alias to `bun run web`)
|
|
94
213
|
bun run web # build and serve both backends on http://localhost:8787
|
|
95
214
|
bun run web:build # bundle only, into public/main.js
|
|
96
215
|
bun run web:serve # serve the existing bundle plus /api/*
|
|
97
216
|
```
|
|
98
217
|
|
|
218
|
+
From a checkout, `./bin/tcmd --server` (or `bun run tcmd --server`) does the
|
|
219
|
+
same thing, building the browser bundle if it is missing. Installed packages
|
|
220
|
+
include the bundle. Open http://localhost:8787/?fs=http for your actual files,
|
|
221
|
+
or http://localhost:8787/?fs=opfs for the browser-storage demo. Stop with Ctrl-C.
|
|
222
|
+
Use `HOST=0.0.0.0 PORT=9000 tcmd --server` to change the address and port
|
|
223
|
+
(default `127.0.0.1:8787`), or
|
|
224
|
+
`TCMD_WEB_ROOT=/some/dir tcmd --server` to confine the filesystem API. `HOST`
|
|
225
|
+
takes an IP address (`0.0.0.0` or `::` for every interface); a machine name
|
|
226
|
+
resolves to several addresses and the server binds only one of them.
|
|
227
|
+
|
|
99
228
|
`make run-local` is the local dev entry point (it wraps `bun run web` and
|
|
100
229
|
honours `TCMD_WEB_PORT`, default 8787). The Makefile bootstraps the LittleSDK
|
|
101
230
|
into `deps/sdk` for the standard `make` targets.
|
|
102
231
|
|
|
103
232
|
Appending `?fs=opfs` or `?fs=http` to the URL forces one backend. The HTTP
|
|
104
233
|
backend needs the page served by `bun run web:serve` (a plain static server
|
|
105
|
-
only supports OPFS); the server binds to loopback
|
|
106
|
-
token injected into `index.html`.
|
|
234
|
+
only supports OPFS); the server binds to loopback by default (`HOST`
|
|
235
|
+
overrides it) and requires a per-process token injected into `index.html`.
|
|
236
|
+
`TCMD_WEB_ROOT=/some/dir` confines the API
|
|
107
237
|
(default `/`, the same access as `bun run tcmd`), but it is not a sandbox:
|
|
108
|
-
don't expose the port.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
238
|
+
don't expose the port. Symlinks that resolve outside the root are rejected;
|
|
239
|
+
root and destination checks are not race-proof against another local process
|
|
240
|
+
changing the filesystem during a request. With OPFS, deleting moves entries to `/.trash`;
|
|
241
|
+
the API uses the desktop trash. OPFS coordination across tabs uses Web Locks
|
|
242
|
+
when available; without them, writes are coordinated per adapter only.
|
|
243
|
+
Open/edit/run are unavailable in the browser
|
|
244
|
+
either way (the null host adapter reports it in the command bar); everything
|
|
245
|
+
else — navigation, marks, scripting, copy/move/erase — works on either
|
|
112
246
|
backend. `public/index.html` is the host page; `public/main.js` is generated.
|
|
113
247
|
|
|
248
|
+
### Desktop app (tinyjs)
|
|
249
|
+
|
|
250
|
+
The native desktop target reuses the web presentation and shared session, but
|
|
251
|
+
uses a tinyjs backend for real filesystem access. It runs without Bun/Node in
|
|
252
|
+
the shipped app and does not start an HTTP server or open a port.
|
|
253
|
+
|
|
254
|
+
Install [tinyjs](https://tinyjs.app/) and the project dependencies, then run:
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
bun install
|
|
258
|
+
bun run desktop:dev # bundle the webview UI, then open the native window
|
|
259
|
+
bun run desktop:build # tinyjs build; emits a platform-native app/package
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
`tinyjs.json` configures the app. `tinyjs build` bundles the desktop frontend
|
|
263
|
+
through `desktop:bundle`; the source page is `desktop/frontend/index.html`.
|
|
264
|
+
For frontend changes during development, rerun `bun run desktop:bundle` before
|
|
265
|
+
reloading the window. The desktop uses the system webview and requires tinyjs
|
|
266
|
+
0.57.0 or newer.
|
|
267
|
+
|
|
268
|
+
The desktop window keeps the native title bar, hides the native menu, and uses
|
|
269
|
+
an in-app toolbar for folder navigation, refresh, view/preview toggles, commands
|
|
270
|
+
and help. Its toolbar replaces the TinyCmd identity header; the browser UI keeps
|
|
271
|
+
that header. Press F6 to move focus between the workspace and toolbar; toolbar
|
|
272
|
+
buttons use native Tab/Enter/Space keys, and Escape returns to the workspace.
|
|
273
|
+
|
|
274
|
+
The web UI uses the system sans-serif font for controls and chrome, with the
|
|
275
|
+
M+1Code Nerd Font Mono bundled for filenames, paths, and file content. The
|
|
276
|
+
font is from Nerd Fonts v3.5.1 and is distributed under SIL OFL 1.1; its license
|
|
277
|
+
is included in `public/fonts/OFL.txt`.
|
|
278
|
+
The UI icons are from Lucide and distributed under ISC (with the listed Feather
|
|
279
|
+
icons under MIT); the license is included in `public/licenses/LUCIDE.txt`.
|
|
280
|
+
|
|
281
|
+
On Linux, the tinyjs launcher also needs the Ayatana AppIndicator runtime. On
|
|
282
|
+
Fedora install it with `sudo dnf install libayatana-appindicator-gtk3`; on
|
|
283
|
+
Debian/Ubuntu use `sudo apt install libayatana-appindicator3-1`. Install the
|
|
284
|
+
WebKitGTK runtime required by tinyjs as well.
|
|
285
|
+
|
|
286
|
+
Filesystem operations are performed by the backend using txiki.js APIs. Text
|
|
287
|
+
files open in the operating system's default editor; `!` / shell commands run
|
|
288
|
+
in a non-interactive shell and display captured output in a native dialog.
|
|
289
|
+
Trash uses the Freedesktop trash under `$XDG_DATA_HOME` (or
|
|
290
|
+
`~/.local/share/Trash`), including undo/restore. The current desktop adapter
|
|
291
|
+
supports POSIX paths on Linux and macOS. Windows drive/UNC paths are not yet
|
|
292
|
+
supported; the app reports this rather than attempting file operations.
|
|
293
|
+
|
|
294
|
+
The desktop backend has full access to the current user's files, and the
|
|
295
|
+
command panel intentionally exposes shell execution. Treat commands and
|
|
296
|
+
filesystem prompts accordingly. Browser and TUI builds remain separate and
|
|
297
|
+
continue to use their existing backends.
|
|
298
|
+
|
|
114
299
|
## Architecture
|
|
115
300
|
|
|
116
301
|
MVC with a presentation-agnostic core, so a web UI can share the same model
|
|
@@ -125,26 +310,26 @@ src/js/tcmd/
|
|
|
125
310
|
entries.js filter/sort over file entries (dirs first)
|
|
126
311
|
selection.js cursor/mark arithmetic
|
|
127
312
|
directory.js one pane's directory: path, entries, view, cursor, marks
|
|
128
|
-
pane.js one directory, proxying its cells
|
|
313
|
+
pane.js one directory, proxying its cells + currentPane resolution
|
|
129
314
|
bookmarks.js named bookmarks (path + file/dir kind) + the memory/file stores
|
|
130
|
-
panel.js command panel
|
|
131
|
-
session.js two panes + active pane, preview,
|
|
315
|
+
panel.js grouped CLI command panel, with arguments bound from context
|
|
316
|
+
session.js two panes + parent/preview mirrors, view mode, active pane, preview, panel, dispatch
|
|
132
317
|
keymap.js key chords -> command ids
|
|
133
318
|
commands.js command registry; commands are `run(session, payload)`
|
|
134
|
-
operations.js
|
|
135
|
-
script.js scripting line parser
|
|
319
|
+
operations.js CLI operations (copy/move/delete/erase/stat/sel/…)
|
|
320
|
+
script.js scripting line parser and runner
|
|
136
321
|
components/ Shared, renderer-agnostic component logic
|
|
137
322
|
App.js root controller: key + event -> dispatch
|
|
138
323
|
bind.js `ui.component(name).does(behaviors).using(source)` binding
|
|
139
324
|
rows.js keyed row-bag builders shared by both catalogues
|
|
140
325
|
FileList.js … one `.js` per component (behaviors + pure helpers)
|
|
141
326
|
FileRow.js … row behaviors shared by both engines
|
|
327
|
+
Frame.js workspace chrome, breakpoints and command-bar geometry
|
|
328
|
+
Help.js key-reference overlay behaviors + the two-column key text
|
|
142
329
|
format.js presentation helpers (sizes/counts)
|
|
143
330
|
tui/ View (select.tui templates on OpenTUI)
|
|
144
331
|
app.js binds App.js + App.xml + TUI environment; size/help cells
|
|
145
332
|
catalogue.js binds each `components/*.js` to its `.xml` and registers tags
|
|
146
|
-
frame.js TUI-only chrome: frame title, breakpoints
|
|
147
|
-
help.js TUI-only `?` key-reference overlay behaviors
|
|
148
333
|
index.js TUI entry point; feeds `size` from renderer resize events
|
|
149
334
|
theme.js TUI palette resolver (dark/light/mono, NO_COLOR)
|
|
150
335
|
web/ Browser view (select.js templates on the DOM, same core)
|
|
@@ -152,6 +337,8 @@ src/js/tcmd/
|
|
|
152
337
|
catalogue.js binds each `components/*.js` to its `.html`
|
|
153
338
|
index.js web entry point: `main(session, selector)`
|
|
154
339
|
main.js browser bootstrap: backend choice + session (bundled)
|
|
340
|
+
effects.js instance-owned focus, key trapping, resize and scroll effects
|
|
341
|
+
bookmarks.js localStorage bookmark store, scoped by backend and API root
|
|
155
342
|
opfs.js OPFS filesystem adapter (browser storage)
|
|
156
343
|
http.js filesystem adapter over the `/api` JSON endpoints
|
|
157
344
|
server.js dev server: `public/` + `/api/*` (used by `bun run web:serve`)
|
|
@@ -177,9 +364,13 @@ reactive collections, sharing row behaviors over a `{ item, index, … }` bag
|
|
|
177
364
|
built by `components/rows.js`; cells stay in the bag and are read at the point
|
|
178
365
|
of use.
|
|
179
366
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
367
|
+
`components/Frame.js` holds the renderer-free workspace chrome (brand bar,
|
|
368
|
+
responsive breakpoints, pane borders, command-bar geometry) and
|
|
369
|
+
`components/Help.js` holds the key reference — behaviours and the two-column
|
|
370
|
+
key text, with every distinct keymap chord shown in the command bar's bracket
|
|
371
|
+
style — used by both views. Web-only components live under `web/` (`header.js` +
|
|
372
|
+
`header.html`, plus the DOM effects in `effects.js` and the bookmark store in
|
|
373
|
+
`bookmarks.js`). Shared
|
|
183
374
|
behaviors only ever grow additively (a new key for a new presentation),
|
|
184
375
|
so the other engine's presentation and the parity tests keep working unchanged.
|
|
185
376
|
|
|
@@ -206,8 +397,8 @@ Decoupling rules:
|
|
|
206
397
|
see `src/js/tcmd/vendor/`)
|
|
207
398
|
- Bun as the runtime
|
|
208
399
|
|
|
209
|
-
`deps/` symlinks to sibling checkouts; `scripts/link-deps.sh`
|
|
210
|
-
|
|
400
|
+
`deps/` symlinks to sibling checkouts; run `scripts/link-deps.sh` after
|
|
401
|
+
`bun install` to re-link `node_modules` so upstream edits propagate.
|
|
211
402
|
|
|
212
403
|
## Tests
|
|
213
404
|
|
|
@@ -222,14 +413,17 @@ tests run the shared logic through select.js on a happy-dom document.
|
|
|
222
413
|
## Roadmap
|
|
223
414
|
|
|
224
415
|
- [x] Walking skeleton: two panes, navigation, breadcrumbs, summary, command
|
|
225
|
-
bar, command
|
|
226
|
-
- [x] File operations (copy/move/delete/erase/stat/touch/sel),
|
|
227
|
-
|
|
228
|
-
- [x]
|
|
229
|
-
|
|
416
|
+
bar, command panel, text preview
|
|
417
|
+
- [x] File operations (copy/move/delete/erase/stat/touch/sel), with the
|
|
418
|
+
`$A`/`$B` pane filesets and `$FILE` still resolving in any argument
|
|
419
|
+
- [x] Grouped command panel (`:`): white / amber / red risk sections, editable
|
|
420
|
+
arguments, an explicit Run action and an inline picker; `!` lands on shell
|
|
230
421
|
- [x] Pane fuzzy filtering (`/` to filter, `Enter` to keep, `Esc` to clear)
|
|
231
|
-
- [x] Named bookmarks (`b`
|
|
422
|
+
- [x] Named bookmarks (`b` opens the panel to add, filter, open or remove them,
|
|
423
|
+
persisted to a JSON file)
|
|
232
424
|
- [ ] Quick find, grep
|
|
233
|
-
- [
|
|
425
|
+
- [x] Image previews (terminal graphics protocol) and tree-sitter source
|
|
426
|
+
highlighting in the inspector
|
|
427
|
+
- [ ] PDF/audio/video previews
|
|
234
428
|
- [x] Web view on the same core (shared components rendered by select.js)
|
|
235
429
|
- [x] Pane navigation history (`Esc`/`⇧Esc` back/forward)
|
package/bin/tcmd
CHANGED
|
@@ -5,13 +5,21 @@ if ! command -v bun >/dev/null 2>&1; then
|
|
|
5
5
|
exit 1
|
|
6
6
|
fi
|
|
7
7
|
script=$0
|
|
8
|
+
case "$script" in
|
|
9
|
+
*/*) ;;
|
|
10
|
+
*) script=$(command -v "$script") ;;
|
|
11
|
+
esac
|
|
12
|
+
case "$script" in
|
|
13
|
+
/*) ;;
|
|
14
|
+
*) script=$PWD/$script ;;
|
|
15
|
+
esac
|
|
8
16
|
while [ -L "$script" ]; do
|
|
9
|
-
dir=$(CDPATH= cd -- "$(dirname -- "$script")" && pwd)
|
|
17
|
+
dir=$(CDPATH= cd -P -- "$(dirname -- "$script")" && pwd)
|
|
10
18
|
link=$(readlink "$script")
|
|
11
19
|
case "$link" in
|
|
12
20
|
/*) script=$link ;;
|
|
13
21
|
*) script=$dir/$link ;;
|
|
14
22
|
esac
|
|
15
23
|
done
|
|
16
|
-
dir=$(CDPATH= cd -- "$(dirname -- "$script")" && pwd)
|
|
24
|
+
dir=$(CDPATH= cd -P -- "$(dirname -- "$script")" && pwd)
|
|
17
25
|
exec bun "$dir/tcmd.js" "$@"
|
package/bin/tcmd.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// TinyCommand `tcmd` entry point.
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const raw = process.argv.slice(2).filter((arg) => arg !== "-")
|
|
4
|
+
// `--server` is a mode flag, not a path: accept it in any position.
|
|
5
|
+
const server = raw.includes("--server")
|
|
6
|
+
const paths = raw.filter((arg) => arg !== "--server")
|
|
7
|
+
if (server) {
|
|
8
|
+
if (paths.length > 0) {
|
|
9
|
+
console.error("Usage: tcmd --server (configure with HOST, PORT and TCMD_WEB_ROOT)")
|
|
10
|
+
process.exit(1)
|
|
11
|
+
}
|
|
12
|
+
await import("../scripts/serve-web.js").then((web) => web.main())
|
|
13
|
+
} else {
|
|
14
|
+
await import("../src/js/tcmd/tui/index.js").then((tui) => tui.main(paths))
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>TinyCommand</title>
|
|
7
|
+
<link rel="stylesheet" href="./style.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="./main.js"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|