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.
Files changed (2) hide show
  1. package/dist/numux.js +75 -1
  2. 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.16.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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numux",
3
- "version": "1.16.1",
3
+ "version": "1.17.0",
4
4
  "description": "Terminal multiplexer with dependency orchestration",
5
5
  "type": "module",
6
6
  "license": "MIT",