@linxin666/dsh-ssh 0.1.14 → 0.1.16

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/lib/client.js CHANGED
@@ -14554,6 +14554,7 @@ window.__ModuleLoader__.load({
14554
14554
  const termRef = (0, react.useRef)(null);
14555
14555
  const fitRef = (0, react.useRef)(null);
14556
14556
  const connRef = (0, react.useRef)(null);
14557
+ const dataSubRef = (0, react.useRef)(null);
14557
14558
  (0, react.useEffect)(() => {
14558
14559
  ensureXtermCss();
14559
14560
  }, []);
@@ -14586,6 +14587,8 @@ window.__ModuleLoader__.load({
14586
14587
  connection.onExit = void 0;
14587
14588
  connection.close();
14588
14589
  }
14590
+ dataSubRef.current?.dispose();
14591
+ dataSubRef.current = null;
14589
14592
  termRef.current?.dispose();
14590
14593
  termRef.current = null;
14591
14594
  fitRef.current = null;
@@ -14633,7 +14636,7 @@ window.__ModuleLoader__.load({
14633
14636
  fitRef.current = fit;
14634
14637
  connRef.current = connection;
14635
14638
  let settled = false;
14636
- const dataSub = term.onData((data) => {
14639
+ dataSubRef.current = term.onData((data) => {
14637
14640
  connection.send(data);
14638
14641
  });
14639
14642
  connection.onReady = () => {
@@ -14648,7 +14651,8 @@ window.__ModuleLoader__.load({
14648
14651
  connection.onExit = (code, error) => {
14649
14652
  if (settled) return;
14650
14653
  settled = true;
14651
- dataSub.dispose();
14654
+ dataSubRef.current?.dispose();
14655
+ dataSubRef.current = null;
14652
14656
  term.options.disableStdin = true;
14653
14657
  connRef.current = null;
14654
14658
  setStatus({
@@ -15059,6 +15063,24 @@ window.__ModuleLoader__.load({
15059
15063
  * mounted) with per-row stop, a stop-all action scoped to the selected alias,
15060
15064
  * and a new-tunnel form.
15061
15065
  */
15066
+ /** Live-tunnel polling interval while the tab is mounted (ms). */
15067
+ const TUNNEL_POLL_MS = 5e3;
15068
+ /**
15069
+ * Return `next` only when the tunnel list changed in a user-visible way
15070
+ * (identity, ordering or any renderable field), else `null` so a poll tick
15071
+ * with no real change keeps the previous reference and React skips the
15072
+ * re-render. `prev === null` (first load) always accepts the list.
15073
+ */
15074
+ function diffTunnels(prev, next) {
15075
+ if (prev === null) return next;
15076
+ if (prev.length !== next.length) return next;
15077
+ for (let index = 0; index < prev.length; index += 1) {
15078
+ const a = prev[index];
15079
+ const b = next[index];
15080
+ if (a.id !== b.id || a.alias !== b.alias || a.state !== b.state || a.localPort !== b.localPort || a.remoteHost !== b.remoteHost || a.remotePort !== b.remotePort || a.startedAt !== b.startedAt || a.error !== b.error) return next;
15081
+ }
15082
+ return null;
15083
+ }
15062
15084
  /** The tunnels tab. */
15063
15085
  function TunnelsTab({ api }) {
15064
15086
  const [hosts, setHosts] = (0, react.useState)([]);
@@ -15089,7 +15111,7 @@ window.__ModuleLoader__.load({
15089
15111
  try {
15090
15112
  const list = await api.listTunnels();
15091
15113
  if (seq !== seqRef.current) return;
15092
- setTunnels(list);
15114
+ setTunnels((prev) => diffTunnels(prev, list) ?? prev);
15093
15115
  setError(null);
15094
15116
  } catch (cause) {
15095
15117
  if (seq !== seqRef.current) return;
@@ -15099,7 +15121,7 @@ window.__ModuleLoader__.load({
15099
15121
  load();
15100
15122
  const timer = setInterval(() => {
15101
15123
  load();
15102
- }, 5e3);
15124
+ }, TUNNEL_POLL_MS);
15103
15125
  return () => {
15104
15126
  clearInterval(timer);
15105
15127
  };