@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.
Files changed (134) hide show
  1. package/README.md +256 -62
  2. package/bin/tcmd +10 -2
  3. package/bin/tcmd.js +13 -2
  4. package/desktop/dist/index.html +13 -0
  5. package/desktop/dist/main.js +20298 -0
  6. package/desktop/dist/style.css +1515 -0
  7. package/desktop/frontend/index.html +13 -0
  8. package/package.json +19 -6
  9. package/public/fonts/OFL.txt +93 -0
  10. package/public/index.html +14 -0
  11. package/public/licenses/LUCIDE.txt +43 -0
  12. package/public/main.js +20679 -0
  13. package/public/style.css +1515 -0
  14. package/scripts/build-desktop.js +31 -0
  15. package/scripts/serve-web.js +61 -0
  16. package/src/js/tcmd/components/App.html +80 -14
  17. package/src/js/tcmd/components/App.js +339 -114
  18. package/src/js/tcmd/components/App.xml +76 -22
  19. package/src/js/tcmd/components/BookmarkList.html +5 -5
  20. package/src/js/tcmd/components/BookmarkList.js +15 -41
  21. package/src/js/tcmd/components/BookmarkList.xml +1 -2
  22. package/src/js/tcmd/components/BookmarkRow.html +2 -1
  23. package/src/js/tcmd/components/BookmarkRow.js +14 -9
  24. package/src/js/tcmd/components/BookmarkRow.xml +1 -1
  25. package/src/js/tcmd/components/Breadcrumbs.js +1 -1
  26. package/src/js/tcmd/components/CommandArgRow.html +28 -0
  27. package/src/js/tcmd/components/CommandArgRow.js +146 -30
  28. package/src/js/tcmd/components/CommandArgRow.xml +55 -4
  29. package/src/js/tcmd/components/CommandBar.html +2 -2
  30. package/src/js/tcmd/components/CommandBar.js +33 -27
  31. package/src/js/tcmd/components/CommandBar.xml +2 -2
  32. package/src/js/tcmd/components/CommandItem.html +1 -1
  33. package/src/js/tcmd/components/CommandItem.js +25 -25
  34. package/src/js/tcmd/components/CommandItem.xml +2 -2
  35. package/src/js/tcmd/components/CommandPanel.html +29 -0
  36. package/src/js/tcmd/components/CommandPanel.js +78 -52
  37. package/src/js/tcmd/components/CommandPanel.xml +49 -25
  38. package/src/js/tcmd/components/Confirm.html +2 -2
  39. package/src/js/tcmd/components/EntryDetail.html +7 -7
  40. package/src/js/tcmd/components/EntryDetail.js +22 -8
  41. package/src/js/tcmd/components/FileList.html +5 -1
  42. package/src/js/tcmd/components/FileList.js +2 -1
  43. package/src/js/tcmd/components/FilePicker.html +10 -0
  44. package/src/js/tcmd/components/FilePicker.js +43 -0
  45. package/src/js/tcmd/components/FilePicker.xml +13 -0
  46. package/src/js/tcmd/components/FilePickerRow.html +6 -0
  47. package/src/js/tcmd/components/FilePickerRow.js +27 -0
  48. package/src/js/tcmd/components/FilePickerRow.xml +10 -0
  49. package/src/js/tcmd/components/FilePreview.html +14 -4
  50. package/src/js/tcmd/components/FilePreview.js +111 -12
  51. package/src/js/tcmd/components/FilePreview.xml +28 -8
  52. package/src/js/tcmd/components/FileRow.html +2 -1
  53. package/src/js/tcmd/components/FileRow.js +70 -12
  54. package/src/js/tcmd/components/FilterBar.html +1 -1
  55. package/src/js/tcmd/components/FilterBar.js +14 -4
  56. package/src/js/tcmd/components/FilterBar.xml +1 -1
  57. package/src/js/tcmd/components/Frame.js +324 -0
  58. package/src/js/tcmd/components/Help.html +15 -0
  59. package/src/js/tcmd/components/Help.js +260 -0
  60. package/src/js/tcmd/components/Help.xml +13 -4
  61. package/src/js/tcmd/components/HelpRow.html +8 -0
  62. package/src/js/tcmd/components/HelpRow.xml +7 -0
  63. package/src/js/tcmd/components/OverlayList.html +8 -0
  64. package/src/js/tcmd/components/OverlayList.js +69 -0
  65. package/src/js/tcmd/components/{CommandPalette.xml → OverlayList.xml} +2 -5
  66. package/src/js/tcmd/components/OverlayRow.html +5 -0
  67. package/src/js/tcmd/components/OverlayRow.js +47 -0
  68. package/src/js/tcmd/components/OverlayRow.xml +9 -0
  69. package/src/js/tcmd/components/Summary.html +1 -1
  70. package/src/js/tcmd/components/Summary.js +1 -6
  71. package/src/js/tcmd/components/format.js +4 -23
  72. package/src/js/tcmd/components/icons.js +176 -5
  73. package/src/js/tcmd/components/index.js +9 -5
  74. package/src/js/tcmd/components/rows.js +176 -48
  75. package/src/js/tcmd/core/base64.js +64 -0
  76. package/src/js/tcmd/core/bookmarks.js +45 -8
  77. package/src/js/tcmd/core/commands.js +978 -260
  78. package/src/js/tcmd/core/concurrency.js +59 -0
  79. package/src/js/tcmd/core/decompress.js +117 -0
  80. package/src/js/tcmd/core/directory.js +219 -93
  81. package/src/js/tcmd/core/format.js +36 -0
  82. package/src/js/tcmd/core/fs.js +438 -54
  83. package/src/js/tcmd/core/history.js +58 -0
  84. package/src/js/tcmd/core/index.js +3 -9
  85. package/src/js/tcmd/core/journal.js +93 -0
  86. package/src/js/tcmd/core/keymap.js +42 -6
  87. package/src/js/tcmd/core/operations.js +813 -48
  88. package/src/js/tcmd/core/os.js +29 -3
  89. package/src/js/tcmd/core/pane.js +33 -8
  90. package/src/js/tcmd/core/panel.js +280 -104
  91. package/src/js/tcmd/core/path.js +30 -3
  92. package/src/js/tcmd/core/permissions.js +107 -0
  93. package/src/js/tcmd/core/picker.js +156 -0
  94. package/src/js/tcmd/core/preview.js +102 -0
  95. package/src/js/tcmd/core/script.js +7 -95
  96. package/src/js/tcmd/core/selection.js +9 -1
  97. package/src/js/tcmd/core/session.js +498 -200
  98. package/src/js/tcmd/core/watch.js +153 -0
  99. package/src/js/tcmd/desktop/backend/api.js +165 -0
  100. package/src/js/tcmd/desktop/backend/fs.js +626 -0
  101. package/src/js/tcmd/desktop/backend/main.ts +9 -0
  102. package/src/js/tcmd/desktop/bookmarks.js +23 -0
  103. package/src/js/tcmd/desktop/fs.js +91 -0
  104. package/src/js/tcmd/desktop/main.js +194 -0
  105. package/src/js/tcmd/desktop/os.js +33 -0
  106. package/src/js/tcmd/index.js +0 -1
  107. package/src/js/tcmd/tui/app.js +118 -60
  108. package/src/js/tcmd/tui/catalogue.js +238 -42
  109. package/src/js/tcmd/tui/index.js +18 -19
  110. package/src/js/tcmd/tui/opentui.js +33 -0
  111. package/src/js/tcmd/tui/syntax.js +91 -0
  112. package/src/js/tcmd/tui/theme.js +32 -2
  113. package/src/js/tcmd/vendor/bzip2.js +276 -0
  114. package/src/js/tcmd/web/app.js +225 -41
  115. package/src/js/tcmd/web/bookmarks.js +36 -0
  116. package/src/js/tcmd/web/catalogue.js +81 -38
  117. package/src/js/tcmd/web/effects.js +223 -0
  118. package/src/js/tcmd/web/header.html +1 -1
  119. package/src/js/tcmd/web/header.js +15 -11
  120. package/src/js/tcmd/web/http.js +13 -0
  121. package/src/js/tcmd/web/index.js +2 -2
  122. package/src/js/tcmd/web/main.js +6 -66
  123. package/src/js/tcmd/web/opfs.js +297 -30
  124. package/src/js/tcmd/web/seed.js +4 -4
  125. package/src/js/tcmd/web/server.js +124 -21
  126. package/src/js/tcmd/web/theme.js +4 -0
  127. package/tinyjs.json +16 -0
  128. package/src/js/tcmd/components/CommandPalette.html +0 -7
  129. package/src/js/tcmd/components/CommandPalette.js +0 -58
  130. package/src/js/tcmd/components/PaletteRow.html +0 -3
  131. package/src/js/tcmd/components/PaletteRow.js +0 -43
  132. package/src/js/tcmd/components/PaletteRow.xml +0 -4
  133. package/src/js/tcmd/tui/frame.js +0 -269
  134. package/src/js/tcmd/tui/help.js +0 -32
