numux 1.16.1 → 1.17.0
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/dist/numux.js +75 -1
- package/package.json +1 -1
package/dist/numux.js
CHANGED
|
@@ -36,7 +36,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
36
36
|
var require_package = __commonJS((exports, module) => {
|
|
37
37
|
module.exports = {
|
|
38
38
|
name: "numux",
|
|
39
|
-
version: "1.
|
|
39
|
+
version: "1.17.0",
|
|
40
40
|
description: "Terminal multiplexer with dependency orchestration",
|
|
41
41
|
type: "module",
|
|
42
42
|
license: "MIT",
|
|
@@ -2019,6 +2019,7 @@ var STATUS_HINTS = [
|
|
|
2019
2019
|
[SHORTCUTS.search.label, SHORTCUTS.search.description],
|
|
2020
2020
|
[SHORTCUTS.copy.label, SHORTCUTS.copy.description],
|
|
2021
2021
|
[SHORTCUTS.clear.label, SHORTCUTS.clear.description],
|
|
2022
|
+
["Ctrl+Click", "open link"],
|
|
2022
2023
|
["Ctrl+C", "quit"]
|
|
2023
2024
|
];
|
|
2024
2025
|
var STATUS_BAR_TEXT = STATUS_HINTS.map(([l, d]) => `${l}: ${d}`).join(" ");
|
|
@@ -2027,12 +2028,57 @@ var STATUS_BAR_TEXT = STATUS_HINTS.map(([l, d]) => `${l}: ${d}`).join(" ");
|
|
|
2027
2028
|
import { ScrollBoxRenderable } from "@opentui/core";
|
|
2028
2029
|
import { GhosttyTerminalRenderable } from "ghostty-opentui/terminal-buffer";
|
|
2029
2030
|
|
|
2031
|
+
// src/ui/url-handler.ts
|
|
2032
|
+
var URL_RE = /https?:\/\/[^\s<>"'`)\]},;]+/g;
|
|
2033
|
+
var FILE_PATH_RE = /(?:\.\.?\/|\/)[^\s:]+(?::(\d+)(?::(\d+))?)?/g;
|
|
2034
|
+
var TRAILING_PUNCT = /[.,;:!?)>\]'"]+$/;
|
|
2035
|
+
function findLinksInLine(line) {
|
|
2036
|
+
const links = [];
|
|
2037
|
+
for (const m of line.matchAll(URL_RE)) {
|
|
2038
|
+
const url = m[0].replace(TRAILING_PUNCT, "");
|
|
2039
|
+
links.push({
|
|
2040
|
+
url,
|
|
2041
|
+
start: m.index,
|
|
2042
|
+
end: m.index + url.length,
|
|
2043
|
+
type: "url"
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
for (const m of line.matchAll(FILE_PATH_RE)) {
|
|
2047
|
+
const start = m.index;
|
|
2048
|
+
const end = m.index + m[0].length;
|
|
2049
|
+
if (links.some((l) => start < l.end && end > l.start))
|
|
2050
|
+
continue;
|
|
2051
|
+
links.push({
|
|
2052
|
+
url: m[0],
|
|
2053
|
+
start,
|
|
2054
|
+
end,
|
|
2055
|
+
type: "file"
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
return links.sort((a, b) => a.start - b.start);
|
|
2059
|
+
}
|
|
2060
|
+
function findLinkAtPosition(line, col) {
|
|
2061
|
+
const links = findLinksInLine(line);
|
|
2062
|
+
return links.find((l) => col >= l.start && col < l.end) ?? null;
|
|
2063
|
+
}
|
|
2064
|
+
function openLink(link) {
|
|
2065
|
+
const opener = process.platform === "darwin" ? "open" : process.platform === "linux" ? "xdg-open" : null;
|
|
2066
|
+
if (!opener)
|
|
2067
|
+
return;
|
|
2068
|
+
try {
|
|
2069
|
+
const proc = Bun.spawn([opener, link.url]);
|
|
2070
|
+
proc.exited.catch(() => {});
|
|
2071
|
+
} catch {}
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
// src/ui/pane.ts
|
|
2030
2075
|
class Pane {
|
|
2031
2076
|
scrollBox;
|
|
2032
2077
|
terminal;
|
|
2033
2078
|
decoder = new TextDecoder;
|
|
2034
2079
|
_onScroll = null;
|
|
2035
2080
|
_onCopy = null;
|
|
2081
|
+
_onLinkClick = null;
|
|
2036
2082
|
_textLines = null;
|
|
2037
2083
|
_textLinesLower = null;
|
|
2038
2084
|
constructor(renderer, name, cols, rows, interactive = false) {
|
|
@@ -2067,6 +2113,15 @@ class Pane {
|
|
|
2067
2113
|
}
|
|
2068
2114
|
return result;
|
|
2069
2115
|
};
|
|
2116
|
+
this.terminal.onMouseDown = (event) => {
|
|
2117
|
+
if (event.modifiers.ctrl && event.button === 0) {
|
|
2118
|
+
const link = this.getLinkAtMouse(event.x, event.y);
|
|
2119
|
+
if (link) {
|
|
2120
|
+
event.stopPropagation();
|
|
2121
|
+
this._onLinkClick?.(link);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2070
2125
|
this.scrollBox.add(this.terminal);
|
|
2071
2126
|
}
|
|
2072
2127
|
feed(data) {
|
|
@@ -2105,6 +2160,21 @@ class Pane {
|
|
|
2105
2160
|
onCopy(handler) {
|
|
2106
2161
|
this._onCopy = handler;
|
|
2107
2162
|
}
|
|
2163
|
+
onLinkClick(handler) {
|
|
2164
|
+
this._onLinkClick = handler;
|
|
2165
|
+
}
|
|
2166
|
+
getLinkAtMouse(localX, localY) {
|
|
2167
|
+
if (!this._textLines) {
|
|
2168
|
+
const text = this.terminal.getText();
|
|
2169
|
+
this._textLines = text.split(`
|
|
2170
|
+
`);
|
|
2171
|
+
this._textLinesLower = this._textLines.map((l) => l.toLowerCase());
|
|
2172
|
+
}
|
|
2173
|
+
const lineIndex = Math.floor(this.scrollBox.scrollTop) + localY;
|
|
2174
|
+
if (lineIndex < 0 || lineIndex >= this._textLines.length)
|
|
2175
|
+
return null;
|
|
2176
|
+
return findLinkAtPosition(this._textLines[lineIndex], localX);
|
|
2177
|
+
}
|
|
2108
2178
|
show() {
|
|
2109
2179
|
this.scrollBox.visible = true;
|
|
2110
2180
|
}
|
|
@@ -2554,6 +2624,10 @@ class App {
|
|
|
2554
2624
|
this.copyToClipboard(text);
|
|
2555
2625
|
this.statusBar.showTemporaryMessage("Copied!");
|
|
2556
2626
|
});
|
|
2627
|
+
pane.onLinkClick((link) => {
|
|
2628
|
+
openLink(link);
|
|
2629
|
+
this.statusBar.showTemporaryMessage(`Opening ${link.url}`);
|
|
2630
|
+
});
|
|
2557
2631
|
pane.onScroll(() => {
|
|
2558
2632
|
if (this.searchMode && this.searchMatches.length > 0 && this.activePane === name) {
|
|
2559
2633
|
this.updateSearchHighlights();
|