mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-31 07:35:24 +00:00
fix(file-manager): prioritize file names in narrow lists (#1483)
* fix(file-manager): prioritize file names in narrow lists * test(css): exercise the configured PostCSS plugin chain * test(identity): preserve full Ed25519 public keys in fixtures * test(remote-desktop): always mutate ciphertext in tamper test
This commit is contained in:
1 parent
44f751a5e5
commit
c98003f90e
6 files changed
+27
-18
No files matched your search
@@ -1353,7 +1353,7 @@ export function FileManagerGrid({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{listColumns.isVisible("owner") && (
|
{listColumns.isVisible("owner") && (
|
||||||
<div className="relative hidden md:flex items-center min-w-0">
|
<div className="relative flex items-center min-w-0">
|
||||||
<ColumnResizeHandle
|
<ColumnResizeHandle
|
||||||
{...listColumns.getHandleProps("owner")}
|
{...listColumns.getHandleProps("owner")}
|
||||||
/>
|
/>
|
||||||
@@ -1502,7 +1502,7 @@ export function FileManagerGrid({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isEditing && listColumns.isVisible("owner") && (
|
{!isEditing && listColumns.isVisible("owner") && (
|
||||||
<span className="text-[10px] text-muted-foreground truncate hidden md:block pointer-events-none">
|
<span className="text-[10px] text-muted-foreground truncate pointer-events-none">
|
||||||
{file.owner
|
{file.owner
|
||||||
? `${file.owner}${file.group ? `:${file.group}` : ""}`
|
? `${file.owner}${file.group ? `:${file.group}` : ""}`
|
||||||
: "—"}
|
: "—"}
|
||||||
@@ -1510,7 +1510,7 @@ export function FileManagerGrid({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!isEditing && listColumns.isVisible("size") && (
|
{!isEditing && listColumns.isVisible("size") && (
|
||||||
<span className="text-[10px] text-right text-muted-foreground tabular-nums pointer-events-none">
|
<span className="text-[10px] text-right text-muted-foreground tabular-nums pointer-events-none truncate">
|
||||||
{file.type === "file" &&
|
{file.type === "file" &&
|
||||||
file.size !== undefined &&
|
file.size !== undefined &&
|
||||||
file.size !== null
|
file.size !== null
|
||||||
|
|||||||
@@ -1058,7 +1058,7 @@ export function LocalFilePane({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{columns.isVisible("size") && (
|
{columns.isVisible("size") && (
|
||||||
<span className="text-muted-foreground text-right pointer-events-none tabular-nums">
|
<span className="text-muted-foreground text-right pointer-events-none tabular-nums truncate">
|
||||||
{entry.type === "directory"
|
{entry.type === "directory"
|
||||||
? "--"
|
? "--"
|
||||||
: formatFileSize(entry.size)}
|
: formatFileSize(entry.size)}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ interface UseResizableColumnsOptions {
|
|||||||
/** localStorage key; widths are remembered per pane. */
|
/** localStorage key; widths are remembered per pane. */
|
||||||
storageKey: string;
|
storageKey: string;
|
||||||
/**
|
/**
|
||||||
* Fixed-width columns, in visual order, that follow a leading flexible
|
* Columns, in visual order, that follow the file name. Saved widths are
|
||||||
* column (the file name). The flexible column absorbs whatever is left.
|
* preferred maxima; metadata shrinks first when the pane is narrow.
|
||||||
*/
|
*/
|
||||||
columns: ResizableColumnSpec[];
|
columns: ResizableColumnSpec[];
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ function clamp(value: number, column: ResizableColumnSpec): number {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Column widths for a list view whose first column is flexible and whose
|
* Column widths for a list view whose first column is flexible and whose
|
||||||
* remaining columns are fixed and user-resizable by dragging the boundary at
|
* remaining columns are user-resizable by dragging the boundary at
|
||||||
* the left edge of each header cell. Double-clicking a handle resets that
|
* the left edge of each header cell. Double-clicking a handle resets that
|
||||||
* column to its default. Widths persist in localStorage.
|
* column to its default. Widths persist in localStorage.
|
||||||
*/
|
*/
|
||||||
@@ -141,8 +141,8 @@ export function useResizableColumns({
|
|||||||
const gridTemplateColumns = useMemo(
|
const gridTemplateColumns = useMemo(
|
||||||
() =>
|
() =>
|
||||||
[
|
[
|
||||||
"minmax(140px, 1fr)",
|
"minmax(min(240px, 50%), 1fr)",
|
||||||
...visibleColumns.map((c) => `${widths[c.key]}px`),
|
...visibleColumns.map((c) => `minmax(0, ${widths[c.key]}px)`),
|
||||||
].join(" "),
|
].join(" "),
|
||||||
[visibleColumns, widths],
|
[visibleColumns, widths],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ describe("useResizableColumns", () => {
|
|||||||
useResizableColumns({ storageKey: STORAGE_KEY, columns }),
|
useResizableColumns({ storageKey: STORAGE_KEY, columns }),
|
||||||
);
|
);
|
||||||
expect(result.current.gridTemplateColumns).toBe(
|
expect(result.current.gridTemplateColumns).toBe(
|
||||||
"minmax(140px, 1fr) 120px 80px",
|
"minmax(min(240px, 50%), 1fr) minmax(0, 120px) minmax(0, 80px)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,7 +64,9 @@ describe("useResizableColumns", () => {
|
|||||||
);
|
);
|
||||||
act(() => result.current.toggleColumn("size"));
|
act(() => result.current.toggleColumn("size"));
|
||||||
expect(result.current.isVisible("size")).toBe(false);
|
expect(result.current.isVisible("size")).toBe(false);
|
||||||
expect(result.current.gridTemplateColumns).toBe("minmax(140px, 1fr) 120px");
|
expect(result.current.gridTemplateColumns).toBe(
|
||||||
|
"minmax(min(240px, 50%), 1fr) minmax(0, 120px)",
|
||||||
|
);
|
||||||
expect(result.current.visibleColumns.map((c) => c.key)).toEqual([
|
expect(result.current.visibleColumns.map((c) => c.key)).toEqual([
|
||||||
"modified",
|
"modified",
|
||||||
]);
|
]);
|
||||||
@@ -79,7 +81,7 @@ describe("useResizableColumns", () => {
|
|||||||
expect(remount.result.current.isVisible("size")).toBe(false);
|
expect(remount.result.current.isVisible("size")).toBe(false);
|
||||||
act(() => remount.result.current.toggleColumn("size"));
|
act(() => remount.result.current.toggleColumn("size"));
|
||||||
expect(remount.result.current.gridTemplateColumns).toBe(
|
expect(remount.result.current.gridTemplateColumns).toBe(
|
||||||
"minmax(140px, 1fr) 120px 80px",
|
"minmax(min(240px, 50%), 1fr) minmax(0, 120px) minmax(0, 80px)",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,12 @@ describe("verifyToken", () => {
|
|||||||
expect(
|
expect(
|
||||||
tokens.verifyToken(encode({ ...data, iv: iv.toString("base64") })),
|
tokens.verifyToken(encode({ ...data, iv: iv.toString("base64") })),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
|
const ciphertext = Buffer.from(data.value, "base64");
|
||||||
|
ciphertext[0] ^= 0xff;
|
||||||
expect(
|
expect(
|
||||||
tokens.verifyToken(encode({ ...data, value: `A${data.value.slice(1)}` })),
|
tokens.verifyToken(
|
||||||
|
encode({ ...data, value: ciphertext.toString("base64") }),
|
||||||
|
),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,14 @@ afterEach(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function ed25519Line(comment = "") {
|
function ed25519Line(comment = "") {
|
||||||
const pair = ssh2.utils.generateKeyPairSync("ed25519");
|
// ssh2's generator can truncate leading zero bytes in Ed25519 keys.
|
||||||
const parsed = ssh2.utils.parseKey(pair.public);
|
const { publicKey } = crypto.generateKeyPairSync("ed25519");
|
||||||
const key = Array.isArray(parsed) ? parsed[0] : parsed;
|
const raw = Buffer.from(publicKey.export({ format: "jwk" }).x!, "base64url");
|
||||||
if (key instanceof Error) throw key;
|
const blob = Buffer.concat([
|
||||||
const line = `${key.type} ${key.getPublicSSH().toString("base64")}`;
|
Buffer.from("0000000b7373682d6564323535313900000020", "hex"),
|
||||||
|
raw,
|
||||||
|
]);
|
||||||
|
const line = `ssh-ed25519 ${blob.toString("base64")}`;
|
||||||
return comment ? `${line} ${comment}` : line;
|
return comment ? `${line} ${comment}` : line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user