mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-11-11 08:42:45 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8de57e53d5 | ||
|
|
18220e012e |
No files matched your search
@@ -51,3 +51,17 @@ test("Vault import entry is disabled while an import is running", () => {
|
||||
assert.match(vaultViewLayoutSource, /disabled=\{importProgress\?\.status === "running"\}/);
|
||||
assert.match(vaultViewLayoutSource, /if \(importProgress\?\.status === "running"\) return;/);
|
||||
});
|
||||
|
||||
test("new host menu keeps the local shell icon at the same size as its siblings", () => {
|
||||
const menuStart = vaultViewLayoutSource.indexOf('<DropdownContent className="flex w-max min-w-44 flex-col" align="end" alignToParent>');
|
||||
assert.ok(menuStart >= 0);
|
||||
const menuEnd = vaultViewLayoutSource.indexOf("</DropdownContent>", menuStart);
|
||||
const menu = vaultViewLayoutSource.slice(menuStart, menuEnd);
|
||||
assert.match(menu, /className="flex w-max min-w-44 flex-col"/);
|
||||
assert.doesNotMatch(menu, /className="w-44"/);
|
||||
assert.match(menu, /<TerminalSquare size=\{14\} className="shrink-0" \/>/);
|
||||
assert.match(menu, /<FolderTree size=\{14\} className="shrink-0" \/>/);
|
||||
assert.match(menu, /<Upload size=\{14\} className="shrink-0" \/>/);
|
||||
assert.match(menu, /<Download size=\{14\} className="shrink-0" \/>/);
|
||||
assert.doesNotMatch(menu, /className="w-full justify-start gap-2"/);
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
const toastSource = readFileSync(new URL("./toast.tsx", import.meta.url), "utf8");
|
||||
|
||||
test("toast surfaces follow app theme tokens instead of OS palette fills", () => {
|
||||
const styles = toastSource.slice(
|
||||
toastSource.indexOf("const TOAST_STYLES"),
|
||||
toastSource.indexOf("export const ToastProvider"),
|
||||
);
|
||||
|
||||
assert.match(styles, /success: 'border-emerald-600'/);
|
||||
assert.match(styles, /error: 'border-red-600'/);
|
||||
assert.match(styles, /warning: 'border-yellow-600'/);
|
||||
assert.match(styles, /info: 'border-blue-600'/);
|
||||
// `dark:` is prefers-color-scheme, so these fills stay pale while
|
||||
// text-foreground / text-muted-foreground follow the `.dark` class.
|
||||
assert.doesNotMatch(styles, /bg-(?:emerald|red|yellow|blue)-50/);
|
||||
assert.doesNotMatch(styles, /dark:bg-(?:emerald|red|yellow|blue)-950/);
|
||||
assert.match(toastSource, /"bg-card animate-in/);
|
||||
assert.match(toastSource, /text-sm font-medium text-foreground/);
|
||||
assert.match(toastSource, /text-sm text-muted-foreground break-words/);
|
||||
});
|
||||
@@ -82,11 +82,15 @@ const TOAST_ICONS: Record<ToastType, React.ReactNode> = {
|
||||
info: <Info className="h-4 w-4 text-blue-500" />,
|
||||
};
|
||||
|
||||
// Colored `bg-*-50` / `dark:bg-*-950` fills follow the OS color scheme.
|
||||
// Title and body use `.dark` tokens (`text-foreground`, `text-muted-foreground`),
|
||||
// so an app-dark / OS-light session paints light text on a pale fill.
|
||||
// The container's `bg-card` tracks the app theme; the border and icon mark the variant.
|
||||
const TOAST_STYLES: Record<ToastType, string> = {
|
||||
success: 'border-emerald-600 bg-emerald-50 dark:bg-emerald-950',
|
||||
error: 'border-red-600 bg-red-50 dark:bg-red-950',
|
||||
warning: 'border-yellow-600 bg-yellow-50 dark:bg-yellow-950',
|
||||
info: 'border-blue-600 bg-blue-50 dark:bg-blue-950',
|
||||
success: 'border-emerald-600',
|
||||
error: 'border-red-600',
|
||||
warning: 'border-yellow-600',
|
||||
info: 'border-blue-600',
|
||||
};
|
||||
|
||||
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
|
||||
@@ -1007,42 +1007,43 @@ export function VaultViewLayout({ ctx }: { ctx: VaultViewLayoutContext }) {
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
</div>
|
||||
<DropdownContent className="w-44" align="end" alignToParent>
|
||||
{/* Fixed w-44 flex-shrinks the shell icon when the label is longer than the siblings. */}
|
||||
<DropdownContent className="flex w-max min-w-44 flex-col" align="end" alignToParent>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2"
|
||||
className="justify-start gap-2"
|
||||
onClick={handleNewLocalShellHost}
|
||||
>
|
||||
<TerminalSquare size={14} /> {t("vault.hosts.newLocalShell")}
|
||||
<TerminalSquare size={14} className="shrink-0" /> {t("vault.hosts.newLocalShell")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2"
|
||||
className="justify-start gap-2"
|
||||
onClick={() => {
|
||||
setTargetParentPath(selectedGroupPath);
|
||||
setNewFolderName("");
|
||||
setIsNewFolderOpen(true);
|
||||
}}
|
||||
>
|
||||
<FolderTree size={14} /> {t("vault.hosts.newGroup")}
|
||||
<FolderTree size={14} className="shrink-0" /> {t("vault.hosts.newGroup")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2"
|
||||
className="justify-start gap-2"
|
||||
disabled={importProgress?.status === "running"}
|
||||
onClick={() => {
|
||||
if (importProgress?.status === "running") return;
|
||||
setIsImportOpen(true);
|
||||
}}
|
||||
>
|
||||
<Upload size={14} /> {t("vault.hosts.import")}
|
||||
<Upload size={14} className="shrink-0" /> {t("vault.hosts.import")}
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start gap-2"
|
||||
className="justify-start gap-2"
|
||||
onClick={handleExportHosts}
|
||||
>
|
||||
<Download size={14} /> {t("vault.hosts.export")}
|
||||
<Download size={14} className="shrink-0" /> {t("vault.hosts.export")}
|
||||
</Button>
|
||||
</DropdownContent>
|
||||
</Dropdown>
|
||||
|
||||
Reference in new issue
Block a user