feat: customizable desktop and mobile icon layouts (#40)

* feat: add customizable icon layouts

* Address PR #40 review feedback on icon layout and drag interactions

Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
mroxso
2026-09-06 22:39:22 +02:00
committed by GitHub
parent 4c20938098
commit b94753ec68
7 changed files with 674 additions and 23 deletions

View File

@@ -11,6 +11,8 @@ import { useAppContext } from '@/hooks/useAppContext';
import { useTheme } from '@/hooks/useTheme';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { useWindowManager } from '@/os/useWindowManager';
import { desktopApps } from '@/os/registry';
import { useIconLayout } from '@/os/useIconLayout';
import { useToast } from '@/hooks/useToast';
import { npubOf } from '@/lib/nostrUtils';
import { cn } from '@/lib/utils';
@@ -42,6 +44,8 @@ export default function SettingsApp({ setTitle }: AppProps) {
<Separator />
<MediaSection />
<Separator />
<IconLayoutSection />
<Separator />
<SessionSection />
</div>
</AppBody>
@@ -49,6 +53,32 @@ export default function SettingsApp({ setTitle }: AppProps) {
);
}
function IconLayoutSection() {
const { toast } = useToast();
const appIds = desktopApps().map((app) => app.id);
// Reset uses a deterministic, measurement-free registry order. The desktop
// clamps it to its actual surface on render; mobile reflows into its columns.
const { reset } = useIconLayout(appIds, { columns: 8, rows: 16 });
const resetAndToast = (profile: 'desktop' | 'mobile' | 'both') => {
reset(profile);
toast({ title: profile === 'both' ? 'All icon layouts reset' : `${profile === 'desktop' ? 'Desktop' : 'Mobile'} icon layout reset` });
};
return (
<Section
title="Home screen layout"
description="Arrange icons by dragging them. On a keyboard, press Space to pick up an icon, use the arrow keys to move it, then press Enter to drop."
>
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={() => resetAndToast('desktop')}>Reset desktop</Button>
<Button variant="outline" onClick={() => resetAndToast('mobile')}>Reset mobile</Button>
<Button variant="outline" onClick={() => resetAndToast('both')}>Reset both</Button>
</div>
</Section>
);
}
function Section({
title,
description,