Something went wrong. Try again.
[mirror] A local-first desktop markdown writer with grammar checking, at proto integration, and more github.com/stormlightlabs/writer/releases
atproto codemirror markdown tauri rust react
Something went wrong. Try again.
9.0 kB · 266 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267import { CollapsibleSection } from "$components/CollapsibleSection";import { Sheet, type SheetPosition, type SheetSize } from "$components/Sheet";import { useRoutedSheet } from "$hooks/useRoutedSheet";import { useViewportTier } from "$hooks/useViewportTier";import { useLayoutSettingsUiState } from "$state/selectors";import type { SettingsScope } from "$types";import { cn } from "$utils/tw";import { useCallback, useEffect, useMemo } from "react";import { AccessibilitySection } from "./Accessibility";import { AtProtoSection } from "./AtProtoSection";import { ChromeSettingsSection } from "./ChromeSettings";import { EditorSettingsSection } from "./EditorSettings";import { FocusModeSection } from "./FocusModeSettings";import { QuickCaptureSection } from "./QuickCapture";import { SettingsHeader } from "./SettingsHeader";import { WritingTools } from "./WriterTools";
type SettingsSheetLayout = { position: SheetPosition; size: SheetSize; className: string; backdropClassName: string };
type SettingsBodyProps = { scope: SettingsScope; onOpenAtProtoAuth: () => void; onOpenStandardSiteImport: () => void; onLogoutAtProto: () => void; atProtoSession: import("$types").AtProtoSession | null; atProtoPending: boolean;};
const SettingsBody = ( { scope, onOpenAtProtoAuth, onLogoutAtProto, atProtoSession, atProtoPending }: SettingsBodyProps,) => ( <div className={cn("min-h-0 flex-1 overflow-y-auto overscroll-contain", scope === "full" ? "space-y-3 pr-2" : "pr-1")}> <CollapsibleSection title="Accessibility" description="Configure motion and display preferences." className={cn( scope === "full" && "rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm", )} contentClassName={cn(scope === "full" && "pb-3")}> <AccessibilitySection /> </CollapsibleSection> <CollapsibleSection title="Chrome" description="Control visibility of core app UI regions." defaultOpen className={cn( scope === "full" && "rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm", )} contentClassName={cn(scope === "full" && "pb-3")}> <ChromeSettingsSection /> </CollapsibleSection> <CollapsibleSection title="Editor" description="Tune text presentation, wrapping, and typography." defaultOpen className={cn( scope === "full" && "rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm", )} contentClassName={cn(scope === "full" && "pb-3")}> <EditorSettingsSection /> </CollapsibleSection> {scope === "full" && ( <> <CollapsibleSection title="Focus Mode" description="Configure centered writing and dimming behavior." className="rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm" contentClassName="pb-3"> <FocusModeSection /> </CollapsibleSection> <CollapsibleSection title="Writing Tools" description="Adjust writing analysis and guidance features." className="rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm" contentClassName="pb-3"> <WritingTools /> </CollapsibleSection> <CollapsibleSection title="Tangled" description="Manage your AT Protocol session." className="rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm" contentClassName="pb-3"> <AtProtoSection session={atProtoSession} isPending={atProtoPending} onOpenAuth={onOpenAtProtoAuth} onLogout={onLogoutAtProto} /> </CollapsibleSection> <CollapsibleSection title="Quick Capture" description="Toggle global quick-note capture behavior." className="rounded-lg border border-stroke-subtle bg-layer-02/35 px-3.5 first:border-t shadow-sm" contentClassName="pb-3"> <QuickCaptureSection /> </CollapsibleSection> </> )} </div>);
type SettingsContentProps = { title: string; scope: SettingsScope; onClose: () => void; closeAriaLabel: string; onViewMore?: () => void; onOpenAtProtoAuth: () => void; onOpenStandardSiteImport: () => void; onLogoutAtProto: () => void; atProtoSession: import("$types").AtProtoSession | null; atProtoPending: boolean;};
const SettingsContent = ( { title, scope, onClose, closeAriaLabel, onViewMore, onOpenAtProtoAuth, onOpenStandardSiteImport, onLogoutAtProto, atProtoSession, atProtoPending, }: SettingsContentProps,) => ( <section className={cn("flex min-h-0 h-full flex-col overflow-hidden", scope === "full" ? "p-5 sm:p-6" : "p-4")}> <SettingsHeader title={title} scope={scope} onClose={onClose} closeAriaLabel={closeAriaLabel} onViewMore={onViewMore} /> <SettingsBody scope={scope} onOpenAtProtoAuth={onOpenAtProtoAuth} onOpenStandardSiteImport={onOpenStandardSiteImport} onLogoutAtProto={onLogoutAtProto} atProtoSession={atProtoSession} atProtoPending={atProtoPending} /> </section>);
function useSettingsSheetLayout(scope: SettingsScope): SettingsSheetLayout { const { isCompact, viewportWidth } = useViewportTier(); const compactPanel = useMemo(() => isCompact || viewportWidth < 920, [isCompact, viewportWidth]);
return useMemo(() => { if (compactPanel) { return { position: "b", size: scope === "basic" ? "lg" : "full", className: scope === "basic" ? "left-3 right-3 bottom-3 rounded-xl border" : "left-2 right-2 top-2 bottom-2 rounded-2xl border shadow-2xl", backdropClassName: scope === "basic" ? "bg-black/35" : "bg-black/45", }; }
return { position: "r", size: scope === "basic" ? "md" : "xl", className: scope === "basic" ? "right-4 top-14 bottom-4 rounded-xl border" : "right-4 top-4 bottom-4 rounded-2xl border shadow-2xl", backdropClassName: scope === "basic" ? "bg-black/30" : "bg-black/40", }; }, [compactPanel, scope]);}
type SettingsSheetProps = { atProtoSession?: import("$types").AtProtoSession | null; atProtoPending?: boolean; onOpenAtProtoAuth?: () => void; onOpenStandardSiteImport?: () => void; onLogoutAtProto?: () => void;};
export function LayoutSettingsPanel( { atProtoSession = null, atProtoPending = false, onOpenAtProtoAuth = () => {}, onOpenStandardSiteImport = () => {}, onLogoutAtProto = () => {}, }: SettingsSheetProps,) { const { isOpen: isVisible, setOpen } = useLayoutSettingsUiState(); const { isOpen: isSettingsRouteOpen, open: openSettingsRoute } = useRoutedSheet("/settings"); const layout = useSettingsSheetLayout("basic");
useEffect(() => { if (isVisible && isSettingsRouteOpen) { setOpen(false); } }, [isSettingsRouteOpen, isVisible, setOpen]);
const handleClose = useCallback(() => { setOpen(false); }, [setOpen]);
const handleViewMore = useCallback(() => { setOpen(false); openSettingsRoute(); }, [openSettingsRoute, setOpen]);
return ( <Sheet isOpen={isVisible} onClose={handleClose} position={layout.position} size={layout.size} ariaLabel="Layout settings" backdropClassName={layout.backdropClassName} className={layout.className}> <SettingsContent title="Layout Settings" scope="basic" onClose={handleClose} closeAriaLabel="Close layout settings" onViewMore={handleViewMore} onOpenAtProtoAuth={onOpenAtProtoAuth} onOpenStandardSiteImport={onOpenStandardSiteImport} onLogoutAtProto={onLogoutAtProto} atProtoSession={atProtoSession} atProtoPending={atProtoPending} /> </Sheet> );}
export function RoutedSettingsSheet( { atProtoSession = null, atProtoPending = false, onOpenAtProtoAuth = () => {}, onOpenStandardSiteImport = () => {}, onLogoutAtProto = () => {}, }: SettingsSheetProps,) { const { isOpen, close } = useRoutedSheet("/settings"); const layout = useSettingsSheetLayout("full");
return ( <Sheet isOpen={isOpen} onClose={close} position={layout.position} size={layout.size} ariaLabel="Settings" backdropClassName={layout.backdropClassName} className={layout.className}> <SettingsContent title="Settings" scope="full" onClose={close} closeAriaLabel="Close settings panel" onOpenAtProtoAuth={onOpenAtProtoAuth} onOpenStandardSiteImport={onOpenStandardSiteImport} onLogoutAtProto={onLogoutAtProto} atProtoSession={atProtoSession} atProtoPending={atProtoPending} /> </Sheet> );}