Skip to content

Commit b2eb5b4

Browse files
authored
Merge pull request #20 from Nuzair46/minor-ui-fixes
Minor UI improvements
2 parents 0e8a916 + 132a500 commit b2eb5b4

24 files changed

Lines changed: 374 additions & 351 deletions

web/App.tsx

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
type PendingDisplayToggle,
2222
type View,
2323
} from "@/app/ui";
24-
import { capitalizeToastError, formatMs } from "@/app/utils";
24+
import { capitalizeToastError, formatMs, shortcutSlotKey } from "@/app/utils";
2525

2626
import {
2727
applyLayout,
@@ -44,16 +44,6 @@ import type {
4444
DisplayInfo,
4545
} from "./types";
4646

47-
function shortcutSlotKey(index: number): string | null {
48-
if (index >= 0 && index <= 8) {
49-
return String(index + 1);
50-
}
51-
if (index === 9) {
52-
return "0";
53-
}
54-
return null;
55-
}
56-
5747
function buildShortcutFromBase(base: string | null, slotIndex: number): string | null {
5848
const trimmedBase = (base ?? "").trim();
5949
if (!trimmedBase) {
@@ -179,12 +169,11 @@ function App() {
179169
successNotice?: string,
180170
refresh = true,
181171
): Promise<boolean> {
182-
let succeeded = false;
183172
setBusy(true);
184173
setError(null);
174+
185175
try {
186176
await action();
187-
succeeded = true;
188177
if (successNotice) {
189178
toast.success(successNotice);
190179
}
@@ -193,16 +182,15 @@ function App() {
193182
setError(message);
194183
toast.error(capitalizeToastError(message));
195184
return false;
196-
}
197-
finally {
185+
} finally {
198186
setBusy(false);
199187
}
200188

201-
if (succeeded && refresh) {
189+
if (refresh) {
202190
void refreshState();
203191
}
204192

205-
return succeeded;
193+
return true;
206194
}
207195

208196
function runPendingLayoutDecision(
@@ -218,7 +206,7 @@ function App() {
218206
current ? { ...current, pending_confirmation: null } : current,
219207
);
220208

221-
void (async () => {
209+
const run = async () => {
222210
try {
223211
await action();
224212
} catch (err) {
@@ -229,7 +217,9 @@ function App() {
229217
setPendingLayoutDecisionBusy(false);
230218
void refreshState();
231219
}
232-
})();
220+
};
221+
222+
void run();
233223
}
234224

235225
useEffect(() => {
@@ -493,39 +483,38 @@ function App() {
493483
);
494484
}
495485

496-
function handleRevertTimeoutInputChange(value: string) {
486+
function markSettingsDirty() {
497487
setError(null);
498488
settingsDirtyRef.current = true;
489+
}
490+
491+
function handleRevertTimeoutInputChange(value: string) {
492+
markSettingsDirty();
499493
setRevertTimeoutInput(value);
500494
}
501495

502496
function handleStartWithWindowsChange(checked: boolean) {
503-
setError(null);
504-
settingsDirtyRef.current = true;
497+
markSettingsDirty();
505498
setStartWithWindowsEnabled(checked);
506499
}
507500

508501
function handleStartupProfileNameChange(value: string | null) {
509-
setError(null);
510-
settingsDirtyRef.current = true;
502+
markSettingsDirty();
511503
setStartupProfileName(value);
512504
}
513505

514506
function handleGlobalShortcutsEnabledChange(checked: boolean) {
515-
setError(null);
516-
settingsDirtyRef.current = true;
507+
markSettingsDirty();
517508
setGlobalShortcutsEnabled(checked);
518509
}
519510

520511
function handleProfileShortcutBaseChange(value: string) {
521-
setError(null);
522-
settingsDirtyRef.current = true;
512+
markSettingsDirty();
523513
setProfileShortcutBaseInput(value);
524514
}
525515

526516
function handleDisplayShortcutBaseChange(value: string) {
527-
setError(null);
528-
settingsDirtyRef.current = true;
517+
markSettingsDirty();
529518
setDisplayShortcutBaseInput(value);
530519
}
531520

web/app/components/app-header.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@ export function AppHeader() {
1515
return (
1616
<Card className="overflow-hidden">
1717
<CardHeader className="gap-4 lg:flex-row lg:items-start lg:justify-between">
18-
<div className="space-y-3">
19-
<div className="space-y-2">
20-
<CardTitle className="tracking-widest text-primary">
21-
MONARCH
22-
</CardTitle>
23-
<CardDescription className="max-w-3xl text-sm leading-relaxed sm:text-base">
24-
Detach, restore, and switch monitor layouts without touching cables.
25-
</CardDescription>
26-
</div>
18+
<div className="space-y-2">
19+
<CardTitle className="tracking-widest text-primary">
20+
MONARCH
21+
</CardTitle>
22+
<CardDescription className="max-w-3xl text-sm leading-relaxed sm:text-base">
23+
Detach, restore, and switch monitor layouts without touching cables.
24+
</CardDescription>
2725
</div>
2826

2927
<div className="flex w-full flex-col gap-2 lg:w-auto lg:items-end">

web/app/components/dialogs.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import { formatMs } from "@/app/utils";
1212
import type { PendingDisplayToggle } from "@/app/ui";
1313
import type { PendingConfirmation } from "@/types";
1414

15+
const noop = () => {};
16+
1517
type PendingConfirmationDialogProps = {
1618
pendingConfirmation: PendingConfirmation | null;
1719
busy: boolean;
@@ -26,7 +28,7 @@ export function PendingConfirmationDialog({
2628
onRevert,
2729
}: PendingConfirmationDialogProps) {
2830
return (
29-
<AlertDialog open={Boolean(pendingConfirmation)} onOpenChange={() => {}}>
31+
<AlertDialog open={Boolean(pendingConfirmation)} onOpenChange={noop}>
3032
<AlertDialogContent>
3133
<AlertDialogHeader>
3234
<AlertDialogTitle>Confirm layout change</AlertDialogTitle>
@@ -68,34 +70,37 @@ export function DisplayToggleDialog({
6870
onOpenChange,
6971
onConfirm,
7072
}: DisplayToggleDialogProps) {
73+
const isActive = Boolean(pendingDisplayToggle?.currentlyActive);
74+
const actionLabel = isActive ? "Detach Display" : "Attach Display";
75+
const title = isActive ? "Detach display?" : "Attach display?";
76+
const description = isActive
77+
? "will be removed from the active layout."
78+
: "will be added back to the active layout.";
79+
7180
return (
7281
<AlertDialog open={Boolean(pendingDisplayToggle)} onOpenChange={onOpenChange}>
7382
<AlertDialogContent>
7483
<AlertDialogHeader>
75-
<AlertDialogTitle>
76-
{pendingDisplayToggle?.currentlyActive ? "Detach display?" : "Attach display?"}
77-
</AlertDialogTitle>
84+
<AlertDialogTitle>{title}</AlertDialogTitle>
7885
<AlertDialogDescription>
7986
{pendingDisplayToggle ? (
8087
<>
8188
<span className="font-medium text-foreground">
8289
{pendingDisplayToggle.friendlyName}
8390
</span>{" "}
84-
{pendingDisplayToggle.currentlyActive
85-
? "will be removed from the active layout."
86-
: "will be added back to the active layout."}
91+
{description}
8792
</>
8893
) : null}
8994
</AlertDialogDescription>
9095
</AlertDialogHeader>
9196
<AlertDialogFooter>
9297
<AlertDialogCancel disabled={busy}>Cancel</AlertDialogCancel>
9398
<AlertDialogAction
94-
variant={pendingDisplayToggle?.currentlyActive ? "destructive" : "default"}
99+
variant={isActive ? "destructive" : "default"}
95100
disabled={busy}
96101
onClick={onConfirm}
97102
>
98-
{pendingDisplayToggle?.currentlyActive ? "Detach Display" : "Attach Display"}
103+
{actionLabel}
99104
</AlertDialogAction>
100105
</AlertDialogFooter>
101106
</AlertDialogContent>

web/app/components/layout-preview.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { useMemo } from "react";
33
import { cn } from "@/lib/utils";
44
import type { AppSnapshot } from "@/types";
55

6+
type LayoutOutput = AppSnapshot["layout"]["outputs"][number];
7+
68
function previewOutputs(snapshot: AppSnapshot | null) {
79
if (!snapshot) {
810
return [];
@@ -14,32 +16,44 @@ function previewOutputs(snapshot: AppSnapshot | null) {
1416
);
1517
}
1618

17-
function layoutBounds(snapshot: AppSnapshot | null) {
18-
const outputs = previewOutputs(snapshot);
19+
function getOutputResolution(
20+
output: LayoutOutput,
21+
displayByKey: Map<string, AppSnapshot["displays"][number]>,
22+
) {
23+
const display = displayByKey.get(output.display_key);
24+
if (!display?.is_active) {
25+
return output.resolution;
26+
}
27+
28+
return display.resolution;
29+
}
30+
31+
function layoutBounds(snapshot: AppSnapshot | null, outputs: LayoutOutput[]) {
1932
if (!snapshot || outputs.length === 0) {
2033
return null;
2134
}
2235

2336
const displayByKey = new Map(snapshot.displays.map((display) => [display.id_key, display]));
24-
const outputWidth = (output: AppSnapshot["layout"]["outputs"][number]) =>
25-
displayByKey.get(output.display_key)?.is_active
26-
? (displayByKey.get(output.display_key)?.resolution.width ?? output.resolution.width)
27-
: output.resolution.width;
28-
const outputHeight = (output: AppSnapshot["layout"]["outputs"][number]) =>
29-
displayByKey.get(output.display_key)?.is_active
30-
? (displayByKey.get(output.display_key)?.resolution.height ?? output.resolution.height)
31-
: output.resolution.height;
37+
3238
const left = Math.min(...outputs.map((o) => o.position.x));
3339
const top = Math.min(...outputs.map((o) => o.position.y));
34-
const right = Math.max(...outputs.map((o) => o.position.x + outputWidth(o)));
35-
const bottom = Math.max(...outputs.map((o) => o.position.y + outputHeight(o)));
40+
const right = Math.max(
41+
...outputs.map((output) => output.position.x + getOutputResolution(output, displayByKey).width),
42+
);
43+
const bottom = Math.max(
44+
...outputs.map((output) => output.position.y + getOutputResolution(output, displayByKey).height),
45+
);
3646

3747
return { left, top, right, bottom, width: right - left, height: bottom - top };
3848
}
3949

4050
export function LayoutPreview({ snapshot }: { snapshot: AppSnapshot | null }) {
41-
const bounds = useMemo(() => layoutBounds(snapshot), [snapshot]);
4251
const outputs = useMemo(() => previewOutputs(snapshot), [snapshot]);
52+
const bounds = useMemo(() => layoutBounds(snapshot, outputs), [snapshot, outputs]);
53+
const displayByKey = useMemo(
54+
() => new Map((snapshot?.displays ?? []).map((display) => [display.id_key, display])),
55+
[snapshot],
56+
);
4357
const monitorNumberByDisplayKey = useMemo(
4458
() =>
4559
new Map(
@@ -71,7 +85,7 @@ export function LayoutPreview({ snapshot }: { snapshot: AppSnapshot | null }) {
7185
}}
7286
>
7387
{outputs.map((output) => {
74-
const display = snapshot.displays.find((d) => d.id_key === output.display_key);
88+
const display = displayByKey.get(output.display_key);
7589
const monitorNumber = monitorNumberByDisplayKey.get(output.display_key);
7690
const active = output.enabled;
7791
const previewResolution =

0 commit comments

Comments
 (0)