@@ -14,6 +14,13 @@
14
14
  // - `edit(path)`: Promise<void> - $EDITOR (or vi)
15
15
  // - `run(command, options)`: Promise<void> - `$SHELL -c command` in
16
16
  // `options.cwd`
17
+ // - `copy(text)`: boolean - best-effort clipboard write. The TUI sends OSC 52
18
+ // through the renderer; the browser and desktop webview use the Clipboard
19
+ // API. False when the host has no clipboard.
20
+ // - `canEdit`: boolean - whether `edit` can launch an editor at all. A host
21
+ // without one (the web's null adapter) sets it false, so Enter on a file
22
+ // falls back to the fullscreen inspector instead of erroring (see
23
+ // `core/commands#pane.enter`).
17
24
 
18
25
  // `node:child_process` is imported lazily: only `createNodeOs` needs it, so
19
26
  // bundles that use the null adapter (the web view, tests) never pull in a Node
@@ -29,9 +36,14 @@ function loadChildProcess() {
29
36
 
30
37
  // Function: createNodeOs
31
38
  // Node/Bun-backed host adapter. `suspend`/`resume` are optional hooks the TUI
32
- // uses to release and restore the terminal around interactive commands.
33
- function createNodeOs({ suspend, resume } = {}) {
39
+ // uses to release and restore the terminal around interactive commands;
40
+ // `copy` is the TUI's OSC 52 hook (`renderer.copyToClipboardOSC52`).
41
+ function createNodeOs({ suspend, resume, copy } = {}) {
34
42
  return {
43
+ canEdit: true,
44
+ copy(text) {
45
+ return Boolean(copy?.(`${text ?? ""}`))
46
+ },
35
47
  async open(path) {
36
48
  await openWith(path, openerCandidates())
37
49
  },
@@ -48,14 +60,28 @@ function createNodeOs({ suspend, resume } = {}) {
48
60
  }
49
61
  }
50
62
 
63
+ // Function: copyToBrowserClipboard
64
+ // Best-effort clipboard write for a browser or desktop webview. The write is
65
+ // asynchronous but fire-and-forget; a refused write (no permission, no user
66
+ // gesture) leaves the clipboard untouched.
67
+ function copyToBrowserClipboard(text) {
68
+ const clipboard = globalThis.navigator?.clipboard
69
+ if (!clipboard?.writeText) return false
70
+ void clipboard.writeText(`${text ?? ""}`).catch(() => {})
71
+ return true
72
+ }
73
+
51
74
  // Function: createNullOs
52
75
  // Host adapter for environments without a shell (web): every operation fails
53
76
  // with a clear message, which the dispatch path reports in the command bar.
77
+ // Clipboard access is the exception: the browser has one.
54
78
  function createNullOs() {
55
79
  const unsupported = (name) => async () => {
56
80
  throw new Error(`${name} is not available in this environment`)
57
81
  }
58
82
  return {
83
+ canEdit: false,
84
+ copy: (text) => copyToBrowserClipboard(text),
59
85
  open: unsupported("open"),
60
86
  edit: unsupported("edit"),
61
87
  run: unsupported("run"),
@@ -123,6 +149,6 @@ async function runDetached(command, args) {
123
149
  })
124
150
  }
125
151
 
126
- export { createNodeOs, createNullOs }
152
+ export { copyToBrowserClipboard, createNodeOs, createNullOs }
127
153
 
128
154
  // EOF
@@ -6,8 +6,11 @@
6
6
  // Module: tcmd/core/pane
7
7
  // A Pane is one column with a single directory: it owns that directory and
8
8
  // proxies its cells (path, entries, cursor, marks, disk, …) as pane-level
9
- // cells, so views and commands address the pane directly. Navigation is async
10
- // and updates the directory's `rawEntries` in place.
9
+ // cells, so views and commands address the pane directly. Navigation reads the
10
+ // target first and commits atomically (see `core/directory#read`), so the pane
11
+ // keeps showing its current listing until the replacement is ready. The pane
12
+ // also owns its navigation history (back/forward snapshots of the view), which
13
+ // the session drives to restore position, selection and focus.
11
14
 
12
15
  import { cell } from "select/state/cells"
13
16
  import { createDirectory } from "./directory.js"
@@ -22,6 +25,23 @@ const PANE_BUFFERS = { left: "A", right: "B" }
22
25
  // destination (DST).
23
26
  const PANE_LABELS = { left: "SRC", right: "DST" }
24
27
 
28
+ // Function: currentPaneId
29
+ // The id of the pane that owns the current directory: the focused pane while
30
+ // focus is on source/destination, the anchor pane otherwise.
31
+ function currentPaneId(session) {
32
+ return session.currentId?.value ?? session.active?.value ?? "left"
33
+ }
34
+
35
+ // Function: currentPane
36
+ // The pane that owns the current directory: the focused pane while focus is
37
+ // on source/destination, the anchor pane while it is on an auxiliary column
38
+ // (see `core/session`). Path-changing commands — open, up, history,
39
+ // breadcrumbs, relative arguments — target it; only cursor movement follows
40
+ // the focused column. `core/commands` keeps `pane()` for the latter.
41
+ function currentPane(session) {
42
+ return session.panes[currentPaneId(session)] ?? session.panes.left
43
+ }
44
+
25
45
  // Function: createPane
26
46
  // Creates a pane bound to a filesystem adapter at `path`.
27
47
  //
@@ -31,10 +51,10 @@ const PANE_LABELS = { left: "SRC", right: "DST" }
31
51
  // await pane.refresh()
32
52
  // pane.entries.value // sorted, filtered FileEntry[]
33
53
  // ```
34
- function createPane({ fs, path: initialPath } = {}) {
54
+ function createPane({ fs, path: initialPath, context, watch } = {}) {
35
55
  const pane = {}
36
56
  pane.fs = fs
37
- pane.directory = createDirectory({ fs, path: initialPath })
57
+ pane.directory = createDirectory({ fs, path: initialPath, context, watch })
38
58
 
39
59
  // Proxy cells mirror the directory, so views address the pane uniformly.
40
60
  const dir = pane.directory
@@ -46,29 +66,34 @@ function createPane({ fs, path: initialPath } = {}) {
46
66
  pane.sort = dir.sort
47
67
  pane.entries = dir.entries
48
68
  pane.cursor = dir.cursor
49
- pane.cursorStat = dir.cursorStat
50
69
  pane.marked = dir.marked
51
70
  pane.disk = dir.disk
52
71
  pane.segments = dir.segments
72
+ pane.history = dir.history
73
+ pane.autoRefresh = dir.autoRefresh
53
74
 
54
75
  // Function: current
55
76
  // The pane's directory.
56
77
  pane.current = () => pane.directory
57
78
 
58
79
  // Function: refresh / navigate / up / open / back / forward /
59
- // currentEntry / toggleMark / clearFilter — delegate to the directory.
80
+ // currentEntry / toggleMark / setFilter / clearFilter — delegate to the
81
+ // directory.
60
82
  pane.refresh = () => pane.directory.refresh()
61
- pane.navigate = (path) => pane.directory.navigate(path)
83
+ pane.setAutoRefresh = (enabled) => pane.directory.setAutoRefresh(enabled)
84
+ pane.navigate = (path, options) => pane.directory.navigate(path, options)
62
85
  pane.up = () => pane.directory.up()
63
86
  pane.open = () => pane.directory.open()
64
87
  pane.back = () => pane.directory.back()
65
88
  pane.forward = () => pane.directory.forward()
66
89
  pane.currentEntry = () => pane.directory.currentEntry()
67
90
  pane.toggleMark = () => pane.directory.toggleMark()
91
+ pane.setFilter = (query) => pane.directory.setFilter(query)
68
92
  pane.clearFilter = () => pane.directory.clearFilter()
93
+ pane.setContext = (context) => pane.directory.setContext(context)
69
94
  pane.dispose = () => pane.directory.dispose()
70
95
 
71
96
  return pane
72
97
  }
73
98
 
74
- export { PANE_BUFFERS, PANE_LABELS, createPane }
99
+ export { PANE_BUFFERS, PANE_LABELS, createPane, currentPane, currentPaneId }