mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
release-2.7.1 (#1296)
* Add Helm and GitOps deployment setup * fix: build better-sqlite3 from source in Docker (#1267) * fix: preserve runtime SSL settings (#1268) * fix: support forwarding from the memory SSH agent (#1269) * fix: support forwarding from the memory agent * style: format memory agent test * fix: prompt for encrypted SFTP key passphrases (#1270) * fix: prompt for SFTP key passphrases * style: format SSH key utility test * fix: include host context in automation notifications (#1271) * fix: include host context in automation notifications * style: format automation notification changes * fix: reserve sidebar height for host tags (#1272) * fix: keep host action rows stable at large font sizes (#1273) * fix: honor certificate setting during server probe (#1274) * fix: package standard Linux icon sizes (#1275) * fix: avoid duplicate Docker HTTPS listener (#1276) * Fix host status without metrics collection (#1277) * fix: allow eight-digit secure auth codes (#1263) Allow TOTP prompts to accept secure auth codes longer than six digits without blocking valid authentication attempts. Generated with Codebuff 🤖 Co-authored-by: Chetan <chetan.development@gmail.com> Co-authored-by: Codebuff <noreply@codebuff.com> * Harden Helm deployment defaults * Update Helm workflow action * Exclude Helm templates from Prettier * Fix browser RDP file drops (#1279) * Fix Proxmox guest credential usernames (#1280) * Add WSL local terminal option (#1281) * refactor: split the transfer engine into focused modules (#1282) * refactor: extract SFTP promisify helpers into sftp-promisify module * refactor: extract transfer timing and rate stats into transfer-stats module * refactor: extract transfer error classes and recovery checks into transfer-errors module * refactor: extract host/path utility helpers into transfer-host-utils module * refactor: extract SFTP directory tree helpers into transfer-sftp-dir module * refactor: extract segment copy job builder into transfer-segment-copy module * refactor: extract file scan and sample helpers into transfer-scan module * refactor: move throttled progress helper into transfer-stats module * style: format transfer modules * perf: optimize tmux monitor aggregation (#1283) * fix: reserve credential tag row height (#1284) * feat: edit AI provider model settings (#1285) * fix: clarify click-to-expand host setting (#1286) * fix: allow portable imports on remote databases (#1287) * fix: allow HTTPS to share the configured port (#1288) * fix: resolve synced jump hosts on the server (#1289) * fix: make terminal clipboard shortcuts layout independent (#1290) * fix: use compatible fetch dispatcher for Tailscale (#1291) * fix: add OIDC environment recovery override (#1292) * fix: coalesce rapid mobile terminal input (#1293) * fix: coalesce rapid mobile terminal input * fix: support clean xterm patch installs * fix: resolve synced remote desktop host IDs (#1295) * feat: make the SFTP file manager path bar editable (#1294) Co-authored-by: Maxime Bonillo <257463937+dropafterfree@users.noreply.github.com> Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> * feat: add passkey sign in to the login screen * fix: remove rounded corners from the host list search bar * fix: stop image storage settings text wrapping to one word per line * fix: prevent malformed websocket messages from crashing the server * chore: increment version * fix: remove gaps between host rows in the sidebar list Keep sub-pixel row measurements and stop wiping the size cache on hover. * fix: Failed to connect through jump hosts (#1180) https://github.com/Termix-SSH/Support/issues/1180 * feat: Progress bar for file downloads in the file manager (#1158) https://github.com/Termix-SSH/Support/issues/1158 * feat: Allow setting Silent OIDC Login via ENV var (#1174) https://github.com/Termix-SSH/Support/issues/1174 * feat: `IdentityFile` to limit the number of attempts by agents (#1165) https://github.com/Termix-SSH/Support/issues/1165 * feat: Credentials clone (#1159) https://github.com/Termix-SSH/Support/issues/1159 * chore: update release notes * docs: move helm setup guide to the docs site * fix: type errors in FilteredAgent agent identity handling * fix: remove stale better-sqlite3 prebuilds so the source build is used * fix: actually build better-sqlite3 from source so arm64 docker images work * fix: credential edit pencil in host editor and add clone action to credential list * fix: clear editingHost so the credential pencil actually opens the editor * chore: run format and lint * fix: folder drag and drop upload failing in the file manager * chore: sync Crowdin translations for 2.7.1 --------- Co-authored-by: alex-ctms <alex-ctms@users.noreply.github.com> Co-authored-by: ZacharyZcR <zacharyzcr1984@gmail.com> Co-authored-by: Chetan Kumar <74929596+ckloop@users.noreply.github.com> Co-authored-by: Chetan <chetan.development@gmail.com> Co-authored-by: Codebuff <noreply@codebuff.com> Co-authored-by: ZacharyZcR <payasonorahc@protonmail.com> Co-authored-by: dropafterfree <maxime.bonillo@gmail.com> Co-authored-by: Maxime Bonillo <257463937+dropafterfree@users.noreply.github.com>
This commit is contained in:
165 files changed
+6778
-2090
No files matched your search
@@ -66,6 +66,7 @@ import {
|
||||
rowKind,
|
||||
ROOT_PARENT,
|
||||
} from "./visible-rows";
|
||||
import { RESOURCE_ROW_EXTRA, rendersResourceRow } from "./row-metrics";
|
||||
import { ReorderIndicator } from "@/sidebar/ReorderIndicator";
|
||||
import { useSidebarSelection } from "./hooks/useSidebarSelection";
|
||||
import { useSidebarDragState } from "./hooks/useSidebarDragState";
|
||||
@@ -699,34 +700,47 @@ export function SidebarTree({
|
||||
const CLICK_CHEVRON_EXTRA = clickTrayActive ? 4.5 : 0;
|
||||
const HOST_ROW_HEIGHT = (isCompactDensity ? 27.5 : 45) + CLICK_CHEVRON_EXTRA;
|
||||
const FOLDER_ROW_HEIGHT = 31.5;
|
||||
// "always" mode permanently renders the connection-buttons row (plus the
|
||||
// management row/resource bars for online hosts) -- measured directly
|
||||
// rather than derived, since it has its own fixed shape.
|
||||
// "always" mode permanently renders the connection-buttons row plus the
|
||||
// management row -- measured directly rather than derived, since it has its
|
||||
// own fixed shape. The resource bars are NOT included: they only render for
|
||||
// an online host with CPU/RAM, so rowHeight adds RESOURCE_ROW_EXTRA per row.
|
||||
// Carries the click-mode chevron so the open row lands at its measured
|
||||
// 104.75 rather than always mode's 100.25. The term cancels out of
|
||||
// OPEN_TRAY_EXTRA below, leaving HOST_ROW_HEIGHT to supply it once.
|
||||
const ALWAYS_ROW_HEIGHT =
|
||||
(isCompactDensity ? 73.75 : 100.25) + CLICK_CHEVRON_EXTRA;
|
||||
(isCompactDensity ? 73.75 : 83) + CLICK_CHEVRON_EXTRA;
|
||||
const OPEN_TRAY_EXTRA = ALWAYS_ROW_HEIGHT - HOST_ROW_HEIGHT;
|
||||
// "actionsOnly" permanently renders just the connection-buttons row above
|
||||
// the tray; the management row/resource bars stay collapsed until toggled.
|
||||
const ACTIONS_ONLY_ROW_HEIGHT = isCompactDensity ? 50.25 : 75.75;
|
||||
// Opening the management row from actionsOnly's closed state (which
|
||||
// already includes the connection row).
|
||||
const ACTIONS_ONLY_OPEN_ROW_HEIGHT = isCompactDensity ? 79.25 : 104.75;
|
||||
// Hiding the resource bars only ever makes a row shorter than the height
|
||||
// reserved for it, which the existing shapes already tolerate: the bars are
|
||||
// conditional on the host being online with CPU/RAM data, so an offline host
|
||||
// has always rendered short of ALWAYS_ROW_HEIGHT. Simple therefore pairs
|
||||
// showResourceBars:false with trayTrigger:"actionsOnly", whose measured
|
||||
// height excludes the resource/management row to begin with.
|
||||
// already includes the connection row). Excludes the resource bars for the
|
||||
// same reason as ALWAYS_ROW_HEIGHT -- rowHeight adds them per row.
|
||||
const ACTIONS_ONLY_OPEN_ROW_HEIGHT = isCompactDensity ? 79.25 : 87.5;
|
||||
// Tag pills are a separate flex row. Comfortable density adds the row plus
|
||||
// its gap; compact density pulls it upward by 2px but still needs a slot.
|
||||
const TAG_ROW_EXTRA = isCompactDensity ? 12.5 : 18.5;
|
||||
// showResourceBars:false simply drops RESOURCE_ROW_EXTRA from every row.
|
||||
|
||||
const rowHeight = useCallback(
|
||||
(index: number) => {
|
||||
const row = visibleRows[index];
|
||||
if (!row) return FOLDER_ROW_HEIGHT;
|
||||
if (isFolder(row.item)) return FOLDER_ROW_HEIGHT;
|
||||
if (alwaysShowActions) return ALWAYS_ROW_HEIGHT;
|
||||
const tagExtra = showTags && row.item.tags?.length ? TAG_ROW_EXTRA : 0;
|
||||
// The resource bars only render for an online host that reported CPU/RAM.
|
||||
// Reserving their height unconditionally left a gap under every offline
|
||||
// row. measureElement corrects any drift from live status this estimate
|
||||
// can't see.
|
||||
const resourceExtra = rendersResourceRow(
|
||||
row.item,
|
||||
showResourceBars,
|
||||
isCompactDensity,
|
||||
)
|
||||
? RESOURCE_ROW_EXTRA
|
||||
: 0;
|
||||
if (alwaysShowActions)
|
||||
return ALWAYS_ROW_HEIGHT + tagExtra + resourceExtra;
|
||||
const toggledOpen =
|
||||
(openTrayHostId === row.item.id || openMenuHostId === row.item.id) &&
|
||||
(clickTrayActive || actionsOnly);
|
||||
@@ -738,10 +752,20 @@ export function SidebarTree({
|
||||
!selectionMode &&
|
||||
(hoveredHostId === row.item.id || openMenuHostId === row.item.id);
|
||||
const isOpen = toggledOpen || hoverOpen;
|
||||
// The bars live inside the tray, so they only take space once it opens.
|
||||
const openResourceExtra = isOpen ? resourceExtra : 0;
|
||||
if (actionsOnly) {
|
||||
return isOpen ? ACTIONS_ONLY_OPEN_ROW_HEIGHT : ACTIONS_ONLY_ROW_HEIGHT;
|
||||
return (
|
||||
(isOpen ? ACTIONS_ONLY_OPEN_ROW_HEIGHT : ACTIONS_ONLY_ROW_HEIGHT) +
|
||||
tagExtra +
|
||||
openResourceExtra
|
||||
);
|
||||
}
|
||||
return isOpen ? HOST_ROW_HEIGHT + OPEN_TRAY_EXTRA : HOST_ROW_HEIGHT;
|
||||
return (
|
||||
(isOpen ? HOST_ROW_HEIGHT + OPEN_TRAY_EXTRA : HOST_ROW_HEIGHT) +
|
||||
tagExtra +
|
||||
openResourceExtra
|
||||
);
|
||||
},
|
||||
[
|
||||
visibleRows,
|
||||
@@ -752,12 +776,16 @@ export function SidebarTree({
|
||||
clickTrayActive,
|
||||
alwaysShowActions,
|
||||
actionsOnly,
|
||||
showTags,
|
||||
HOST_ROW_HEIGHT,
|
||||
CLICK_CHEVRON_EXTRA,
|
||||
OPEN_TRAY_EXTRA,
|
||||
ALWAYS_ROW_HEIGHT,
|
||||
ACTIONS_ONLY_ROW_HEIGHT,
|
||||
ACTIONS_ONLY_OPEN_ROW_HEIGHT,
|
||||
TAG_ROW_EXTRA,
|
||||
showResourceBars,
|
||||
isCompactDensity,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -766,6 +794,15 @@ export function SidebarTree({
|
||||
getScrollElement: () => parentRef.current,
|
||||
estimateSize: rowHeight,
|
||||
overscan: 12,
|
||||
// The library's default rounds every measurement to a whole pixel. Rows
|
||||
// here land on fractions (a 63.84px row rounds to 64), and the rounded-up
|
||||
// size becomes the slot pitch -- leaving a visible sliver under every
|
||||
// single row, which stacks into the gaps the list is judged by. Keep the
|
||||
// sub-pixel size so slots sit flush against the row above.
|
||||
measureElement: (element, entry) => {
|
||||
const box = entry?.borderBoxSize?.[0];
|
||||
return box ? box.blockSize : element.getBoundingClientRect().height;
|
||||
},
|
||||
getItemKey: (index) => {
|
||||
const row = visibleRows[index];
|
||||
if (!row) return index;
|
||||
@@ -792,28 +829,21 @@ export function SidebarTree({
|
||||
return reorderHoverEdge === "before" ? slot.start : slot.start + slot.size;
|
||||
})();
|
||||
|
||||
// Fixed heights mean estimateSize already IS the real size. Do not attach
|
||||
// measureElement here: its index-based ResizeObserver measurements can be
|
||||
// reused for a different row after a deletion and override the fixed size.
|
||||
// The virtualizer still needs telling when a row's height classification
|
||||
// changes (tray opened/closed, tree reshaped, density/trigger changed).
|
||||
// rowHeight is only an estimate: a row's real height also depends on live
|
||||
// state the tree can't cheaply know per row (an offline host drops the
|
||||
// resource bars "always" mode reserves space for). measureElement corrects
|
||||
// it from the DOM, keyed by getItemKey rather than index so a deleted row's
|
||||
// size is never reused for whatever shifts into its slot.
|
||||
//
|
||||
// measure() wipes the ENTIRE measurement cache, so it may only run when
|
||||
// every row's shape changes at once -- a density/trigger/tag switch. Hover
|
||||
// and tray state are deliberately absent: they change one row, which
|
||||
// re-renders and re-measures itself through the observer anyway, whereas
|
||||
// calling measure() for them threw all 5000 rows back to their estimates
|
||||
// and made the list visibly jump apart on every pointer move.
|
||||
useLayoutEffect(() => {
|
||||
virtualizer.measure();
|
||||
}, [
|
||||
virtualizer,
|
||||
openFolders,
|
||||
closedHostParents,
|
||||
openTrayHostId,
|
||||
openMenuHostId,
|
||||
hoveredHostId,
|
||||
selectionMode,
|
||||
query,
|
||||
visibleRows.length,
|
||||
density,
|
||||
trayTrigger,
|
||||
showTags,
|
||||
showResourceBars,
|
||||
]);
|
||||
}, [virtualizer, density, trayTrigger, showTags, showResourceBars]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -909,6 +939,8 @@ export function SidebarTree({
|
||||
return (
|
||||
<div
|
||||
key={vItem.key}
|
||||
data-index={vItem.index}
|
||||
ref={virtualizer.measureElement}
|
||||
className="absolute top-0 left-0 w-full"
|
||||
style={{
|
||||
transform: `translateY(${vItem.start}px)`,
|
||||
|
||||
Reference in new issue
Block a user