diff --git a/Cargo.lock b/Cargo.lock index d1f1aeed..a3f2ff1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10309,6 +10309,7 @@ dependencies = [ "serde", "tauri", "tauri-build", + "tauri-plugin-os", "yaak-proxy", "yaak-window", ] diff --git a/apps/yaak-client/components/Settings/Settings.tsx b/apps/yaak-client/components/Settings/Settings.tsx index 11d14b44..d370a125 100644 --- a/apps/yaak-client/components/Settings/Settings.tsx +++ b/apps/yaak-client/components/Settings/Settings.tsx @@ -12,7 +12,7 @@ import { CountBadge } from '../core/CountBadge'; import { Icon } from '../core/Icon'; import { HStack } from '../core/Stacks'; import { TabContent, type TabItem, Tabs } from '../core/Tabs/Tabs'; -import { HeaderSize } from '../HeaderSize'; +import { HeaderSize } from '@yaakapp-internal/ui'; import { SettingsCertificates } from './SettingsCertificates'; import { SettingsGeneral } from './SettingsGeneral'; import { SettingsHotkeys } from './SettingsHotkeys'; @@ -77,6 +77,10 @@ export default function Settings({ hide }: Props) { onlyXWindowControl size="md" className="x-theme-appHeader bg-surface text-text-subtle flex items-center justify-center border-b border-border-subtle text-sm font-semibold" + osType={type()} + hideWindowControls={settings.hideWindowControls} + useNativeTitlebar={settings.useNativeTitlebar} + interfaceScale={settings.interfaceScale} > - + @@ -178,6 +181,10 @@ export function Workspace() { size="lg" className="relative x-theme-appHeader bg-surface" style={head} + osType={osType} + hideWindowControls={settings.hideWindowControls} + useNativeTitlebar={settings.useNativeTitlebar} + interfaceScale={settings.interfaceScale} >
(null); const [busy, setBusy] = useState(false); + const osType = type(); async function startProxy() { setBusy(true); @@ -46,46 +51,61 @@ function App() { } return ( -
-
-
-
-

Yaak Proxy

-

Status: {status}

-

- Port: {port ?? "Not running"} -

-
- -
- - - -
+
+ +
+ Yaak Proxy
-
-
+ +
+
+
+
+

Status: {status}

+

+ Port: {port ?? "Not running"} +

+
+ +
+ + +
+
+
+
+
); } createRoot(document.getElementById("root") as HTMLElement).render( - + + + , ); diff --git a/apps/yaak-proxy/package.json b/apps/yaak-proxy/package.json index 64fd56fd..0ad345c3 100644 --- a/apps/yaak-proxy/package.json +++ b/apps/yaak-proxy/package.json @@ -9,9 +9,11 @@ "lint": "tsc --noEmit" }, "dependencies": { + "@tanstack/react-query": "^5.90.5", + "@tauri-apps/api": "^2.9.1", + "@tauri-apps/plugin-os": "^2.3.2", "@yaakapp-internal/theme": "^1.0.0", "@yaakapp-internal/ui": "^1.0.0", - "@tauri-apps/api": "^2.9.1", "react": "^19.1.0", "react-dom": "^19.1.0" }, diff --git a/apps/yaak-proxy/tsconfig.json b/apps/yaak-proxy/tsconfig.json index c2eba75d..9dd388e1 100644 --- a/apps/yaak-proxy/tsconfig.json +++ b/apps/yaak-proxy/tsconfig.json @@ -15,7 +15,6 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "baseUrl": ".", "paths": { "@yaakapp-internal/theme": ["../../packages/theme/src/index.ts"], "@yaakapp-internal/theme/*": ["../../packages/theme/src/*"], diff --git a/crates-tauri/yaak-app-proxy/Cargo.toml b/crates-tauri/yaak-app-proxy/Cargo.toml index 53c01d50..5a5be04c 100644 --- a/crates-tauri/yaak-app-proxy/Cargo.toml +++ b/crates-tauri/yaak-app-proxy/Cargo.toml @@ -16,5 +16,6 @@ tauri-build = { version = "2.5.3", features = [] } log = { workspace = true } serde = { workspace = true, features = ["derive"] } tauri = { workspace = true } +tauri-plugin-os = "2.3.2" yaak-proxy = { workspace = true } yaak-window = { workspace = true } diff --git a/crates-tauri/yaak-app-proxy/capabilities/default.json b/crates-tauri/yaak-app-proxy/capabilities/default.json index b2f104a7..44b13715 100644 --- a/crates-tauri/yaak-app-proxy/capabilities/default.json +++ b/crates-tauri/yaak-app-proxy/capabilities/default.json @@ -5,6 +5,14 @@ "*" ], "permissions": [ - "core:default" + "core:default", + "os:allow-os-type", + "core:window:allow-close", + "core:window:allow-is-fullscreen", + "core:window:allow-is-maximized", + "core:window:allow-maximize", + "core:window:allow-minimize", + "core:window:allow-start-dragging", + "core:window:allow-unmaximize" ] } diff --git a/crates-tauri/yaak-app-proxy/src/lib.rs b/crates-tauri/yaak-app-proxy/src/lib.rs index 4d9adaec..622c967c 100644 --- a/crates-tauri/yaak-app-proxy/src/lib.rs +++ b/crates-tauri/yaak-app-proxy/src/lib.rs @@ -58,6 +58,7 @@ fn proxy_stop(state: State<'_, ProxyState>) -> Result { pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_os::init()) .manage(ProxyState::default()) .invoke_handler(tauri::generate_handler![proxy_metadata, proxy_start, proxy_stop]) .build(tauri::generate_context!()) diff --git a/package-lock.json b/package-lock.json index cedbb42b..7494b34d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -224,7 +224,9 @@ "name": "@yaakapp/yaak-proxy", "version": "1.0.0", "dependencies": { + "@tanstack/react-query": "^5.90.5", "@tauri-apps/api": "^2.9.1", + "@tauri-apps/plugin-os": "^2.3.2", "@yaakapp-internal/theme": "^1.0.0", "@yaakapp-internal/ui": "^1.0.0", "react": "^19.1.0", @@ -16143,6 +16145,7 @@ } }, "packages/theme": { + "name": "@yaakapp-internal/theme", "version": "1.0.0", "dependencies": { "@tauri-apps/api": "^2.9.1", @@ -16154,8 +16157,13 @@ "name": "@yaakapp-internal/ui", "version": "1.0.0", "dependencies": { + "@tanstack/react-query": "^5.90.5", + "@tauri-apps/api": "^2.9.1", + "@yaakapp-internal/lib": "^1.0.0", + "classnames": "^2.5.1", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-use": "^17.6.0" }, "peerDependencies": { "react": "^19.1.0", diff --git a/packages/ui/package.json b/packages/ui/package.json index b97529d0..16db545c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -6,8 +6,13 @@ "main": "src/index.ts", "types": "src/index.ts", "dependencies": { + "@tanstack/react-query": "^5.90.5", + "@tauri-apps/api": "^2.9.1", + "@yaakapp-internal/lib": "^1.0.0", + "classnames": "^2.5.1", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-use": "^17.6.0" }, "peerDependencies": { "react": "^19.1.0", diff --git a/apps/yaak-client/components/HeaderSize.tsx b/packages/ui/src/components/HeaderSize.tsx similarity index 73% rename from apps/yaak-client/components/HeaderSize.tsx rename to packages/ui/src/components/HeaderSize.tsx index 03a951ab..34a3926f 100644 --- a/apps/yaak-client/components/HeaderSize.tsx +++ b/packages/ui/src/components/HeaderSize.tsx @@ -1,7 +1,4 @@ -import { type } from '@tauri-apps/plugin-os'; -import { settingsAtom } from '@yaakapp-internal/models'; import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; import type { CSSProperties, HTMLAttributes, ReactNode } from 'react'; import { useMemo } from 'react'; import { useIsFullscreen } from '../hooks/useIsFullscreen'; @@ -14,6 +11,10 @@ interface HeaderSizeProps extends HTMLAttributes { ignoreControlsSpacing?: boolean; onlyXWindowControl?: boolean; hideControls?: boolean; + osType: string; + hideWindowControls: boolean; + useNativeTitlebar: boolean; + interfaceScale: number; } export function HeaderSize({ @@ -24,10 +25,12 @@ export function HeaderSize({ onlyXWindowControl, children, hideControls, + osType, + hideWindowControls, + useNativeTitlebar, + interfaceScale, }: HeaderSizeProps) { - const settings = useAtomValue(settingsAtom); const isFullscreen = useIsFullscreen(); - const nativeTitlebar = settings.useNativeTitlebar; const finalStyle = useMemo(() => { const s = { ...style }; @@ -35,14 +38,14 @@ export function HeaderSize({ if (size === 'md') s.minHeight = HEADER_SIZE_MD; if (size === 'lg') s.minHeight = HEADER_SIZE_LG; - if (nativeTitlebar) { + if (useNativeTitlebar) { // No style updates when using native titlebar - } else if (type() === 'macos') { + } else if (osType === 'macos') { if (!isFullscreen) { // Add large padding for window controls - s.paddingLeft = 72 / settings.interfaceScale; + s.paddingLeft = 72 / interfaceScale; } - } else if (!ignoreControlsSpacing && !settings.hideWindowControls) { + } else if (!ignoreControlsSpacing && !hideWindowControls) { s.paddingRight = WINDOW_CONTROLS_WIDTH; } @@ -50,11 +53,12 @@ export function HeaderSize({ }, [ ignoreControlsSpacing, isFullscreen, - settings.hideWindowControls, - settings.interfaceScale, + hideWindowControls, + interfaceScale, size, style, - nativeTitlebar, + useNativeTitlebar, + osType, ]); return ( @@ -70,6 +74,7 @@ export function HeaderSize({ > {/* NOTE: This needs display:grid or else the element shrinks (even though scrollable) */}
{children}
- {!hideControls && !nativeTitlebar && } + {!hideControls && !useNativeTitlebar && ( + + )}
); } diff --git a/apps/yaak-client/components/WindowControls.tsx b/packages/ui/src/components/WindowControls.tsx similarity index 85% rename from apps/yaak-client/components/WindowControls.tsx rename to packages/ui/src/components/WindowControls.tsx index 000312e5..e7c9d39e 100644 --- a/apps/yaak-client/components/WindowControls.tsx +++ b/packages/ui/src/components/WindowControls.tsx @@ -1,31 +1,28 @@ import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; -import { type } from '@tauri-apps/plugin-os'; -import { settingsAtom } from '@yaakapp-internal/models'; import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; import { useState } from 'react'; import { WINDOW_CONTROLS_WIDTH } from '../lib/constants'; -import { Button } from './core/Button'; -import { HStack } from './core/Stacks'; +import { Button } from './Button'; interface Props { className?: string; onlyX?: boolean; - macos?: boolean; + osType: string; + hideWindowControls: boolean; + useNativeTitlebar: boolean; } -export function WindowControls({ className, onlyX }: Props) { +export function WindowControls({ className, onlyX, osType, hideWindowControls, useNativeTitlebar }: Props) { const [maximized, setMaximized] = useState(false); - const settings = useAtomValue(settingsAtom); + // Never show controls on macOS or if hideWindowControls is true - if (type() === 'macos' || settings.hideWindowControls || settings.useNativeTitlebar) { + if (osType === 'macos' || hideWindowControls || useNativeTitlebar) { return null; } return ( - @@ -88,6 +85,6 @@ export function WindowControls({ className, onlyX }: Props) { /> - + ); } diff --git a/packages/ui/src/hooks/useDebouncedState.ts b/packages/ui/src/hooks/useDebouncedState.ts new file mode 100644 index 00000000..f82e3d2d --- /dev/null +++ b/packages/ui/src/hooks/useDebouncedState.ts @@ -0,0 +1,12 @@ +import { debounce } from '@yaakapp-internal/lib'; +import type { Dispatch, SetStateAction } from 'react'; +import { useMemo, useState } from 'react'; + +export function useDebouncedState( + defaultValue: T, + delay = 500, +): [T, Dispatch>, Dispatch>] { + const [state, setState] = useState(defaultValue); + const debouncedSetState = useMemo(() => debounce(setState, delay), [delay]); + return [state, debouncedSetState, setState]; +} diff --git a/packages/ui/src/hooks/useDebouncedValue.ts b/packages/ui/src/hooks/useDebouncedValue.ts new file mode 100644 index 00000000..c6639aa7 --- /dev/null +++ b/packages/ui/src/hooks/useDebouncedValue.ts @@ -0,0 +1,8 @@ +import { useEffect } from 'react'; +import { useDebouncedState } from './useDebouncedState'; + +export function useDebouncedValue(value: T, delay = 500) { + const [state, setState] = useDebouncedState(value, delay); + useEffect(() => setState(value), [setState, value]); + return state; +} diff --git a/apps/yaak-client/hooks/useIsFullscreen.ts b/packages/ui/src/hooks/useIsFullscreen.ts similarity index 100% rename from apps/yaak-client/hooks/useIsFullscreen.ts rename to packages/ui/src/hooks/useIsFullscreen.ts diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 5925536a..0071568a 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,2 +1,8 @@ export { Button } from "./components/Button"; export type { ButtonProps } from "./components/Button"; +export { HeaderSize } from "./components/HeaderSize"; +export { WindowControls } from "./components/WindowControls"; +export { useIsFullscreen } from "./hooks/useIsFullscreen"; +export { useDebouncedValue } from "./hooks/useDebouncedValue"; +export { useDebouncedState } from "./hooks/useDebouncedState"; +export { HEADER_SIZE_MD, HEADER_SIZE_LG, WINDOW_CONTROLS_WIDTH } from "./lib/constants"; diff --git a/apps/yaak-client/lib/constants.ts b/packages/ui/src/lib/constants.ts similarity index 100% rename from apps/yaak-client/lib/constants.ts rename to packages/ui/src/lib/constants.ts diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 77592bbb..a859843e 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -3,6 +3,8 @@ "target": "es2021", "lib": ["DOM", "DOM.Iterable", "ESNext"], "strict": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, "module": "ESNext", "moduleResolution": "Node", "jsx": "react-jsx",