feat: the last commit of year

This commit is contained in:
reya 2023-12-31 20:53:51 +07:00
parent b1d2496f8e
commit 56fab1dda6
28 changed files with 277 additions and 227 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1,24 +1,50 @@
import { SettingsIcon } from "@lume/icons";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
export function WelcomeScreen() { export function WelcomeScreen() {
return ( return (
<div className="flex h-full w-full flex-col items-center justify-center gap-10"> <div className="flex flex-col items-center justify-between w-full h-full">
<div className="mx-auto max-w-md flex w-full text-center flex-col items-center"> <div />
<h1 className="mb-2 text-4xl font-semibold">Welcome to Nostr!</h1> <div className="flex flex-col items-center w-full max-w-4xl gap-10 mx-auto">
<div className="flex flex-col items-center text-center">
<img
src="/heading.png"
srcSet="/heading@2x.png 2x"
alt="lume"
className="w-2/3"
/>
<p className="mt-5 text-lg font-medium leading-snug text-neutral-600 dark:text-neutral-500">
Lume is your safe Nostr client to meet, explore and
<br />
freely sharing your though to everyone in nostrverse
</p>
</div>
<div className="flex flex-col w-full max-w-xs gap-2 mx-auto">
<Link
to="/auth/create"
className="inline-flex items-center justify-center w-full h-12 font-medium text-white bg-blue-500 rounded-xl hover:bg-blue-600"
>
Create New Account
</Link>
<Link
to="/auth/import"
className="inline-flex items-center justify-center w-full h-12 font-medium text-neutral-50 rounded-xl bg-neutral-950 hover:bg-neutral-900"
>
Login
</Link>
</div>
</div> </div>
<div className="flex flex-col gap-2 px-8 mx-auto max-w-sm"> <div className="flex items-center justify-center h-11">
<Link <p className="text-neutral-800">
to="/auth/create" Before joining Nostr, you can take time to learn more about Nostr{" "}
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 font-medium text-white hover:bg-blue-600" <Link
> to="https://nostr.com"
Create new account target="_blank"
</Link> className="text-blue-500"
<Link >
to="/auth/import" here
className="inline-flex h-11 w-full items-center justify-center rounded-lg font-medium text-neutral-900 hover:bg-neutral-100 dark:text-neutral-100 dark:hover:bg-neutral-900" </Link>
> </p>
Log in
</Link>
</div> </div>
</div> </div>
); );

View File

@ -16,7 +16,7 @@ export function HomeScreen() {
const renderItem = (column: IColumn) => { const renderItem = (column: IColumn) => {
switch (column.kind) { switch (column.kind) {
case COL_TYPES.newsfeed: case COL_TYPES.newsfeed:
return <Timeline key={column.id} />; return <Timeline key={column.id} column={column} />;
case COL_TYPES.thread: case COL_TYPES.thread:
return <Thread key={column.id} column={column} />; return <Thread key={column.id} column={column} />;
case COL_TYPES.user: case COL_TYPES.user:
@ -24,12 +24,12 @@ export function HomeScreen() {
case COL_TYPES.hashtag: case COL_TYPES.hashtag:
return <Hashtag key={column.id} column={column} />; return <Hashtag key={column.id} column={column} />;
default: default:
return <Timeline key={column.id} />; return <Timeline key={column.id} column={column} />;
} }
}; };
return ( return (
<div className="h-full w-full"> <div className="w-full h-full">
<VList <VList
ref={ref} ref={ref}
className="h-full w-full flex-nowrap overflow-x-auto !overflow-y-hidden scrollbar-none focus:outline-none" className="h-full w-full flex-nowrap overflow-x-auto !overflow-y-hidden scrollbar-none focus:outline-none"

View File

@ -18,6 +18,7 @@
"devDependencies": { "devDependencies": {
"@lume/tailwindcss": "workspace:^", "@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^", "@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.46", "@types/react": "^18.2.46",
"tailwind": "^4.0.0", "tailwind": "^4.0.0",
"typescript": "^5.3.3" "typescript": "^5.3.3"

View File

@ -3,11 +3,12 @@ import { TimelineIcon } from "@lume/icons";
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useRef } from "react"; import { useRef } from "react";
import { IColumn } from "../../../types";
import { EventRoute } from "./event"; import { EventRoute } from "./event";
import { HomeRoute } from "./home"; import { HomeRoute } from "./home";
import { UserRoute } from "./user"; import { UserRoute } from "./user";
export function Timeline() { export function Timeline({ column }: { column: IColumn }) {
const colKey = "newsfeed"; const colKey = "newsfeed";
const storage = useStorage(); const storage = useStorage();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -26,7 +27,7 @@ export function Timeline() {
return ( return (
<Column.Root> <Column.Root>
<Column.Header <Column.Header
id="9999" id={column.id}
queryKey={[colKey]} queryKey={[colKey]}
title="Timeline" title="Timeline"
icon={<TimelineIcon className="size-4" />} icon={<TimelineIcon className="size-4" />}

View File

@ -17,35 +17,35 @@ export function ColumnHeader({
queryKey, queryKey,
icon, icon,
}: { }: {
id: string; id: number;
title: string; title: string;
queryKey?: string[]; queryKey?: string[];
icon?: ReactNode; icon?: ReactNode;
}) { }) {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { removeColumn } = useColumnContext(); const { moveColumn, removeColumn } = useColumnContext();
const refresh = async () => { const refresh = async () => {
if (queryKey) await queryClient.refetchQueries({ queryKey }); if (queryKey) await queryClient.refetchQueries({ queryKey });
}; };
const moveLeft = async () => { const moveLeft = async () => {
removeColumn(id); moveColumn(id, "left");
}; };
const moveRight = async () => { const moveRight = async () => {
removeColumn(id); moveColumn(id, "right");
}; };
const deleteWidget = async () => { const deleteWidget = async () => {
removeColumn(id); await removeColumn(id);
}; };
return ( return (
<div className="flex h-11 w-full shrink-0 items-center justify-between border-b border-neutral-100 px-3 dark:border-neutral-900"> <div className="flex items-center justify-between w-full px-3 border-b h-11 shrink-0 border-neutral-100 dark:border-neutral-900">
<div className="inline-flex items-center gap-4"> <div className="inline-flex items-center gap-4">
<div className="h-5 w-1 shrink-0 rounded-full bg-blue-500" /> <div className="w-1 h-5 bg-blue-500 rounded-full shrink-0" />
<div className="text-neutral-800 dark:text-neutral-200 inline-flex items-center gap-2 flex-1"> <div className="inline-flex items-center flex-1 gap-2 text-neutral-800 dark:text-neutral-200">
{icon ? icon : <ThreadIcon className="size-4" />} {icon ? icon : <ThreadIcon className="size-4" />}
<div className="text-sm font-medium">{title}</div> <div className="text-sm font-medium">{title}</div>
</div> </div>
@ -55,7 +55,7 @@ export function ColumnHeader({
<DropdownMenu.Trigger asChild> <DropdownMenu.Trigger asChild>
<button <button
type="button" type="button"
className="inline-flex h-6 w-6 items-center justify-center" className="inline-flex items-center justify-center w-6 h-6"
> >
<HorizontalDotsIcon className="size-4" /> <HorizontalDotsIcon className="size-4" />
</button> </button>
@ -66,7 +66,7 @@ export function ColumnHeader({
<button <button
type="button" type="button"
onClick={refresh} onClick={refresh}
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50" className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
> >
<RefreshIcon className="size-5" /> <RefreshIcon className="size-5" />
Refresh Refresh
@ -76,7 +76,7 @@ export function ColumnHeader({
<button <button
type="button" type="button"
onClick={moveLeft} onClick={moveLeft}
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50" className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
> >
<ArrowLeftIcon className="size-5" /> <ArrowLeftIcon className="size-5" />
Move left Move left
@ -86,18 +86,18 @@ export function ColumnHeader({
<button <button
type="button" type="button"
onClick={moveRight} onClick={moveRight}
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50" className="inline-flex items-center gap-2 px-3 text-sm font-medium rounded-lg h-9 text-neutral-700 hover:bg-blue-100 hover:text-blue-500 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-900 dark:hover:text-neutral-50"
> >
<ArrowRightIcon className="size-5" /> <ArrowRightIcon className="size-5" />
Move right Move right
</button> </button>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Separator className="my-1 h-px bg-neutral-100 dark:bg-neutral-900" /> <DropdownMenu.Separator className="h-px my-1 bg-neutral-100 dark:bg-neutral-900" />
<DropdownMenu.Item asChild> <DropdownMenu.Item asChild>
<button <button
type="button" type="button"
onClick={deleteWidget} onClick={deleteWidget}
className="inline-flex h-9 items-center gap-2 rounded-lg px-3 text-sm font-medium text-red-500 hover:bg-red-500 hover:text-red-50 focus:outline-none" className="inline-flex items-center gap-2 px-3 text-sm font-medium text-red-500 rounded-lg h-9 hover:bg-red-500 hover:text-red-50 focus:outline-none"
> >
<TrashIcon className="size-5" /> <TrashIcon className="size-5" />
Delete Delete

View File

@ -1,6 +1,5 @@
import { IColumn } from "@lume/types"; import { IColumn } from "@lume/types";
import { COL_TYPES } from "@lume/utils"; import { COL_TYPES } from "@lume/utils";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { import {
ReactNode, ReactNode,
createContext, createContext,
@ -13,9 +12,10 @@ import { useStorage } from "../../provider";
type ColumnContext = { type ColumnContext = {
columns: IColumn[]; columns: IColumn[];
addColumn: (column: IColumn) => void; addColumn: (column: IColumn) => Promise<void>;
removeColumn: (id: string) => void; removeColumn: (id: number) => Promise<void>;
loadAllColumns: () => void; moveColumn: (id: number, position: "left" | "right") => void;
loadAllColumns: () => Promise<IColumn[]>;
}; };
const ColumnContext = createContext<ColumnContext>(null); const ColumnContext = createContext<ColumnContext>(null);
@ -24,7 +24,7 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
const storage = useStorage(); const storage = useStorage();
const [columns, setColumns] = useState<IColumn[]>([ const [columns, setColumns] = useState<IColumn[]>([
{ {
id: "9999", id: 9999,
title: "Newsfeed", title: "Newsfeed",
content: "", content: "",
kind: COL_TYPES.newsfeed, kind: COL_TYPES.newsfeed,
@ -44,11 +44,28 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
if (result) setColumns((prev) => [...prev, column]); if (result) setColumns((prev) => [...prev, column]);
}, []); }, []);
const removeColumn = useCallback(async (id: string) => { const removeColumn = useCallback(async (id: number) => {
await storage.removeWidget(id); await storage.removeWidget(id);
setColumns((prev) => prev.filter((t) => t.id !== id)); setColumns((prev) => prev.filter((t) => t.id !== id));
}, []); }, []);
const moveColumn = useCallback(
(id: number, position: "left" | "right") => {
const newCols = [...columns];
const col = newCols.find((el) => el.id === id);
const colIndex = newCols.findIndex((el) => el.id === id);
newCols.splice(colIndex, 1);
if (position === "left") newCols.splice(colIndex - 1, 0, col);
if (position === "right") newCols.splice(colIndex + 1, 0, col);
setColumns(newCols);
},
[columns],
);
useEffect(() => { useEffect(() => {
let isMounted = true; let isMounted = true;
@ -63,7 +80,7 @@ export function ColumnProvider({ children }: { children: ReactNode }) {
return ( return (
<ColumnContext.Provider <ColumnContext.Provider
value={{ columns, addColumn, removeColumn, loadAllColumns }} value={{ columns, addColumn, removeColumn, moveColumn, loadAllColumns }}
> >
{children} {children}
</ColumnContext.Provider> </ColumnContext.Provider>

View File

@ -14,23 +14,23 @@ export function NotePin() {
<Tooltip.Trigger asChild> <Tooltip.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => onClick={async () =>
addColumn({ await addColumn({
kind: COL_TYPES.thread, kind: COL_TYPES.thread,
title: "Thread", title: "Thread",
content: event.id, content: event.id,
}) })
} }
className="inline-flex h-7 w-max items-center justify-center gap-2 rounded-full bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 px-2 text-sm font-medium dark:bg-neutral-900" className="inline-flex items-center justify-center gap-2 pl-2 pr-3 text-sm font-medium rounded-full h-7 w-max bg-neutral-100 hover:bg-neutral-200 dark:hover:bg-neutral-800 dark:bg-neutral-900"
> >
<PinIcon className="size-4" /> <PinIcon className="size-4" />
Pin Pin
</button> </button>
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Portal> <Tooltip.Portal>
<Tooltip.Content className="bg-neutral-950 text-white -left-10 inline-flex h-7 select-none items-center justify-center rounded-md px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
Pin note Pin note
<Tooltip.Arrow className="fill-neutral-950" /> <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Portal> </Tooltip.Portal>
</Tooltip.Root> </Tooltip.Root>

View File

@ -1,7 +1,6 @@
import { ReactionIcon } from "@lume/icons"; import { ReactionIcon } from "@lume/icons";
import * as HoverCard from "@radix-ui/react-hover-card"; import * as HoverCard from "@radix-ui/react-hover-card";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner";
import { useNoteContext } from "../provider"; import { useNoteContext } from "../provider";
const REACTIONS = [ const REACTIONS = [
@ -49,7 +48,7 @@ export function NoteReaction() {
setOpen(false); setOpen(false);
} catch (e) { } catch (e) {
toast.error(e); console.error(e);
} }
}; };
@ -58,22 +57,22 @@ export function NoteReaction() {
<HoverCard.Trigger asChild> <HoverCard.Trigger asChild>
<button <button
type="button" type="button"
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400" className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
> >
{reaction ? ( {reaction ? (
<img <img
src={getReactionImage(reaction)} src={getReactionImage(reaction)}
alt={reaction} alt={reaction}
className="size-5" className="size-6"
/> />
) : ( ) : (
<ReactionIcon className="size-5 group-hover:text-blue-500" /> <ReactionIcon className="size-6 group-hover:text-blue-500" />
)} )}
</button> </button>
</HoverCard.Trigger> </HoverCard.Trigger>
<HoverCard.Portal> <HoverCard.Portal>
<HoverCard.Content <HoverCard.Content
className="select-none rounded-lg bg-neutral-950 px-1 py-1 text-sm will-change-[transform,opacity] data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=top]:animate-slideDownAndFade" className="select-none rounded-lg bg-neutral-950 dark:bg-neutral-50 px-1 py-1 text-sm will-change-[transform,opacity] data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=top]:animate-slideDownAndFade"
sideOffset={0} sideOffset={0}
side="top" side="top"
> >
@ -81,56 +80,56 @@ export function NoteReaction() {
<button <button
type="button" type="button"
onClick={() => react("👏")} onClick={() => react("👏")}
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10" className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
> >
<img <img
src="/clapping_hands.png" src="/clapping_hands.png"
alt="Clapping Hands" alt="Clapping Hands"
className="h-6 w-6" className="size-6"
/> />
</button> </button>
<button <button
type="button" type="button"
onClick={() => react("🤪")} onClick={() => react("🤪")}
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10" className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
> >
<img <img
src="/face_with_tongue.png" src="/face_with_tongue.png"
alt="Face with Tongue" alt="Face with Tongue"
className="h-6 w-6" className="size-6"
/> />
</button> </button>
<button <button
type="button" type="button"
onClick={() => react("😮")} onClick={() => react("😮")}
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10" className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
> >
<img <img
src="/face_with_open_mouth.png" src="/face_with_open_mouth.png"
alt="Face with Open Mouth" alt="Face with Open Mouth"
className="h-6 w-6" className="size-6"
/> />
</button> </button>
<button <button
type="button" type="button"
onClick={() => react("😢")} onClick={() => react("😢")}
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10" className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
> >
<img <img
src="/crying_face.png" src="/crying_face.png"
alt="Crying Face" alt="Crying Face"
className="h-6 w-6" className="size-6"
/> />
</button> </button>
<button <button
type="button" type="button"
onClick={() => react("🤡")} onClick={() => react("🤡")}
className="inline-flex h-8 w-8 items-center justify-center rounded-md backdrop-blur-xl hover:bg-white/10" className="inline-flex items-center justify-center w-8 h-8 rounded-md backdrop-blur-xl hover:bg-white/10 dark:hover:bg-black/10"
> >
<img src="/clown_face.png" alt="Clown Face" className="h-6 w-6" /> <img src="/clown_face.png" alt="Clown Face" className="size-6" />
</button> </button>
</div> </div>
<HoverCard.Arrow className="fill-neutral-950" /> <HoverCard.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</HoverCard.Content> </HoverCard.Content>
</HoverCard.Portal> </HoverCard.Portal>
</HoverCard.Root> </HoverCard.Root>

View File

@ -26,15 +26,15 @@ export function NoteReply({
}).toString(), }).toString(),
}) })
} }
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400" className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
> >
<ReplyIcon className="h-5 w-5 group-hover:text-blue-500" /> <ReplyIcon className="size-6 group-hover:text-blue-500" />
</button> </button>
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Portal> <Tooltip.Portal>
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-white will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
Quick reply Quick reply
<Tooltip.Arrow className="fill-neutral-950" /> <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Portal> </Tooltip.Portal>
</Tooltip.Root> </Tooltip.Root>

View File

@ -29,20 +29,20 @@ export function NoteRepost() {
<button <button
type="button" type="button"
onClick={submit} onClick={submit}
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400" className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
> >
<RepostIcon <RepostIcon
className={twMerge( className={twMerge(
"h-5 w-5 group-hover:text-blue-600", "size-6 group-hover:text-blue-600",
isRepost ? "text-blue-500" : "", isRepost ? "text-blue-500" : "",
)} )}
/> />
</button> </button>
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Portal> <Tooltip.Portal>
<Tooltip.Content className="inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-950 px-3.5 text-sm text-white will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"> <Tooltip.Content className="inline-flex h-7 select-none text-neutral-50 dark:text-neutral-950 items-center justify-center rounded-md bg-neutral-950 dark:bg-neutral-50 px-3.5 text-sm will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade">
Repost Repost
<Tooltip.Arrow className="fill-neutral-950" /> <Tooltip.Arrow className="fill-neutral-950 dark:fill-neutral-50" />
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Portal> </Tooltip.Portal>
</Tooltip.Root> </Tooltip.Root>

View File

@ -107,32 +107,32 @@ export function NoteZap() {
<Dialog.Trigger asChild> <Dialog.Trigger asChild>
<button <button
type="button" type="button"
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400" className="inline-flex items-center justify-center group h-7 w-7 text-neutral-600 dark:text-neutral-400"
> >
<ZapIcon className="h-5 w-5 group-hover:text-blue-500" /> <ZapIcon className="size-6 group-hover:text-blue-500" />
</button> </button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Portal> <Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-sm dark:bg-black/20" /> <Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-sm dark:bg-black/20" />
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center"> <Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
<div className="relative h-min w-full max-w-xl rounded-xl bg-white dark:bg-black"> <div className="relative w-full max-w-xl bg-white h-min rounded-xl dark:bg-black">
<div className="inline-flex w-full shrink-0 items-center justify-between px-5 py-3"> <div className="inline-flex items-center justify-between w-full px-5 py-3 shrink-0">
<div className="w-6" /> <div className="w-6" />
<Dialog.Title className="text-center font-semibold"> <Dialog.Title className="font-semibold text-center">
Send tip to{" "} Send tip to{" "}
{user?.name || {user?.name ||
user?.displayName || user?.displayName ||
displayNpub(event.pubkey, 16)} displayNpub(event.pubkey, 16)}
</Dialog.Title> </Dialog.Title>
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md bg-neutral-100 dark:bg-neutral-900"> <Dialog.Close className="inline-flex items-center justify-center w-6 h-6 rounded-md bg-neutral-100 dark:bg-neutral-900">
<CancelIcon className="h-4 w-4" /> <CancelIcon className="w-4 h-4" />
</Dialog.Close> </Dialog.Close>
</div> </div>
<div className="overflow-y-auto overflow-x-hidden px-5 pb-5"> <div className="px-5 pb-5 overflow-x-hidden overflow-y-auto">
{!invoice ? ( {!invoice ? (
<> <>
<div className="relative flex h-40 flex-col"> <div className="relative flex flex-col h-40">
<div className="inline-flex h-full flex-1 items-center justify-center gap-1"> <div className="inline-flex items-center justify-center flex-1 h-full gap-1">
<CurrencyInput <CurrencyInput
placeholder="0" placeholder="0"
defaultValue={"21"} defaultValue={"21"}
@ -142,9 +142,9 @@ export function NoteZap() {
max={10000} // 1M sats max={10000} // 1M sats
maxLength={10000} // 1M sats maxLength={10000} // 1M sats
onValueChange={(value) => setAmount(value)} onValueChange={(value) => setAmount(value)}
className="w-full flex-1 border-none bg-transparent text-right text-4xl font-semibold placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400" className="flex-1 w-full text-4xl font-semibold text-right bg-transparent border-none placeholder:text-neutral-600 focus:outline-none focus:ring-0 dark:text-neutral-400"
/> />
<span className="w-full flex-1 text-left text-4xl font-semibold text-neutral-600 dark:text-neutral-400"> <span className="flex-1 w-full text-4xl font-semibold text-left text-neutral-600 dark:text-neutral-400">
sats sats
</span> </span>
</div> </div>
@ -186,7 +186,7 @@ export function NoteZap() {
</button> </button>
</div> </div>
</div> </div>
<div className="mt-4 flex w-full flex-col gap-2"> <div className="flex flex-col w-full gap-2 mt-4">
<input <input
name="zapMessage" name="zapMessage"
value={zapMessage} value={zapMessage}
@ -203,7 +203,7 @@ export function NoteZap() {
<button <button
type="button" type="button"
onClick={() => createZapRequest()} onClick={() => createZapRequest()}
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 px-4 font-medium text-white hover:bg-blue-600" className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
> >
{isCompleted ? ( {isCompleted ? (
<p className="leading-tight">Successfully zapped</p> <p className="leading-tight">Successfully zapped</p>
@ -229,7 +229,7 @@ export function NoteZap() {
<button <button
type="button" type="button"
onClick={() => createZapRequest()} onClick={() => createZapRequest()}
className="inline-flex h-11 w-full items-center justify-center rounded-lg bg-blue-500 px-4 font-medium text-white hover:bg-blue-600" className="inline-flex items-center justify-center w-full px-4 font-medium text-white bg-blue-500 rounded-lg h-11 hover:bg-blue-600"
> >
Create Lightning invoice Create Lightning invoice
</button> </button>
@ -238,13 +238,13 @@ export function NoteZap() {
</div> </div>
</> </>
) : ( ) : (
<div className="mt-3 flex flex-col items-center justify-center gap-4"> <div className="flex flex-col items-center justify-center gap-4 mt-3">
<div className="rounded-md bg-neutral-100 p-3 dark:bg-neutral-900"> <div className="p-3 rounded-md bg-neutral-100 dark:bg-neutral-900">
<QRCodeSVG value={invoice} size={256} /> <QRCodeSVG value={invoice} size={256} />
</div> </div>
<div className="flex flex-col items-center gap-1"> <div className="flex flex-col items-center gap-1">
<h3 className="text-lg font-medium">Scan to zap</h3> <h3 className="text-lg font-medium">Scan to zap</h3>
<span className="text-center text-sm text-neutral-600 dark:text-neutral-400"> <span className="text-sm text-center text-neutral-600 dark:text-neutral-400">
You must use Bitcoin wallet which support Lightning You must use Bitcoin wallet which support Lightning
<br /> <br />
such as: Blue Wallet, Bitkit, Phoenix,... such as: Blue Wallet, Bitkit, Phoenix,...

View File

@ -7,14 +7,14 @@ export function Hashtag({ tag }: { tag: string }) {
return ( return (
<button <button
type="button" type="button"
onClick={() => onClick={async () =>
addColumn({ await addColumn({
kind: COL_TYPES.hashtag, kind: COL_TYPES.hashtag,
title: tag, title: tag,
content: tag, content: tag,
}) })
} }
className="cursor-default break-all text-blue-500 hover:text-blue-600" className="text-blue-500 break-all cursor-default hover:text-blue-600"
> >
{tag} {tag}
</button> </button>

View File

@ -24,7 +24,7 @@ export const MentionNote = memo(function MentionNote({
if (isLoading) { if (isLoading) {
return ( return (
<div className="w-full cursor-default rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900"> <div className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900">
Loading Loading
</div> </div>
); );
@ -32,7 +32,7 @@ export const MentionNote = memo(function MentionNote({
if (isError) { if (isError) {
return ( return (
<div className="w-full cursor-default rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900"> <div className="w-full p-3 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900">
Failed to fetch event Failed to fetch event
</div> </div>
); );
@ -40,11 +40,11 @@ export const MentionNote = memo(function MentionNote({
return ( return (
<Note.Provider event={data}> <Note.Provider event={data}>
<Note.Root className="flex w-full cursor-default flex-col gap-1 rounded-lg bg-neutral-100 dark:bg-neutral-900"> <Note.Root className="flex flex-col w-full gap-1 my-1 rounded-lg cursor-default bg-neutral-100 dark:bg-neutral-900">
<div className="mt-3 px-3"> <div className="px-3 mt-3">
<Note.User variant="mention" /> <Note.User variant="mention" />
</div> </div>
<div className="mt-1 px-3 pb-3"> <div className="px-3 pb-3 mt-1">
{renderKind(data)} {renderKind(data)}
<Link <Link
to={`/events/${data.id}`} to={`/events/${data.id}`}

View File

@ -13,14 +13,14 @@ export const MentionUser = memo(function MentionUser({
return ( return (
<DropdownMenu.Root> <DropdownMenu.Root>
<DropdownMenu.Trigger className="break-words text-blue-500 hover:text-blue-600"> <DropdownMenu.Trigger className="text-blue-500 break-words hover:text-blue-600">
{`@${user?.name || user?.displayName || user?.username || "anon"}`} {`@${user?.name || user?.displayName || user?.username || "anon"}`}
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Content className="left-[50px] z-50 relative flex w-[200px] flex-col overflow-hidden rounded-xl border border-neutral-200 bg-neutral-950 focus:outline-none dark:border-neutral-900"> <DropdownMenu.Content className="left-[50px] z-50 relative flex w-[200px] flex-col overflow-hidden rounded-xl border border-neutral-200 bg-neutral-950 focus:outline-none dark:border-neutral-900">
<DropdownMenu.Item asChild> <DropdownMenu.Item asChild>
<Link <Link
to={`/users/${pubkey}`} to={`/users/${pubkey}`}
className="inline-flex h-10 items-center px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none" className="inline-flex items-center h-10 px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none"
> >
View profile View profile
</Link> </Link>
@ -28,14 +28,14 @@ export const MentionUser = memo(function MentionUser({
<DropdownMenu.Item asChild> <DropdownMenu.Item asChild>
<button <button
type="button" type="button"
onClick={() => onClick={async () =>
addColumn({ await addColumn({
kind: COL_TYPES.user, kind: COL_TYPES.user,
title: user?.name || user?.displayName || "", title: user?.name || user?.displayName || "",
content: pubkey, content: pubkey,
}) })
} }
className="inline-flex h-10 items-center px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none" className="inline-flex items-center h-10 px-4 text-sm text-white hover:bg-neutral-900 focus:outline-none"
> >
Pin Pin
</button> </button>

View File

@ -39,7 +39,7 @@ export function ImagePreview({ url }: { url: string }) {
return ( return (
// biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> // biome-ignore lint/a11y/useKeyWithClickEvents: <explanation>
<div onClick={open} className="group relative"> <div onClick={open} className="relative my-1 group">
<img <img
src={url} src={url}
alt={url} alt={url}
@ -47,17 +47,17 @@ export function ImagePreview({ url }: { url: string }) {
decoding="async" decoding="async"
style={{ contentVisibility: "auto" }} style={{ contentVisibility: "auto" }}
onError={fallback} onError={fallback}
className="h-auto w-full rounded-lg border border-neutral-200/50 object-cover dark:border-neutral-800/50" className="object-cover w-full h-auto border rounded-lg border-neutral-200/50 dark:border-neutral-800/50"
/> />
<button <button
type="button" type="button"
onClick={(e) => downloadImage(e)} onClick={(e) => downloadImage(e)}
className="absolute right-2 top-2 z-10 hidden h-10 w-10 items-center justify-center rounded-lg bg-blue-500 group-hover:inline-flex hover:bg-blue-600" className="absolute z-10 items-center justify-center hidden w-10 h-10 bg-blue-500 rounded-lg right-2 top-2 group-hover:inline-flex hover:bg-blue-600"
> >
{downloaded ? ( {downloaded ? (
<CheckCircleIcon className="h-5 w-5 text-white" /> <CheckCircleIcon className="w-5 h-5 text-white" />
) : ( ) : (
<DownloadIcon className="h-5 w-5 text-white" /> <DownloadIcon className="w-5 h-5 text-white" />
)} )}
</button> </button>
</div> </div>

View File

@ -11,11 +11,11 @@ export function LinkPreview({ url }: { url: string }) {
if (status === "pending") { if (status === "pending") {
return ( return (
<div className="flex w-full flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900"> <div className="flex flex-col w-full my-1 rounded-lg bg-neutral-100 dark:bg-neutral-900">
<div className="h-48 w-full animate-pulse bg-neutral-300 dark:bg-neutral-700" /> <div className="w-full h-48 animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<div className="flex flex-col gap-2 px-3 py-3"> <div className="flex flex-col gap-2 px-3 py-3">
<div className="h-3 w-2/3 animate-pulse rounded bg-neutral-300 dark:bg-neutral-700" /> <div className="w-2/3 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<div className="h-3 w-3/4 animate-pulse rounded bg-neutral-300 dark:bg-neutral-700" /> <div className="w-3/4 h-3 rounded animate-pulse bg-neutral-300 dark:bg-neutral-700" />
<span className="mt-2.5 text-sm leading-none text-neutral-600 dark:text-neutral-400"> <span className="mt-2.5 text-sm leading-none text-neutral-600 dark:text-neutral-400">
{domain.hostname} {domain.hostname}
</span> </span>
@ -42,29 +42,29 @@ export function LinkPreview({ url }: { url: string }) {
to={url} to={url}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="flex w-full flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900" className="flex flex-col w-full my-1 overflow-hidden rounded-lg bg-neutral-100 dark:bg-neutral-900"
> >
{isImage(data.image) ? ( {isImage(data.image) ? (
<img <img
src={data.image} src={data.image}
alt={url} alt={url}
className="h-48 w-full rounded-t-lg bg-white object-cover" className="object-cover w-full h-48 bg-white rounded-t-lg"
/> />
) : null} ) : null}
<div className="flex flex-col items-start px-3 py-3"> <div className="flex flex-col items-start px-3 py-3">
<div className="flex flex-col items-start gap-1 text-left"> <div className="flex flex-col items-start gap-1 text-left">
{data.title ? ( {data.title ? (
<div className="break-all text-base font-semibold text-neutral-900 dark:text-neutral-100"> <div className="text-base font-semibold break-all text-neutral-900 dark:text-neutral-100">
{data.title} {data.title}
</div> </div>
) : null} ) : null}
{data.description ? ( {data.description ? (
<div className="mb-2 line-clamp-3 break-all text-sm text-neutral-700 dark:text-neutral-400"> <div className="mb-2 text-sm break-all line-clamp-3 text-neutral-700 dark:text-neutral-400">
{data.description} {data.description}
</div> </div>
) : null} ) : null}
</div> </div>
<div className="break-all text-sm text-neutral-600 dark:text-neutral-400"> <div className="text-sm break-all text-neutral-600 dark:text-neutral-400">
{domain.hostname} {domain.hostname}
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ export function VideoPreview({ url }: { url: string }) {
return ( return (
<MediaPlayer <MediaPlayer
src={url} src={url}
className="w-full overflow-hidden rounded-lg" className="w-full my-1 overflow-hidden rounded-lg"
aspectRatio="16/9" aspectRatio="16/9"
load="visible" load="visible"
> >

View File

@ -94,7 +94,7 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
const sqliteAdapter = await Database.load("sqlite:lume_v2.db"); const sqliteAdapter = await Database.load("sqlite:lume_v2.db");
const storage = new LumeStorage(sqliteAdapter, platformName); const storage = new LumeStorage(sqliteAdapter, platformName);
storage.init(); await storage.init();
// check for new update // check for new update
if (storage.settings.autoupdate) { if (storage.settings.autoupdate) {
@ -193,9 +193,9 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
return ( return (
<div <div
data-tauri-drag-region data-tauri-drag-region
className="relative flex h-screen w-screen items-center justify-center bg-neutral-50 dark:bg-neutral-950" className="relative flex items-center justify-center w-screen h-screen bg-neutral-50 dark:bg-neutral-950"
> >
<div className="flex max-w-2xl flex-col items-start gap-1"> <div className="flex flex-col items-start max-w-2xl gap-1">
<h5 className="font-semibold uppercase">TIP:</h5> <h5 className="font-semibold uppercase">TIP:</h5>
<Markdown <Markdown
options={{ options={{
@ -214,7 +214,7 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
</Markdown> </Markdown>
</div> </div>
<div className="absolute bottom-5 right-5 inline-flex items-center gap-2.5"> <div className="absolute bottom-5 right-5 inline-flex items-center gap-2.5">
<LoaderIcon className="h-6 w-6 animate-spin text-blue-500" /> <LoaderIcon className="w-6 h-6 text-blue-500 animate-spin" />
<p className="font-semibold"> <p className="font-semibold">
{isNewVersion ? "Found a new version, updating..." : "Starting..."} {isNewVersion ? "Found a new version, updating..." : "Starting..."}
</p> </p>

View File

@ -1,27 +1,31 @@
import { SVGProps } from 'react'; import { SVGProps } from "react";
export function SettingsIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) { export function SettingsIcon(
return ( props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>,
<svg ) {
xmlns="http://www.w3.org/2000/svg" return (
width="24" <svg
height="24" xmlns="http://www.w3.org/2000/svg"
fill="none" width="24"
viewBox="0 0 24 24" height="24"
{...props} fill="none"
> viewBox="0 0 24 24"
<path {...props}
stroke="currentColor" >
strokeLinejoin="round" <path
strokeWidth="1.5" stroke="currentColor"
d="M8.552 5.37l-1.793-.414a1 1 0 00-.932.267l-.604.604a1 1 0 00-.267.932l.414 1.793a1 1 0 01-.42 1.056l-1.755 1.17a1 1 0 00-.445.832v.78a1 1 0 00.445.832l1.755 1.17a1 1 0 01.42 1.056l-.414 1.793a1 1 0 00.267.932l.604.604a1 1 0 00.932.267l1.793-.414a1 1 0 011.056.42l1.17 1.755a1 1 0 00.832.445h.78a1 1 0 00.832-.445l1.17-1.755a1 1 0 011.056-.42l1.793.414a1 1 0 00.932-.267l.604-.604a1 1 0 00.267-.932l-.414-1.793a1 1 0 01.42-1.056l1.755-1.17a1 1 0 00.445-.832v-.78a1 1 0 00-.445-.832l-1.755-1.17a1 1 0 01-.42-1.056l.414-1.793a1 1 0 00-.267-.932l-.604-.604a1 1 0 00-.932-.267l-1.793.414a1 1 0 01-1.056-.42l-1.17-1.755a1 1 0 00-.832-.445h-.78a1 1 0 00-.832.445L9.608 4.95a1 1 0 01-1.056.42z" strokeLinecap="round"
/> strokeLinejoin="round"
<path strokeWidth="2"
stroke="currentColor" d="M11 12a1 1 0 112 0 1 1 0 01-2 0z"
strokeLinejoin="round" />
strokeWidth="1.5" <path
d="M14.75 12a2.75 2.75 0 11-5.5 0 2.75 2.75 0 015.5 0z" stroke="currentColor"
/> strokeLinecap="round"
</svg> strokeLinejoin="round"
); strokeWidth="2"
d="M8.562 4.32c1.252-.723 1.879-1.085 2.544-1.226a4.3 4.3 0 011.788 0c.665.141 1.292.503 2.544 1.225l1.499.865c1.252.723 1.878 1.084 2.334 1.59.403.447.707.974.894 1.546.21.647.21 1.37.21 2.816v1.728c0 1.446 0 2.169-.21 2.816-.186.572-.49 1.1-.894 1.547-.456.505-1.082.866-2.334 1.59l-1.499.864c-1.252.722-1.879 1.084-2.544 1.225a4.299 4.299 0 01-1.788 0c-.665-.141-1.292-.503-2.544-1.225l-1.499-.865c-1.252-.723-1.879-1.084-2.334-1.59a4.296 4.296 0 01-.894-1.546c-.21-.647-.21-1.37-.21-2.816v-1.728c0-1.446 0-2.169.21-2.816.186-.572.49-1.1.894-1.546.455-.506 1.082-.867 2.334-1.59l1.499-.865z"
/>
</svg>
);
} }

View File

@ -1,10 +1,10 @@
import { import {
Account, Account,
IColumn,
NDKCacheEvent, NDKCacheEvent,
NDKCacheEventTag, NDKCacheEventTag,
NDKCacheUser, NDKCacheUser,
NDKCacheUserProfile, NDKCacheUserProfile,
WidgetProps,
} from "@lume/types"; } from "@lume/types";
import { appConfigDir, resolveResource } from "@tauri-apps/api/path"; import { appConfigDir, resolveResource } from "@tauri-apps/api/path";
import { invoke } from "@tauri-apps/api/primitives"; import { invoke } from "@tauri-apps/api/primitives";
@ -323,7 +323,7 @@ export class LumeStorage {
} }
public async getWidgets() { public async getWidgets() {
const widgets: Array<WidgetProps> = await this.#db.select( const widgets: Array<IColumn> = await this.#db.select(
"SELECT * FROM widgets WHERE account_id = $1 ORDER BY created_at DESC;", "SELECT * FROM widgets WHERE account_id = $1 ORDER BY created_at DESC;",
[this.account.id], [this.account.id],
); );
@ -341,7 +341,7 @@ export class LumeStorage {
); );
if (insert) { if (insert) {
const widgets: Array<WidgetProps> = await this.#db.select( const widgets: Array<IColumn> = await this.#db.select(
"SELECT * FROM widgets ORDER BY id DESC LIMIT 1;", "SELECT * FROM widgets ORDER BY id DESC LIMIT 1;",
); );
if (widgets.length < 1) console.error("get created widget failed"); if (widgets.length < 1) console.error("get created widget failed");
@ -351,7 +351,7 @@ export class LumeStorage {
console.error("create widget failed"); console.error("create widget failed");
} }
public async removeWidget(id: string) { public async removeWidget(id: number) {
const res = await this.#db.execute("DELETE FROM widgets WHERE id = $1;", [ const res = await this.#db.execute("DELETE FROM widgets WHERE id = $1;", [
id, id,
]); ]);

View File

@ -34,7 +34,7 @@ export interface WidgetGroupItem {
} }
export interface IColumn { export interface IColumn {
id?: string; id?: number;
kind: number; kind: number;
title: string; title: string;
content: string; content: string;

View File

@ -1,24 +1,23 @@
import { cn } from "@lume/utils"; import { SettingsIcon } from "@lume/icons";
import { type Platform } from "@tauri-apps/plugin-os"; import { type Platform } from "@tauri-apps/plugin-os";
import { Outlet, ScrollRestoration } from "react-router-dom"; import { Outlet } from "react-router-dom";
import { WindowTitleBar } from "../titlebar"; import { WindowTitleBar } from "../titlebar";
export function AuthLayout({ platform }: { platform: Platform }) { export function AuthLayout({ platform }: { platform: Platform }) {
return ( return (
<div <div className="flex flex-col w-screen h-screen bg-black">
className={cn(
"flex h-screen w-screen flex-col",
platform !== "macos" ? "bg-neutral-50 dark:bg-neutral-950" : "",
)}
>
{platform !== "macos" ? ( {platform !== "macos" ? (
<WindowTitleBar platform={platform} /> <WindowTitleBar platform={platform} />
) : ( ) : (
<div data-tauri-drag-region className="h-9 shrink-0" /> <div data-tauri-drag-region className="h-9 shrink-0" />
)} )}
<div className="h-full w-full"> <div className="relative w-full h-full">
<div className="absolute top-0 right-9">
<div className="inline-flex items-center justify-center rounded-lg size-10 bg-neutral-950 group hover:bg-neutral-900">
<SettingsIcon className="size-6 text-neutral-700 group-hover:text-neutral-500" />
</div>
</div>
<Outlet /> <Outlet />
<ScrollRestoration />
</div> </div>
</div> </div>
); );

View File

@ -472,6 +472,9 @@ importers:
'@lume/tsconfig': '@lume/tsconfig':
specifier: workspace:^ specifier: workspace:^
version: link:../../tsconfig version: link:../../tsconfig
'@lume/types':
specifier: workspace:^
version: link:../../types
'@types/react': '@types/react':
specifier: ^18.2.46 specifier: ^18.2.46
version: 18.2.46 version: 18.2.46

View File

@ -1,21 +1,21 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": { "tauri": {
"windows": [ "windows": [
{ {
"width": 1080, "width": 1080,
"height": 800, "height": 800,
"minWidth": 560, "minWidth": 1080,
"minHeight": 800, "minHeight": 800,
"resizable": true, "resizable": true,
"title": "Lume", "title": "Lume",
"center": true, "center": true,
"fullscreen": false, "fullscreen": false,
"hiddenTitle": true, "hiddenTitle": true,
"fileDropEnabled": true, "fileDropEnabled": true,
"decorations": false, "decorations": false,
"transparent": false "transparent": false
} }
] ]
} }
} }

View File

@ -1,25 +1,25 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": { "tauri": {
"windows": [ "windows": [
{ {
"width": 1080, "width": 1080,
"height": 800, "height": 800,
"minWidth": 560, "minWidth": 1080,
"minHeight": 800, "minHeight": 800,
"resizable": true, "resizable": true,
"title": "Lume", "title": "Lume",
"titleBarStyle": "Overlay", "titleBarStyle": "Overlay",
"center": true, "center": true,
"fullscreen": false, "fullscreen": false,
"hiddenTitle": true, "hiddenTitle": true,
"fileDropEnabled": true, "fileDropEnabled": true,
"decorations": true, "decorations": true,
"transparent": true, "transparent": true,
"windowEffects": { "windowEffects": {
"effects": ["sidebar"] "effects": ["sidebar"]
} }
} }
] ]
} }
} }

View File

@ -1,24 +1,24 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": { "tauri": {
"windows": [ "windows": [
{ {
"width": 1080, "width": 1080,
"height": 800, "height": 800,
"minWidth": 560, "minWidth": 1080,
"minHeight": 800, "minHeight": 800,
"resizable": true, "resizable": true,
"title": "Lume", "title": "Lume",
"center": true, "center": true,
"fullscreen": false, "fullscreen": false,
"hiddenTitle": true, "hiddenTitle": true,
"fileDropEnabled": true, "fileDropEnabled": true,
"decorations": false, "decorations": false,
"transparent": true, "transparent": true,
"windowEffects": { "windowEffects": {
"effects": ["micaLight", "micaDark"] "effects": ["micaLight", "micaDark"]
} }
} }
] ]
} }
} }