@ssebastien/tcmd 0.0.0-stage → 0.1.1
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 +234 -2
- package/bin/tcmd +17 -0
- package/package.json +37 -5
- package/src/js/tcmd/components/App.html +28 -0
- package/src/js/tcmd/components/App.js +355 -0
- package/src/js/tcmd/components/App.xml +98 -0
- package/src/js/tcmd/components/BookmarkList.html +12 -0
- package/src/js/tcmd/components/BookmarkList.js +73 -0
- package/src/js/tcmd/components/BookmarkList.xml +18 -0
- package/src/js/tcmd/components/BookmarkRow.html +5 -0
- package/src/js/tcmd/components/BookmarkRow.js +40 -0
- package/src/js/tcmd/components/BookmarkRow.xml +10 -0
- package/src/js/tcmd/components/Breadcrumbs.js +27 -0
- package/src/js/tcmd/components/CommandArgRow.js +90 -0
- package/src/js/tcmd/components/CommandArgRow.xml +5 -0
- package/src/js/tcmd/components/CommandBar.html +4 -0
- package/src/js/tcmd/components/CommandBar.js +126 -0
- package/src/js/tcmd/components/CommandBar.xml +4 -0
- package/src/js/tcmd/components/CommandItem.html +3 -0
- package/src/js/tcmd/components/CommandItem.js +78 -0
- package/src/js/tcmd/components/CommandItem.xml +6 -0
- package/src/js/tcmd/components/CommandPalette.html +7 -0
- package/src/js/tcmd/components/CommandPalette.js +58 -0
- package/src/js/tcmd/components/CommandPalette.xml +16 -0
- package/src/js/tcmd/components/CommandPanel.js +98 -0
- package/src/js/tcmd/components/CommandPanel.xml +35 -0
- package/src/js/tcmd/components/Confirm.html +3 -0
- package/src/js/tcmd/components/Confirm.js +36 -0
- package/src/js/tcmd/components/Confirm.xml +7 -0
- package/src/js/tcmd/components/Crumb.html +4 -0
- package/src/js/tcmd/components/Crumb.js +48 -0
- package/src/js/tcmd/components/Crumb.xml +4 -0
- package/src/js/tcmd/components/EntryDetail.html +15 -0
- package/src/js/tcmd/components/EntryDetail.js +194 -0
- package/src/js/tcmd/components/EntryDetail.xml +16 -0
- package/src/js/tcmd/components/FileList.html +1 -0
- package/src/js/tcmd/components/FileList.js +41 -0
- package/src/js/tcmd/components/FileList.xml +7 -0
- package/src/js/tcmd/components/FilePreview.html +5 -0
- package/src/js/tcmd/components/FilePreview.js +110 -0
- package/src/js/tcmd/components/FilePreview.xml +10 -0
- package/src/js/tcmd/components/FileRow.html +5 -0
- package/src/js/tcmd/components/FileRow.js +135 -0
- package/src/js/tcmd/components/FileRow.xml +10 -0
- package/src/js/tcmd/components/FilterBar.html +6 -0
- package/src/js/tcmd/components/FilterBar.js +50 -0
- package/src/js/tcmd/components/FilterBar.xml +6 -0
- package/src/js/tcmd/components/Help.xml +11 -0
- package/src/js/tcmd/components/PaletteRow.html +3 -0
- package/src/js/tcmd/components/PaletteRow.js +43 -0
- package/src/js/tcmd/components/PaletteRow.xml +4 -0
- package/src/js/tcmd/components/RowList.html +1 -0
- package/src/js/tcmd/components/RowList.xml +1 -0
- package/src/js/tcmd/components/Summary.html +3 -0
- package/src/js/tcmd/components/Summary.js +77 -0
- package/src/js/tcmd/components/Summary.xml +3 -0
- package/src/js/tcmd/components/bind.js +20 -0
- package/src/js/tcmd/components/format.js +155 -0
- package/src/js/tcmd/components/icons.js +185 -0
- package/src/js/tcmd/components/index.js +41 -0
- package/src/js/tcmd/components/rows.js +212 -0
- package/src/js/tcmd/core/bookmarks.js +175 -0
- package/src/js/tcmd/core/commands.js +930 -0
- package/src/js/tcmd/core/directory.js +194 -0
- package/src/js/tcmd/core/entries.js +88 -0
- package/src/js/tcmd/core/fs.js +266 -0
- package/src/js/tcmd/core/index.js +39 -0
- package/src/js/tcmd/core/keymap.js +60 -0
- package/src/js/tcmd/core/operations.js +453 -0
- package/src/js/tcmd/core/os.js +128 -0
- package/src/js/tcmd/core/pane.js +74 -0
- package/src/js/tcmd/core/panel.js +383 -0
- package/src/js/tcmd/core/path.js +57 -0
- package/src/js/tcmd/core/script.js +186 -0
- package/src/js/tcmd/core/selection.js +43 -0
- package/src/js/tcmd/core/session.js +405 -0
- package/src/js/tcmd/index.js +53 -0
- package/src/js/tcmd/tui/app.js +140 -0
- package/src/js/tcmd/tui/catalogue.js +300 -0
- package/src/js/tcmd/tui/frame.js +269 -0
- package/src/js/tcmd/tui/help.js +32 -0
- package/src/js/tcmd/tui/index.js +104 -0
- package/src/js/tcmd/tui/theme.js +177 -0
- package/src/js/tcmd/vendor/fzy.js +243 -0
- package/src/js/tcmd/web/app.js +154 -0
- package/src/js/tcmd/web/catalogue.js +159 -0
- package/src/js/tcmd/web/header.html +1 -0
- package/src/js/tcmd/web/header.js +38 -0
- package/src/js/tcmd/web/http.js +79 -0
- package/src/js/tcmd/web/index.js +22 -0
- package/src/js/tcmd/web/main.js +151 -0
- package/src/js/tcmd/web/opfs.js +282 -0
- package/src/js/tcmd/web/seed.js +90 -0
- package/src/js/tcmd/web/server.js +191 -0
- package/src/js/tcmd/web/theme.js +23 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// Project: TinyCommand
|
|
2
|
+
// Author: Sebastien Pierre
|
|
3
|
+
// License: BSD
|
|
4
|
+
// Created: 2026-10-09
|
|
5
|
+
|
|
6
|
+
// Module: tcmd/core/bookmarks
|
|
7
|
+
// Named bookmarks: a `{ name -> { path, isDir } }` map the user records with
|
|
8
|
+
// `b` and jumps to with `g`. A bookmark remembers whether its path is a file
|
|
9
|
+
// or a directory (or `null` when only a legacy path is known), so a jump can
|
|
10
|
+
// open a directory but select-and-preview a file. Bookmarks are pure model
|
|
11
|
+
// state (a reactive cell) plus a persistence layer: a `BookmarkStore`
|
|
12
|
+
// interface with an in-memory default (tests, web) and a Node/JSON-file
|
|
13
|
+
// implementation (the TUI). The session only ever sees the store interface, so
|
|
14
|
+
// the browser bundle stays free of Node builtins.
|
|
15
|
+
|
|
16
|
+
import { cell } from "select/state/cells"
|
|
17
|
+
|
|
18
|
+
// Function: normalizeName
|
|
19
|
+
// Trims a bookmark name; empty names are rejected by the callers.
|
|
20
|
+
function normalizeName(name) {
|
|
21
|
+
return `${name ?? ""}`.trim()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Function: isValidName
|
|
25
|
+
// A bookmark name is a non-empty token without whitespace or separator
|
|
26
|
+
// punctuation that would be ambiguous in the list.
|
|
27
|
+
function isValidName(name) {
|
|
28
|
+
const text = normalizeName(name)
|
|
29
|
+
return text.length > 0 && !/[\s/]/.test(text)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Function: normalizeEntry
|
|
33
|
+
// Coerces a stored value to `{ path, isDir }`: a plain string is a legacy
|
|
34
|
+
// path with an unknown kind (`isDir: null`), resolved when it is jumped to.
|
|
35
|
+
function normalizeEntry(value) {
|
|
36
|
+
if (typeof value === "string") return { path: value, isDir: null }
|
|
37
|
+
if (value && typeof value === "object" && typeof value.path === "string") {
|
|
38
|
+
return { path: value.path, isDir: value.isDir === true ? true : value.isDir === false ? false : null }
|
|
39
|
+
}
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Function: normalizeMap
|
|
44
|
+
// Coerces a stored `{ name -> value }` map, dropping unusable rows.
|
|
45
|
+
function normalizeMap(map) {
|
|
46
|
+
const out = {}
|
|
47
|
+
for (const [name, value] of Object.entries(map ?? {})) {
|
|
48
|
+
const entry = normalizeEntry(value)
|
|
49
|
+
if (entry) out[name] = entry
|
|
50
|
+
}
|
|
51
|
+
return out
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Function: createMemoryBookmarkStore
|
|
55
|
+
// In-memory store: bookmarks live for the process only. The default, used by
|
|
56
|
+
// tests and the web view (no filesystem surface for config).
|
|
57
|
+
function createMemoryBookmarkStore() {
|
|
58
|
+
return {
|
|
59
|
+
async load() {
|
|
60
|
+
return {}
|
|
61
|
+
},
|
|
62
|
+
async save() {},
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Function: createBookmarks
|
|
67
|
+
// Creates the bookmark model backed by `store` (defaults to in-memory) with
|
|
68
|
+
// initial `entries` (a `{ name -> path|{path,isDir} }` map). `load()` fills
|
|
69
|
+
// the cell from the store; `record(name, path, isDir)` sets a bookmark and
|
|
70
|
+
// persists; `remove(name)` deletes one; `clear()` empties the cell.
|
|
71
|
+
function createBookmarks({ store = createMemoryBookmarkStore(), entries = {} } = {}) {
|
|
72
|
+
const bookmarks = { store }
|
|
73
|
+
bookmarks.entries = cell(normalizeMap(entries))
|
|
74
|
+
|
|
75
|
+
bookmarks.load = async () => {
|
|
76
|
+
const loaded = await store.load?.()
|
|
77
|
+
if (loaded && typeof loaded === "object") bookmarks.entries.set(normalizeMap(loaded))
|
|
78
|
+
return bookmarks.entries.value
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Function: record
|
|
82
|
+
// Sets `name -> { path, isDir }` and persists. Returns the stored name.
|
|
83
|
+
bookmarks.record = async (name, path, isDir = null) => {
|
|
84
|
+
const clean = normalizeName(name)
|
|
85
|
+
if (!isValidName(clean)) throw new Error("bookmark needs a name")
|
|
86
|
+
bookmarks.entries.set({
|
|
87
|
+
...bookmarks.entries.value,
|
|
88
|
+
[clean]: { path, isDir: isDir === true ? true : isDir === false ? false : null },
|
|
89
|
+
})
|
|
90
|
+
await store.save?.(bookmarks.entries.value)
|
|
91
|
+
return clean
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Function: remove
|
|
95
|
+
// Deletes a bookmark and persists. Returns true when one was removed.
|
|
96
|
+
bookmarks.remove = async (name) => {
|
|
97
|
+
const clean = normalizeName(name)
|
|
98
|
+
if (clean in bookmarks.entries.value) {
|
|
99
|
+
const next = { ...bookmarks.entries.value }
|
|
100
|
+
delete next[clean]
|
|
101
|
+
bookmarks.entries.set(next)
|
|
102
|
+
await store.save?.(next)
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Function: set
|
|
109
|
+
// Replaces the whole map (e.g. a `bookmarks rm` script op), persisting.
|
|
110
|
+
bookmarks.set = async (map) => {
|
|
111
|
+
bookmarks.entries.set(normalizeMap(map))
|
|
112
|
+
await store.save?.(bookmarks.entries.value)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Function: find
|
|
116
|
+
// The `{ name, path, isDir }` bookmark whose path matches, or null.
|
|
117
|
+
bookmarks.find = (path) => {
|
|
118
|
+
for (const [name, entry] of Object.entries(bookmarks.entries.value)) {
|
|
119
|
+
if (entry.path === path) return { name, ...entry }
|
|
120
|
+
}
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Function: findByName
|
|
125
|
+
// The `{ name, path, isDir }` bookmark for a name, or null.
|
|
126
|
+
bookmarks.findByName = (name) => {
|
|
127
|
+
const entry = bookmarks.entries.value[normalizeName(name)]
|
|
128
|
+
return entry ? { name: normalizeName(name), ...entry } : null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Function: rows
|
|
132
|
+
// List rows `{ name, path, isDir }`, sorted by name, for the jump list.
|
|
133
|
+
bookmarks.rows = () =>
|
|
134
|
+
Object.entries(bookmarks.entries.value)
|
|
135
|
+
.map(([name, entry]) => ({ name, ...entry }))
|
|
136
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
137
|
+
|
|
138
|
+
return bookmarks
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Function: createFileBookmarkStore
|
|
142
|
+
// Node/JSON-file store. `readFile`/`writeFile`/`dir` are injected so the core
|
|
143
|
+
// carries no Node import (the TUI wires them from node:fs/promises and
|
|
144
|
+
// node:os). `load()` tolerates a missing or malformed file.
|
|
145
|
+
function createFileBookmarkStore({ file, readFile, writeFile, mkdir }) {
|
|
146
|
+
return {
|
|
147
|
+
async load() {
|
|
148
|
+
try {
|
|
149
|
+
const text = await readFile(file, "utf8")
|
|
150
|
+
const parsed = JSON.parse(text)
|
|
151
|
+
return parsed && typeof parsed === "object" ? parsed : {}
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return {}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
async save(map) {
|
|
157
|
+
try {
|
|
158
|
+
await mkdir?.(file.replace(/\/[^/]*$/, ""), { recursive: true })
|
|
159
|
+
await writeFile(file, `${JSON.stringify(map, null, 2)}\n`, "utf8")
|
|
160
|
+
} catch (error) {
|
|
161
|
+
// Persistence is best-effort; the in-memory map stays authoritative.
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export {
|
|
168
|
+
createBookmarks,
|
|
169
|
+
createFileBookmarkStore,
|
|
170
|
+
createMemoryBookmarkStore,
|
|
171
|
+
isValidName,
|
|
172
|
+
normalizeEntry,
|
|
173
|
+
normalizeMap,
|
|
174
|
+
normalizeName,
|
|
175
|
+
}
|