@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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Project: TinyCommand
|
|
2
|
+
// Author: Sebastien Pierre
|
|
3
|
+
// License: BSD
|
|
4
|
+
// Created: 2026-10-17
|
|
5
|
+
|
|
6
|
+
// Module: tcmd/components/OverlayList
|
|
7
|
+
// The shared modal list overlay: the undo history (`u`) and the trash browser
|
|
8
|
+
// (`⇧T`). One component, two modes — `history` lists the journal (a row jumps
|
|
9
|
+
// the file state to that point), `trash` lists trashed entries (a row restores
|
|
10
|
+
// one). The row logic lives in `OverlayRow.js`; this parent maps its `items` to
|
|
11
|
+
// rows and switches title/footer/empty text on the mode.
|
|
12
|
+
//
|
|
13
|
+
// Environment: `overlayBg`, `borderPrimaryActiveFg`, `textHeadingFg` (title),
|
|
14
|
+
// `textSecondaryFg` (footer/empty), `rows(data)` returning the keyed rows and
|
|
15
|
+
// `scrollTop(self, data)` (TUI viewport tracking).
|
|
16
|
+
|
|
17
|
+
import { unwrap } from "select/state/cells"
|
|
18
|
+
|
|
19
|
+
// Function: overlayMode
|
|
20
|
+
// "trash" or "history" for the data's mode (an absent mode reads as history).
|
|
21
|
+
function overlayMode(mode) {
|
|
22
|
+
return unwrap(mode) === "trash" ? "trash" : "history"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Function: overlayTitle
|
|
26
|
+
function overlayTitle(mode) {
|
|
27
|
+
return overlayMode(mode)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Function: overlayFooter
|
|
31
|
+
function overlayFooter(mode) {
|
|
32
|
+
return overlayMode(mode) === "trash"
|
|
33
|
+
? "↑↓ move ↵ restore Esc close"
|
|
34
|
+
: "↑↓ move ↵ jump ^Z undo ^⇧Z redo Esc close"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Function: overlayEmpty
|
|
38
|
+
// The empty-state line: no history yet, or an empty trash.
|
|
39
|
+
function overlayEmpty(mode) {
|
|
40
|
+
return overlayMode(mode) === "trash" ? " trash is empty" : " no history yet"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Function: createBehaviors
|
|
44
|
+
// Builds the OverlayList behavior map for the given renderer environment.
|
|
45
|
+
function createBehaviors(env) {
|
|
46
|
+
const behaviors = {
|
|
47
|
+
bg: () => env.overlayBg,
|
|
48
|
+
borderFg: () => env.borderPrimaryActiveFg,
|
|
49
|
+
titleColor: () => env.textHeadingFg,
|
|
50
|
+
title: (_self, data) => overlayTitle(data.mode),
|
|
51
|
+
footer: (_self, data) => overlayFooter(data.mode),
|
|
52
|
+
footerFg: () => env.textSecondaryFg,
|
|
53
|
+
empty: (_self, data) => overlayEmpty(data.mode),
|
|
54
|
+
emptyFg: () => env.textSecondaryFg,
|
|
55
|
+
emptyVisible: (_self, data) => (unwrap(data.items)?.length ?? 0) === 0,
|
|
56
|
+
open: (_self, data) => Boolean(unwrap(data.open)),
|
|
57
|
+
}
|
|
58
|
+
if (env?.rows) {
|
|
59
|
+
behaviors.rows = (self, data) => env.rows(self, data)
|
|
60
|
+
}
|
|
61
|
+
if (env?.scrollTop) {
|
|
62
|
+
behaviors.scrollTop = (self, data) => env.scrollTop(self, data)
|
|
63
|
+
}
|
|
64
|
+
return behaviors
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { createBehaviors, overlayEmpty, overlayFooter, overlayMode, overlayTitle }
|
|
68
|
+
|
|
69
|
+
// EOF
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
<box width="72%" height="60%" border borderStyle="rounded"
|
|
3
3
|
out:title="title" out:titleColor="titleColor" out:borderColor="borderFg"
|
|
4
4
|
out:backgroundColor="bg" flexDirection="column">
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
<input ref="query" inout="query" flexGrow="1" height="1" out:focused="focused" on:enter="confirm"></input>
|
|
8
|
-
</box>
|
|
9
|
-
<scrollbox ref="list" flexGrow="1">
|
|
5
|
+
<text when="emptyVisible" out="empty" out:fg="emptyFg" paddingLeft="1" wrapMode="none"></text>
|
|
6
|
+
<scrollbox when="!emptyVisible" ref="list" flexGrow="1">
|
|
10
7
|
<box out="rows" flexDirection="column"></box>
|
|
11
8
|
</scrollbox>
|
|
12
9
|
<box flexDirection="row" height="1" paddingLeft="1">
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<li class="overlay-row" out:class="rowClass" out:data-current="rowCurrent" out:data-dim="rowDim" on:click="item.key!overlay:select">
|
|
2
|
+
<span class="overlay-marker" out="rowMarker"></span>
|
|
3
|
+
<span class="overlay-text" out="rowText"></span>
|
|
4
|
+
<span class="overlay-meta" out="rowMeta"></span>
|
|
5
|
+
</li>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Project: TinyCommand
|
|
2
|
+
// Author: Sebastien Pierre
|
|
3
|
+
// License: BSD
|
|
4
|
+
// Created: 2026-10-17
|
|
5
|
+
|
|
6
|
+
// Module: tcmd/components/OverlayRow
|
|
7
|
+
// One row of the shared list overlay (`u` history / `⇧T` trash). Reads
|
|
8
|
+
// `{ item: { text, meta, dim }, index, currentRow }`; the builder snapshots the
|
|
9
|
+
// overlay cursor into a plain `currentRow` boolean, with the shared `cursor`
|
|
10
|
+
// cell as fallback for hand-built bags.
|
|
11
|
+
//
|
|
12
|
+
// Environment: `accentSecondaryFg`, `textFg`, `textSecondaryFg`,
|
|
13
|
+
// `textHeadingFg`, `cursorBg` (current row background), `currentClass` (web).
|
|
14
|
+
|
|
15
|
+
import { unwrap } from "select/state/cells"
|
|
16
|
+
|
|
17
|
+
// Function: rowCurrent
|
|
18
|
+
// Whether the row is the overlay cursor: the plain `currentRow` flag wins over
|
|
19
|
+
// the shared `cursor` cell.
|
|
20
|
+
function rowCurrent(data) {
|
|
21
|
+
if (data.currentRow !== undefined) return data.currentRow === true
|
|
22
|
+
return data.index === unwrap(data.cursor)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Function: createBehaviors
|
|
26
|
+
// Builds the OverlayRow behavior map for the given renderer environment.
|
|
27
|
+
function createBehaviors(env) {
|
|
28
|
+
return {
|
|
29
|
+
rowBg: (_self, data) => (rowCurrent(data) ? env.cursorBg : undefined),
|
|
30
|
+
rowClass: (_self, data) => (rowCurrent(data) ? env.currentClass : null),
|
|
31
|
+
rowCurrent: (_self, data) => `${rowCurrent(data)}`,
|
|
32
|
+
rowDim: (_self, data) => `${Boolean(data.item?.dim)}`,
|
|
33
|
+
rowMarker: (_self, data) => (rowCurrent(data) ? "> " : " "),
|
|
34
|
+
rowMarkerFg: () => env.accentSecondaryFg,
|
|
35
|
+
rowText: (_self, data) => data.item?.text ?? "",
|
|
36
|
+
// The cursor row reads bright; an undone/dim row (a superseded history
|
|
37
|
+
// entry) takes the secondary colour, like a hint.
|
|
38
|
+
rowTextFg: (_self, data) =>
|
|
39
|
+
rowCurrent(data) ? env.textHeadingFg : data.item?.dim ? env.textSecondaryFg : env.textFg,
|
|
40
|
+
rowMeta: (_self, data) => data.item?.meta ?? "",
|
|
41
|
+
rowMetaFg: () => env.textSecondaryFg,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { createBehaviors, rowCurrent }
|
|
46
|
+
|
|
47
|
+
// EOF
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<box flexDirection="row" height="1" columnGap="1" out:backgroundColor="rowBg" on:click="item.key!overlay:select">
|
|
2
|
+
<text out="rowMarker" width="2" out:fg="rowMarkerFg"></text>
|
|
3
|
+
<box flexGrow="1" minWidth="0" overflow="hidden">
|
|
4
|
+
<text out="rowText" out:fg="rowTextFg" wrapMode="none" truncate="true"></text>
|
|
5
|
+
</box>
|
|
6
|
+
<box flexShrink="0" overflow="hidden">
|
|
7
|
+
<text out="rowMeta" out:fg="rowMetaFg" wrapMode="none" truncate="true"></text>
|
|
8
|
+
</box>
|
|
9
|
+
</box>
|
|
@@ -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)}`
|
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
// Module: tcmd/components/format
|
|
7
7
|
// Presentation-only formatting helpers (sizes, counts, header title) shared by
|
|
8
|
-
// every component for every renderer.
|
|
8
|
+
// every component for every renderer. Size and disk formatting come from
|
|
9
|
+
// `core/format` because operations report the same values.
|
|
10
|
+
|
|
11
|
+
import { formatDisk, formatSize } from "../core/format.js"
|
|
9
12
|
|
|
10
13
|
// Function: formatTitle
|
|
11
14
|
// Header line `TinyCmd - HOST DIR/BASE` for the given directory. When it
|
|
@@ -78,28 +81,6 @@ function elideCrumbs(segments, width) {
|
|
|
78
81
|
return [marker, ...rest]
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
// Function: formatSize
|
|
82
|
-
// Human-readable byte size; directories pass through their own label.
|
|
83
|
-
function formatSize(bytes) {
|
|
84
|
-
const value = Number(bytes ?? 0)
|
|
85
|
-
if (value < 1024) return `${value}B`
|
|
86
|
-
const units = ["K", "M", "G", "T"]
|
|
87
|
-
let size = value / 1024
|
|
88
|
-
let unit = 0
|
|
89
|
-
while (size >= 1024 && unit < units.length - 1) {
|
|
90
|
-
size /= 1024
|
|
91
|
-
unit++
|
|
92
|
-
}
|
|
93
|
-
return `${size >= 100 ? Math.round(size) : size.toFixed(1)}${units[unit]}`
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Function: formatDisk
|
|
97
|
-
// Compact free/total disk line, e.g. "12G free of 256G".
|
|
98
|
-
function formatDisk(disk) {
|
|
99
|
-
if (!disk) return ""
|
|
100
|
-
return `${formatSize(disk.free)} free of ${formatSize(disk.total)}`
|
|
101
|
-
}
|
|
102
|
-
|
|
103
84
|
// Function: formatTime
|
|
104
85
|
// Local `YYYY-MM-DD HH:MM` stamp for a millisecond epoch; empty when absent.
|
|
105
86
|
function formatTime(ms) {
|
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
// Created: 2026-10-09
|
|
5
5
|
|
|
6
6
|
// Module: tcmd/components/icons
|
|
7
|
-
// Nerd Font
|
|
8
|
-
// (directory, symlink, device node)
|
|
9
|
-
// generic file icon
|
|
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.
|
|
@@ -164,6 +179,7 @@ function extensionOf(name) {
|
|
|
164
179
|
// file icon.
|
|
165
180
|
function iconFor(entry) {
|
|
166
181
|
if (!entry) return ""
|
|
182
|
+
if (entry.add) return entry.isDir ? DIR_ICON : FILE_ICON
|
|
167
183
|
if (entry.isSymlink) return SYMLINK_ICON
|
|
168
184
|
if (entry.isDevice) return DEVICE_ICON
|
|
169
185
|
if (entry.isDir) return DIR_ICON
|
|
@@ -172,6 +188,141 @@ function iconFor(entry) {
|
|
|
172
188
|
return NAME_ICONS[plain] ?? EXTENSION_ICONS[extensionOf(name)] ?? FILE_ICON
|
|
173
189
|
}
|
|
174
190
|
|
|
191
|
+
// Function: lucideIconFor
|
|
192
|
+
// Lucide's outline icons are used by the web renderers, where SVG works without
|
|
193
|
+
// a patched terminal font. The TUI keeps the detailed Nerd Font mapping above;
|
|
194
|
+
// the web uses a smaller set of semantic file-kind icons.
|
|
195
|
+
const LUCIDE_NAME_ICONS = {
|
|
196
|
+
cmakelists: "file-code",
|
|
197
|
+
dockerfile: "file-code",
|
|
198
|
+
env: "settings",
|
|
199
|
+
gitattributes: "settings",
|
|
200
|
+
gitconfig: "settings",
|
|
201
|
+
gitignore: "settings",
|
|
202
|
+
gitmodules: "settings",
|
|
203
|
+
gnumakefile: "file-code",
|
|
204
|
+
license: "file-text",
|
|
205
|
+
licence: "file-text",
|
|
206
|
+
makefile: "file-code",
|
|
207
|
+
npmrc: "settings",
|
|
208
|
+
readme: "file-text",
|
|
209
|
+
}
|
|
210
|
+
const LUCIDE_EXTENSION_ICONS = {
|
|
211
|
+
"7z": "file-archive",
|
|
212
|
+
aac: "audio-lines",
|
|
213
|
+
avi: "video",
|
|
214
|
+
bmp: "file-image",
|
|
215
|
+
bz2: "file-archive",
|
|
216
|
+
c: "file-code",
|
|
217
|
+
cc: "file-code",
|
|
218
|
+
cjs: "file-code",
|
|
219
|
+
clj: "file-code",
|
|
220
|
+
cmake: "file-code",
|
|
221
|
+
csv: "file-text",
|
|
222
|
+
cfg: "settings",
|
|
223
|
+
conf: "settings",
|
|
224
|
+
cpp: "file-code",
|
|
225
|
+
cs: "file-code",
|
|
226
|
+
css: "file-code",
|
|
227
|
+
d: "file-code",
|
|
228
|
+
db: "database",
|
|
229
|
+
diff: "file-code",
|
|
230
|
+
ex: "file-code",
|
|
231
|
+
exs: "file-code",
|
|
232
|
+
env: "settings",
|
|
233
|
+
flac: "audio-lines",
|
|
234
|
+
gif: "file-image",
|
|
235
|
+
go: "file-code",
|
|
236
|
+
gql: "file-code",
|
|
237
|
+
graphql: "file-code",
|
|
238
|
+
gz: "file-archive",
|
|
239
|
+
h: "file-code",
|
|
240
|
+
hbs: "file-code",
|
|
241
|
+
hs: "file-code",
|
|
242
|
+
hpp: "file-code",
|
|
243
|
+
htm: "file-code",
|
|
244
|
+
html: "file-code",
|
|
245
|
+
ini: "settings",
|
|
246
|
+
java: "file-code",
|
|
247
|
+
jar: "file-archive",
|
|
248
|
+
jpeg: "file-image",
|
|
249
|
+
jpg: "file-image",
|
|
250
|
+
js: "file-code",
|
|
251
|
+
jsx: "file-code",
|
|
252
|
+
json: "file-code",
|
|
253
|
+
kt: "file-code",
|
|
254
|
+
kts: "file-code",
|
|
255
|
+
less: "file-code",
|
|
256
|
+
lock: "settings",
|
|
257
|
+
log: "file-text",
|
|
258
|
+
lua: "file-code",
|
|
259
|
+
m4a: "audio-lines",
|
|
260
|
+
md: "file-text",
|
|
261
|
+
markdown: "file-text",
|
|
262
|
+
mk: "file-code",
|
|
263
|
+
mkv: "video",
|
|
264
|
+
mov: "video",
|
|
265
|
+
mp3: "audio-lines",
|
|
266
|
+
mp4: "video",
|
|
267
|
+
mjs: "file-code",
|
|
268
|
+
nim: "file-code",
|
|
269
|
+
ogg: "audio-lines",
|
|
270
|
+
pdf: "file-text",
|
|
271
|
+
perl: "file-code",
|
|
272
|
+
patch: "file-code",
|
|
273
|
+
php: "file-code",
|
|
274
|
+
pl: "file-code",
|
|
275
|
+
png: "file-image",
|
|
276
|
+
ps1: "file-code",
|
|
277
|
+
py: "file-code",
|
|
278
|
+
r: "file-code",
|
|
279
|
+
rb: "file-code",
|
|
280
|
+
rar: "file-archive",
|
|
281
|
+
rs: "file-code",
|
|
282
|
+
sass: "file-code",
|
|
283
|
+
scala: "file-code",
|
|
284
|
+
scss: "file-code",
|
|
285
|
+
sh: "file-code",
|
|
286
|
+
sql: "file-code",
|
|
287
|
+
sqlite: "database",
|
|
288
|
+
sqlite3: "database",
|
|
289
|
+
svg: "file-image",
|
|
290
|
+
swift: "file-code",
|
|
291
|
+
tar: "file-archive",
|
|
292
|
+
text: "file-text",
|
|
293
|
+
toml: "settings",
|
|
294
|
+
ts: "file-code",
|
|
295
|
+
tsv: "file-text",
|
|
296
|
+
txt: "file-text",
|
|
297
|
+
tsx: "file-code",
|
|
298
|
+
vim: "file-code",
|
|
299
|
+
vue: "file-code",
|
|
300
|
+
wav: "audio-lines",
|
|
301
|
+
webm: "video",
|
|
302
|
+
webp: "file-image",
|
|
303
|
+
xml: "file-code",
|
|
304
|
+
xz: "file-archive",
|
|
305
|
+
yaml: "file-code",
|
|
306
|
+
yml: "file-code",
|
|
307
|
+
zip: "file-archive",
|
|
308
|
+
zsh: "file-code",
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function lucideIconFor(entry) {
|
|
312
|
+
if (!entry) return ""
|
|
313
|
+
if (entry.add) return "folder-plus"
|
|
314
|
+
if (entry.isSymlink) return "link-2"
|
|
315
|
+
if (entry.isDevice) return "cpu"
|
|
316
|
+
if (entry.isDir) return "folder"
|
|
317
|
+
|
|
318
|
+
const name = `${entry.name ?? ""}`
|
|
319
|
+
const basename = name.slice(name.lastIndexOf("/") + 1)
|
|
320
|
+
const extension = extensionOf(basename)
|
|
321
|
+
const stem = extension ? basename.slice(0, -(extension.length + 1)) : basename
|
|
322
|
+
const plain = (basename.startsWith(".") ? basename.slice(1) : basename).toLowerCase()
|
|
323
|
+
return LUCIDE_NAME_ICONS[plain] ?? LUCIDE_NAME_ICONS[stem.toLowerCase()] ?? LUCIDE_EXTENSION_ICONS[extension] ?? "file"
|
|
324
|
+
}
|
|
325
|
+
|
|
175
326
|
// Function: iconsEnabled
|
|
176
327
|
// Whether rows show icons: on by default, `TCMD_ICONS=0|false|off` opts out
|
|
177
328
|
// for terminals without a Nerd Font.
|
|
@@ -180,6 +331,26 @@ function iconsEnabled(environment = globalThis.process?.env ?? {}) {
|
|
|
180
331
|
return value !== "0" && value !== "false" && value !== "off"
|
|
181
332
|
}
|
|
182
333
|
|
|
183
|
-
|
|
334
|
+
// Function: sourceMarker
|
|
335
|
+
// The marker a command-panel entry carries: the Nerd Font glyph when icons are
|
|
336
|
+
// on, the three-character tag otherwise (browsers lack the font).
|
|
337
|
+
function sourceMarker(source, icons) {
|
|
338
|
+
if (icons) return SOURCE_ICON[source] ?? ""
|
|
339
|
+
return SOURCE_TAG[source] ?? ""
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export {
|
|
343
|
+
DEVICE_ICON,
|
|
344
|
+
DIR_ICON,
|
|
345
|
+
FILE_ICON,
|
|
346
|
+
SOURCE_ICON,
|
|
347
|
+
SOURCE_TAG,
|
|
348
|
+
SYMLINK_ICON,
|
|
349
|
+
extensionOf,
|
|
350
|
+
iconFor,
|
|
351
|
+
iconsEnabled,
|
|
352
|
+
lucideIconFor,
|
|
353
|
+
sourceMarker,
|
|
354
|
+
}
|
|
184
355
|
|
|
185
356
|
// EOF
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
// Binding
|
|
13
13
|
export { bind } from "./bind.js"
|
|
14
|
-
export { commandBarMarks, commandBarMarksItem,
|
|
14
|
+
export { commandBarFit, commandBarMarks, commandBarMarksItem, commandBarStatus } from "./CommandBar.js"
|
|
15
15
|
|
|
16
16
|
// Behaviors
|
|
17
17
|
export { createBehaviors as appBehaviors, createEvents as appEvents, createInit as appInit } from "./App.js"
|
|
@@ -22,20 +22,24 @@ 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"
|
|
29
28
|
export { createBehaviors as fileListBehaviors } from "./FileList.js"
|
|
29
|
+
export { createBehaviors as filePickerBehaviors } from "./FilePicker.js"
|
|
30
|
+
export { createBehaviors as filePickerRowBehaviors } from "./FilePickerRow.js"
|
|
30
31
|
export { createBehaviors as filePreviewBehaviors } from "./FilePreview.js"
|
|
31
32
|
export { createBehaviors as fileRowBehaviors } from "./FileRow.js"
|
|
32
33
|
export { createBehaviors as filterBarBehaviors } from "./FilterBar.js"
|
|
33
|
-
export { createBehaviors as
|
|
34
|
+
export { createBehaviors as helpBehaviors } from "./Help.js"
|
|
35
|
+
export { createRowBehaviors as helpRowBehaviors } from "./Help.js"
|
|
36
|
+
export { createBehaviors as overlayListBehaviors } from "./OverlayList.js"
|
|
37
|
+
export { createBehaviors as overlayRowBehaviors } from "./OverlayRow.js"
|
|
34
38
|
export { createBehaviors as summaryBehaviors } from "./Summary.js"
|
|
35
39
|
|
|
36
40
|
// Helpers
|
|
37
41
|
export { formatCounts, formatDisk, formatMode, formatSize, formatTime } from "./format.js"
|
|
38
|
-
export { iconFor, iconsEnabled } from "./icons.js"
|
|
39
|
-
export { bookmarkRows, commandItemRows, crumbRows, fileRows, fileWindow,
|
|
42
|
+
export { iconFor, iconsEnabled, lucideIconFor, sourceMarker } from "./icons.js"
|
|
43
|
+
export { bookmarkRows, commandItemRows, crumbRows, filePickerRows, fileRows, fileWindow, helpRows, listingWindow, overlayRows, panelArgRows, panelOpRows, panelRunRows } from "./rows.js"
|
|
40
44
|
|
|
41
45
|
// EOF
|