feat: child webview

This commit is contained in:
2024-03-16 15:05:06 +07:00
parent 16e6d234e5
commit 46cc01e0ee
37 changed files with 1686 additions and 1403 deletions

View File

@@ -4,7 +4,7 @@
"private": true,
"main": "./src/index.ts",
"dependencies": {
"@getalby/sdk": "^3.3.1",
"@getalby/sdk": "^3.4.0",
"@lume/icons": "workspace:^",
"@lume/utils": "workspace:^",
"@radix-ui/react-avatar": "^1.0.4",
@@ -14,8 +14,8 @@
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-tooltip": "^1.0.7",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-router": "^1.18.1",
"@tanstack/react-query": "^5.28.4",
"@tanstack/react-router": "^1.20.0",
"get-urls": "^12.1.0",
"media-chrome": "^2.2.5",
"minidenticons": "^4.2.1",
@@ -24,7 +24,7 @@
"re-resizable": "^6.9.11",
"react": "^18.2.0",
"react-currency-input-field": "^3.8.0",
"react-i18next": "^14.0.5",
"react-i18next": "^14.1.0",
"react-string-replace": "^1.1.1",
"sonner": "^1.4.3",
"string-strip-html": "^13.4.6",
@@ -34,8 +34,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
},
"devDependencies": {
"@lume/tsconfig": "workspace:*",
"@types/react": "^18.2.61",
"typescript": "^5.3.3"
"@types/react": "^18.2.66",
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,7 +8,7 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0",
"sonner": "^1.4.3",
"virtua": "^0.27.5"
@@ -17,8 +17,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -8,15 +8,15 @@
"@lume/icons": "workspace:^",
"@lume/ui": "workspace:^",
"@lume/utils": "workspace:^",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"react": "^18.2.0"
},
"devDependencies": {
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -1,3 +1,5 @@
import { type Webview } from "@tauri-apps/api/webview";
export interface Settings {
autoupdate: boolean;
nsecbunker: boolean;
@@ -81,9 +83,16 @@ export interface RichContent {
}
export interface LumeColumn {
id: number;
title: string;
content: string;
name: string;
description?: string;
author?: string;
logo?: string;
x?: number;
y?: number;
width?: number;
height?: number;
window?: Webview;
}
export interface Opengraph {

View File

@@ -9,6 +9,6 @@
"access": "public"
},
"devDependencies": {
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -4,7 +4,7 @@
"private": true,
"main": "./src/index.ts",
"dependencies": {
"@getalby/sdk": "^3.3.1",
"@getalby/sdk": "^3.4.0",
"@lume/ark": "workspace:^",
"@lume/icons": "workspace:^",
"@lume/utils": "workspace:^",
@@ -18,9 +18,9 @@
"@radix-ui/react-hover-card": "^1.0.7",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-tooltip": "^1.0.7",
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-router": "^1.18.1",
"framer-motion": "^11.0.8",
"@tanstack/react-query": "^5.28.4",
"@tanstack/react-router": "^1.20.0",
"framer-motion": "^11.0.14",
"get-urls": "^12.1.0",
"media-chrome": "^2.2.5",
"minidenticons": "^4.2.1",
@@ -32,7 +32,7 @@
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.0",
"react-hotkeys-hook": "^4.5.0",
"react-i18next": "^14.0.5",
"react-i18next": "^14.1.0",
"react-string-replace": "^1.1.1",
"slate": "^0.101.5",
"slate-react": "^0.101.6",
@@ -46,8 +46,8 @@
"@lume/tailwindcss": "workspace:^",
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react": "^18.2.66",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}

View File

@@ -0,0 +1,89 @@
import { useCallback, useEffect, useMemo, useRef } from "react";
import {
LogicalPosition,
LogicalSize,
getCurrent,
} from "@tauri-apps/api/window";
import { Webview } from "@tauri-apps/api/webview";
import { LumeColumn } from "@lume/types";
import { useDebouncedCallback } from "use-debounce";
export function Column({
column,
isScroll,
}: {
column: LumeColumn;
isScroll: boolean;
}) {
const mainWindow = useMemo(() => getCurrent(), []);
const childWindow = useRef<Webview>(null);
const divRef = useRef<HTMLDivElement>(null);
const initialRect = useRef<DOMRect>(null);
const handleResize = useDebouncedCallback(() => {
const newRect = divRef.current.getBoundingClientRect();
if (initialRect.current.height !== newRect.height) {
childWindow.current.setSize(
new LogicalSize(newRect.width, newRect.height),
);
}
}, 800);
const trackResize = useCallback(async () => {
const unlisten = await mainWindow.onResized(() => {
handleResize();
});
return () => {
if (unlisten) unlisten();
};
}, []);
useEffect(() => {
if (isScroll) {
const newRect = divRef.current.getBoundingClientRect();
childWindow.current.setPosition(
new LogicalPosition(newRect.x, newRect.y),
);
}
}, [isScroll]);
useEffect(() => {
if (!mainWindow) return;
if (!divRef.current) return;
if (childWindow.current) return;
// get element dimension
const rect = divRef.current.getBoundingClientRect();
// create new webview
initialRect.current = rect;
childWindow.current = new Webview(
mainWindow,
column.name.toLowerCase().replace(/\W/g, ""),
{
url: column.content,
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
transparent: true,
userAgent: "Lume/4.0",
},
);
// track window resize event
trackResize();
}, []);
return (
<div className="shadow-primary relative flex h-full w-[420px] shrink-0 flex-col rounded-xl bg-white dark:bg-black">
<div className="flex h-11 w-full shrink-0 items-center justify-center gap-2 border-b border-neutral-100 dark:border-neutral-900">
<div className="inline-flex items-center gap-1.5">
<div className="text-[13px] font-medium">{column.name}</div>
</div>
</div>
<div ref={divRef} className="flex-1" />
<div className="h-6 w-full shrink-0 border-t border-neutral-100 dark:border-neutral-900" />
</div>
);
}

View File

@@ -1,16 +0,0 @@
import { cn } from "@lume/utils";
import { ReactNode } from "react";
export function ColumnContent({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return (
<div className={cn("flex-1 overflow-y-auto overflow-x-hidden", className)}>
{children}
</div>
);
}

View File

@@ -1,82 +0,0 @@
import {
ChevronDownIcon,
MoveLeftIcon,
MoveRightIcon,
RefreshIcon,
TrashIcon,
} from "@lume/icons";
import { cn } from "@lume/utils";
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
import { useTranslation } from "react-i18next";
export function ColumnHeader({
title,
className,
}: {
title: string;
className?: string;
}) {
const { t } = useTranslation();
return (
<DropdownMenu.Root>
<div
className={cn(
"flex h-11 w-full shrink-0 items-center justify-center gap-2 border-b border-neutral-100 dark:border-neutral-900",
className,
)}
>
<DropdownMenu.Trigger asChild>
<div className="inline-flex items-center gap-1.5">
<div className="text-[13px] font-medium">{title}</div>
<ChevronDownIcon className="size-5" />
</div>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
sideOffset={5}
className="flex w-[200px] flex-col overflow-hidden rounded-2xl bg-white/50 p-2 ring-1 ring-black/10 backdrop-blur-2xl focus:outline-none dark:bg-black/50 dark:ring-white/10"
>
<DropdownMenu.Item asChild>
<button
type="button"
className="inline-flex h-9 items-center gap-3 rounded-lg px-3 text-sm font-medium text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
>
<RefreshIcon className="size-4" />
{t("global.refresh")}
</button>
</DropdownMenu.Item>
<DropdownMenu.Item asChild>
<button
type="button"
className="inline-flex h-9 items-center gap-3 rounded-lg px-3 text-sm font-medium text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
>
<MoveLeftIcon className="size-4" />
{t("global.moveLeft")}
</button>
</DropdownMenu.Item>
<DropdownMenu.Item asChild>
<button
type="button"
className="inline-flex h-9 items-center gap-3 rounded-lg px-3 text-sm font-medium text-black/70 hover:bg-black/10 hover:text-black focus:outline-none dark:text-white/70 dark:hover:bg-white/10 dark:hover:text-white"
>
<MoveRightIcon className="size-4" />
{t("global.moveRight")}
</button>
</DropdownMenu.Item>
<DropdownMenu.Separator className="my-1 h-px bg-black/10 dark:bg-white/10" />
<DropdownMenu.Item asChild>
<button
type="button"
className="inline-flex h-9 items-center gap-3 rounded-lg px-3 text-sm font-medium text-red-500 hover:bg-red-500 hover:text-red-50 focus:outline-none"
>
<TrashIcon className="size-4" />
{t("global.delete")}
</button>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</div>
</DropdownMenu.Root>
);
}

View File

@@ -1,9 +0,0 @@
import { ColumnContent } from "./content";
import { ColumnHeader } from "./header";
import { ColumnRoot } from "./root";
export const Column = {
Root: ColumnRoot,
Header: ColumnHeader,
Content: ColumnContent,
};

View File

@@ -1,21 +0,0 @@
import { cn } from "@lume/utils";
import { ReactNode } from "react";
export function ColumnRoot({
children,
className,
}: {
children: ReactNode;
className?: string;
}) {
return (
<div
className={cn(
"shadow-primary relative mx-2 flex h-full w-[420px] flex-col rounded-xl bg-white dark:bg-black",
className,
)}
>
{children}
</div>
);
}

View File

@@ -1,7 +1,7 @@
export * from "./user";
export * from "./note";
export * from "./column";
// UI
export * from "./column";
export * from "./container";
export * from "./box";

View File

@@ -8,7 +8,7 @@
"access": "public"
},
"dependencies": {
"@tanstack/react-query": "^5.24.1",
"@tanstack/react-query": "^5.28.4",
"bitcoin-units": "^1.0.0",
"clsx": "^2.1.0",
"dayjs": "^1.11.10",
@@ -21,9 +21,9 @@
"devDependencies": {
"@lume/tsconfig": "workspace:^",
"@lume/types": "workspace:^",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"tailwind-merge": "^2.2.1",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
}
}