diff --git a/plugins/file-manager/src/frontend/FileManagerGrid.tsx b/plugins/file-manager/src/frontend/FileManagerGrid.tsx index 8d3b33a9c..e1202aa4a 100644 --- a/plugins/file-manager/src/frontend/FileManagerGrid.tsx +++ b/plugins/file-manager/src/frontend/FileManagerGrid.tsx @@ -1353,7 +1353,7 @@ export function FileManagerGrid({ )} {listColumns.isVisible("owner") && ( -
+
@@ -1502,7 +1502,7 @@ export function FileManagerGrid({ )} {!isEditing && listColumns.isVisible("owner") && ( - + {file.owner ? `${file.owner}${file.group ? `:${file.group}` : ""}` : "—"} @@ -1510,7 +1510,7 @@ export function FileManagerGrid({ )} {!isEditing && listColumns.isVisible("size") && ( - + {file.type === "file" && file.size !== undefined && file.size !== null diff --git a/plugins/file-manager/src/frontend/LocalFilePane.tsx b/plugins/file-manager/src/frontend/LocalFilePane.tsx index 5290b15d9..4b19e238f 100644 --- a/plugins/file-manager/src/frontend/LocalFilePane.tsx +++ b/plugins/file-manager/src/frontend/LocalFilePane.tsx @@ -1058,7 +1058,7 @@ export function LocalFilePane({ )} {columns.isVisible("size") && ( - + {entry.type === "directory" ? "--" : formatFileSize(entry.size)} diff --git a/plugins/file-manager/src/frontend/hooks/useResizableColumns.ts b/plugins/file-manager/src/frontend/hooks/useResizableColumns.ts index 1b7fe9ac1..01a38a10e 100644 --- a/plugins/file-manager/src/frontend/hooks/useResizableColumns.ts +++ b/plugins/file-manager/src/frontend/hooks/useResizableColumns.ts @@ -15,8 +15,8 @@ interface UseResizableColumnsOptions { /** localStorage key; widths are remembered per pane. */ storageKey: string; /** - * Fixed-width columns, in visual order, that follow a leading flexible - * column (the file name). The flexible column absorbs whatever is left. + * Columns, in visual order, that follow the file name. Saved widths are + * preferred maxima; metadata shrinks first when the pane is narrow. */ 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 - * 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 * column to its default. Widths persist in localStorage. */ @@ -141,8 +141,8 @@ export function useResizableColumns({ const gridTemplateColumns = useMemo( () => [ - "minmax(140px, 1fr)", - ...visibleColumns.map((c) => `${widths[c.key]}px`), + "minmax(min(240px, 50%), 1fr)", + ...visibleColumns.map((c) => `minmax(0, ${widths[c.key]}px)`), ].join(" "), [visibleColumns, widths], ); diff --git a/plugins/file-manager/tests/frontend/hooks/useResizableColumns.test.ts b/plugins/file-manager/tests/frontend/hooks/useResizableColumns.test.ts index 460339c6a..8f47ab647 100644 --- a/plugins/file-manager/tests/frontend/hooks/useResizableColumns.test.ts +++ b/plugins/file-manager/tests/frontend/hooks/useResizableColumns.test.ts @@ -20,7 +20,7 @@ describe("useResizableColumns", () => { useResizableColumns({ storageKey: STORAGE_KEY, columns }), ); 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")); 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([ "modified", ]); @@ -79,7 +81,7 @@ describe("useResizableColumns", () => { expect(remount.result.current.isVisible("size")).toBe(false); act(() => remount.result.current.toggleColumn("size")); expect(remount.result.current.gridTemplateColumns).toBe( - "minmax(140px, 1fr) 120px 80px", + "minmax(min(240px, 50%), 1fr) minmax(0, 120px) minmax(0, 80px)", ); }); diff --git a/plugins/remote-desktop/tests/backend/token-security.test.ts b/plugins/remote-desktop/tests/backend/token-security.test.ts index 7a033d99e..b19cafdba 100644 --- a/plugins/remote-desktop/tests/backend/token-security.test.ts +++ b/plugins/remote-desktop/tests/backend/token-security.test.ts @@ -41,8 +41,12 @@ describe("verifyToken", () => { expect( tokens.verifyToken(encode({ ...data, iv: iv.toString("base64") })), ).toBeNull(); + const ciphertext = Buffer.from(data.value, "base64"); + ciphertext[0] ^= 0xff; expect( - tokens.verifyToken(encode({ ...data, value: `A${data.value.slice(1)}` })), + tokens.verifyToken( + encode({ ...data, value: ciphertext.toString("base64") }), + ), ).toBeNull(); }); diff --git a/plugins/termix-identity/tests/backend/termix-identity.test.ts b/plugins/termix-identity/tests/backend/termix-identity.test.ts index a9ad47dd9..0ea89c789 100644 --- a/plugins/termix-identity/tests/backend/termix-identity.test.ts +++ b/plugins/termix-identity/tests/backend/termix-identity.test.ts @@ -13,11 +13,14 @@ afterEach(async () => { }); function ed25519Line(comment = "") { - const pair = ssh2.utils.generateKeyPairSync("ed25519"); - const parsed = ssh2.utils.parseKey(pair.public); - const key = Array.isArray(parsed) ? parsed[0] : parsed; - if (key instanceof Error) throw key; - const line = `${key.type} ${key.getPublicSSH().toString("base64")}`; + // ssh2's generator can truncate leading zero bytes in Ed25519 keys. + const { publicKey } = crypto.generateKeyPairSync("ed25519"); + const raw = Buffer.from(publicKey.export({ format: "jwk" }).x!, "base64url"); + const blob = Buffer.concat([ + Buffer.from("0000000b7373682d6564323535313900000020", "hex"), + raw, + ]); + const line = `ssh-ed25519 ${blob.toString("base64")}`; return comment ? `${line} ${comment}` : line; }