Compare commits

...

7 commits

29 changed files with 18460 additions and 1291 deletions

BIN
bun.lockb

Binary file not shown.

6696
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -17,18 +17,23 @@
"@mui/icons-material": "^5.16.6", "@mui/icons-material": "^5.16.6",
"@mui/lab": "^5.0.0-alpha.173", "@mui/lab": "^5.0.0-alpha.173",
"@mui/material": "^5.16.6", "@mui/material": "^5.16.6",
"@tauri-apps/api": "^1.6.0", "@tauri-apps/api": "^2.0.0",
"@tauri-apps/plugin-dialog": "^2.0.1",
"@tauri-apps/plugin-fs": "^2.0.1",
"@tauri-apps/plugin-notification": "^2.0.0",
"@tauri-apps/plugin-process": "^2.0.0",
"jotai": "^2.9.1", "jotai": "^2.9.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"next": "14.2.5", "next": "14.2.5",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"smol-toml": "^1.3.0", "smol-toml": "^1.3.0",
"tauri": "^0.15.0",
"zustand": "^4.5.4" "zustand": "^4.5.4"
}, },
"devDependencies": { "devDependencies": {
"@tauri-apps/cli": "^2.0.4",
"@types/lodash": "^4.17.7", "@types/lodash": "^4.17.7",
"@tauri-apps/cli": "^1.6.0",
"@types/node": "^20.14.14", "@types/node": "^20.14.14",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
@ -36,4 +41,4 @@
"eslint-config-next": "14.2.5", "eslint-config-next": "14.2.5",
"typescript": "^5.5.4" "typescript": "^5.5.4"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

BIN
public/images/cry.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

2530
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -12,15 +12,19 @@ rust-version = "1.60"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies] [build-dependencies]
tauri-build = { version = "1.5.3", features = [] } tauri-build = { version = "2", features = [] }
[dependencies] [dependencies]
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.7.0", features = [ "protocol-all", "fs-all", "path-all", "window-all", "process-all", "notification-all", "dialog-all"] } tauri = { version = "2", features = ["protocol-asset"] }
image = "0.25.2" image = "0.25.2"
webp = "0.3.0" webp = "0.3.0"
gif = "0.13.1" gif = "0.13.1"
tauri-plugin-notification = "2"
tauri-plugin-process = "2"
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled. # this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.

View file

@ -0,0 +1,77 @@
{
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"windows": [
"main"
],
"permissions": [
"core:default",
"fs:allow-read-file",
"fs:allow-write-file",
"fs:allow-read-dir",
"fs:allow-copy-file",
"fs:allow-mkdir",
"fs:allow-remove",
"fs:allow-remove",
"fs:allow-rename",
"fs:allow-exists",
{
"identifier": "fs:scope",
"allow": [
"**",
"**/*",
"/**/*",
"$CONFIG/stort/",
"$CONFIG/stort/**",
"$HOME/.local/share/stort/*",
"$HOME/.local/share/stort/**"
]
},
"core:window:allow-create",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:window:allow-set-closable",
"core:window:allow-set-title",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-close",
"core:window:allow-set-decorations",
"core:window:allow-set-always-on-top",
"core:window:allow-set-content-protected",
"core:window:allow-set-size",
"core:window:allow-set-min-size",
"core:window:allow-set-max-size",
"core:window:allow-set-position",
"core:window:allow-set-fullscreen",
"core:window:allow-set-focus",
"core:window:allow-set-icon",
"core:window:allow-set-skip-taskbar",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-set-cursor-icon",
"core:window:allow-set-cursor-position",
"core:window:allow-set-ignore-cursor-events",
"core:window:allow-start-dragging",
"core:webview:allow-print",
"dialog:allow-open",
"dialog:allow-save",
"dialog:allow-message",
"dialog:allow-ask",
"dialog:allow-confirm",
"notification:default",
"process:allow-restart",
"process:allow-exit",
"notification:default",
"process:default",
"dialog:default",
"fs:default"
]
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
{"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists",{"identifier":"fs:scope","allow":["**","**/*","/**/*","$CONFIG/stort/","$CONFIG/stort/**","$HOME/.local/share/stort/*","$HOME/.local/share/stort/**"]},"core:window:allow-create","core:window:allow-center","core:window:allow-request-user-attention","core:window:allow-set-resizable","core:window:allow-set-maximizable","core:window:allow-set-minimizable","core:window:allow-set-closable","core:window:allow-set-title","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-set-always-on-top","core:window:allow-set-content-protected","core:window:allow-set-size","core:window:allow-set-min-size","core:window:allow-set-max-size","core:window:allow-set-position","core:window:allow-set-fullscreen","core:window:allow-set-focus","core:window:allow-set-icon","core:window:allow-set-skip-taskbar","core:window:allow-set-cursor-grab","core:window:allow-set-cursor-visible","core:window:allow-set-cursor-icon","core:window:allow-set-cursor-position","core:window:allow-set-ignore-cursor-events","core:window:allow-start-dragging","core:webview:allow-print","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm","notification:default","process:allow-restart","process:allow-exit","notification:default","process:default","dialog:default","fs:default"]}}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -3,78 +3,40 @@
"build": { "build": {
"beforeBuildCommand": "npm run build", "beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",
"devPath": "http://localhost:3000", "frontendDist": "../out",
"distDir": "../out" "devUrl": "http://localhost:3000"
}, },
"package": { "bundle": {
"productName": "stort", "active": true,
"version": "0.1.0" "category": "DeveloperTool",
}, "copyright": "",
"tauri": { "shortDescription": "Launcher for Steam Deck",
"allowlist": { "externalBin": [],
"dialog": { "icon": [
"all": true "icons/32x32.png",
}, "icons/128x128.png",
"fs": { "icons/128x128@2x.png",
"all": true, "icons/icon.icns",
"scope": [ "icons/icon.ico"
"**", ],
"**/*", "targets": [
"/**/*", "appimage",
"$CONFIG/stort/", "deb"
"$CONFIG/stort/**", ],
"$HOME/.local/share/stort/*", "longDescription": "Launcher for Steam Deck",
"$HOME/.local/share/stort/**" "resources": [],
] "linux": {
},
"notification": {
"all": true
},
"path": {
"all": true
},
"process": {
"all": true
},
"protocol": {
"all": true,
"asset": true,
"assetScope": [
"$APPDATA/*",
"$APPDATA/**"
]
},
"window": {
"all": true
}
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": { "deb": {
"depends": [] "depends": []
}, }
"externalBin": [], }
"icon": [ },
"icons/32x32.png", "productName": "stort",
"icons/128x128.png", "mainBinaryName": "stort",
"icons/128x128@2x.png", "version": "0.1.0",
"icons/icon.icns", "identifier": "stort",
"icons/icon.ico" "plugins": {},
], "app": {
"identifier": "stort",
"longDescription": "Launcher for Steam Deck",
"resources": [],
"shortDescription": "Launcher for Steam Deck",
"targets": [
"appimage",
"deb"
]
},
"security": {
"csp": null
},
"windows": [ "windows": [
{ {
"decorations": false, "decorations": false,
@ -84,6 +46,16 @@
"title": "Stort", "title": "Stort",
"width": 800 "width": 800
} }
] ],
"security": {
"assetProtocol": {
"scope": [
"$APPDATA/*",
"$APPDATA/**"
],
"enable": true
},
"csp": null
}
} }
} }

View file

@ -1,5 +1,6 @@
import { Box, Stack } from "@mui/material"; import { Box, Stack } from "@mui/material";
import { useSettings } from "../../contexts/SettingsContext"; import { useSettings } from "../../contexts/SettingsContext";
import { hexToRgba } from "../../lib/utils/color";
import { Settings } from "../HeaderBar/Settings/Settings"; import { Settings } from "../HeaderBar/Settings/Settings";
import { WindowButtons } from "../HeaderBar/WindowButtons"; import { WindowButtons } from "../HeaderBar/WindowButtons";
@ -7,6 +8,8 @@ export const FooterBar = () => {
// contexts // contexts
const { settings } = useSettings(); const { settings } = useSettings();
const { r, g, b, a } = hexToRgba(settings.colors.footer_color);
return ( return (
<Box <Box
sx={{ sx={{
@ -14,6 +17,7 @@ export const FooterBar = () => {
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
height: "66px", height: "66px",
// zIndex: 1000000,
}} }}
> >
<Box <Box
@ -21,8 +25,8 @@ export const FooterBar = () => {
data-tauri-drag-region="true" data-tauri-drag-region="true"
sx={{ sx={{
alignItems: "center", alignItems: "center",
backdropFilter: "blur(10px)", backdropFilter: `blur(${settings.style.blur_radius}px)`,
backgroundColor: "rgba(0, 0, 0, 0.5)", backgroundColor: `rgba(${r}, ${g}, ${b}, 0.5)`,
borderRadius: settings.style.radius + "px", borderRadius: settings.style.radius + "px",
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",

View file

@ -1,5 +1,5 @@
import { Box, Button, useTheme } from "@mui/material"; import { Box, Button, useTheme } from "@mui/material";
import { convertFileSrc } from "@tauri-apps/api/tauri"; import { convertFileSrc } from "@tauri-apps/api/core";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSettings } from "../../contexts/SettingsContext"; import { useSettings } from "../../contexts/SettingsContext";
import { FooterBar } from "../FooterBar/FooterBar"; import { FooterBar } from "../FooterBar/FooterBar";
@ -24,7 +24,6 @@ export const Layout = () => {
return ( return (
<Box <Box
sx={{ sx={{
// Use the URL function for background images
backgroundColor: theme.palette.background.default, backgroundColor: theme.palette.background.default,
backgroundImage: settings.background.background_image_path ? `url(${imageUrl})` : "", backgroundImage: settings.background.background_image_path ? `url(${imageUrl})` : "",
backgroundSize: "cover", backgroundSize: "cover",
@ -41,18 +40,9 @@ export const Layout = () => {
display: "flex", display: "flex",
flexGrow: 1, flexGrow: 1,
overflow: "auto", overflow: "auto",
p: 1,
}} }}
> ></Box>
<Box>
<Button
onClick={() => {
console.log(imageUrl);
}}
>
set background
</Button>
</Box>
</Box>
<FooterBar /> <FooterBar />
</Box> </Box>
); );

View file

@ -1,21 +1,28 @@
import { BugReportOutlined, SettingsOutlined } from "@mui/icons-material"; import {
import { LoadingButton, TabContext, TabList, TabPanel } from "@mui/lab"; BugReportOutlined,
FormatPaintOutlined,
PaletteOutlined,
SettingsOutlined,
WallpaperOutlined,
WebAssetOutlined,
} from "@mui/icons-material";
import { TabContext, TabList, TabPanel } from "@mui/lab";
import { Box, Button, IconButton, Tab, Tooltip, useTheme } from "@mui/material"; import { Box, Button, IconButton, Tab, Tooltip, useTheme } from "@mui/material";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSettings } from "../../../contexts/SettingsContext"; import { useSettings } from "../../../contexts/SettingsContext";
import { stagedSettingsAtom } from "../../../lib/store/jotai/settings"; import { stagedSettingsAtom } from "../../../lib/store/jotai/settings";
import { FloatingDialog } from "../../Generic/FloatingDialog"; import { FloatingDialog } from "../../Generic/FloatingDialog";
import { Background } from "./SettingsTabs/Background"; import { Background } from "./SettingsTabs/Background";
import { Debug } from "./SettingsTabs/Debug";
import { Style } from "./SettingsTabs/Style"; import { Style } from "./SettingsTabs/Style";
import { Window } from "./SettingsTabs/Window"; import { Window } from "./SettingsTabs/Window";
import { Colors } from "./SettingsTabs/Colors";
export const Settings = () => { export const Settings = () => {
// contexts // contexts
const theme = useTheme(); const theme = useTheme();
const { settings, updateSettings } = useSettings(); const { fetchSettings, settings, updateSettings } = useSettings();
const router = useRouter();
// atoms // atoms
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom); const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
@ -24,8 +31,6 @@ export const Settings = () => {
const [settingsOpenState, setSettingsOpenState] = useState<boolean>(false); const [settingsOpenState, setSettingsOpenState] = useState<boolean>(false);
const [settingsMaximisedState, setSettingsMaximisedState] = useState<boolean>(false); const [settingsMaximisedState, setSettingsMaximisedState] = useState<boolean>(false);
const [subTabValue, setSubTabValue] = useState("style"); const [subTabValue, setSubTabValue] = useState("style");
const [applyLoading, setApplyLoading] = useState<boolean>(false);
const [saveLoading, setSaveLoading] = useState<boolean>(false);
const toggleSettings = () => { const toggleSettings = () => {
setSettingsOpenState((prevState) => !prevState); setSettingsOpenState((prevState) => !prevState);
@ -47,9 +52,7 @@ export const Settings = () => {
}; };
const applyClickEvent = () => { const applyClickEvent = () => {
setApplyLoading(true);
updateSettings(stagedSettings); updateSettings(stagedSettings);
setApplyLoading(false);
}; };
const saveClickEvent = () => { const saveClickEvent = () => {
@ -60,25 +63,13 @@ export const Settings = () => {
// set staged settings back to current settings on close // set staged settings back to current settings on close
useEffect(() => { useEffect(() => {
if (settingsOpenState) fetchSettings();
if (!settingsOpenState) setStagedSettings(settings); if (!settingsOpenState) setStagedSettings(settings);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [settingsOpenState]); }, [settingsOpenState]);
return ( return (
<FloatingDialog <FloatingDialog
actionButtons={
<>
<IconButton
onClick={() => {
router.push("/testing");
}}
sx={{
mr: 1,
}}
>
<BugReportOutlined />
</IconButton>
</>
}
body={ body={
<Box <Box
sx={{ sx={{
@ -100,26 +91,41 @@ export const Settings = () => {
scrollButtons={true} scrollButtons={true}
sx={{ sx={{
borderBottom: "1px solid " + theme.palette.divider, borderBottom: "1px solid " + theme.palette.divider,
height: "84px",
}} }}
variant="scrollable" variant="scrollable"
> >
<Tab <Tab
icon={<FormatPaintOutlined />}
label="Style" label="Style"
value="style" value="style"
/> />
<Tab <Tab
icon={<PaletteOutlined />}
label="Colors"
value="colors"
/>
<Tab
icon={<WallpaperOutlined />}
label="Background" label="Background"
value="background" value="background"
/> />
<Tab <Tab
icon={<WebAssetOutlined />}
label="Window" label="Window"
value="window" value="window"
/> />
<Tab
icon={<BugReportOutlined />}
label="Debug"
value="debug"
/>
</TabList> </TabList>
<Box <Box
overflow="auto"
sx={{ sx={{
height: "100%", height: "100%",
overflowY: "auto",
m: 0,
width: "100%", width: "100%",
}} }}
> >
@ -129,6 +135,12 @@ export const Settings = () => {
> >
<Style /> <Style />
</TabPanel> </TabPanel>
<TabPanel
sx={{ p: 2 }}
value="colors"
>
<Colors />
</TabPanel>
<TabPanel <TabPanel
sx={{ p: 2 }} sx={{ p: 2 }}
value="background" value="background"
@ -141,6 +153,12 @@ export const Settings = () => {
> >
<Window /> <Window />
</TabPanel> </TabPanel>
<TabPanel
sx={{ p: 2 }}
value="debug"
>
<Debug />
</TabPanel>
</Box> </Box>
</TabContext> </TabContext>
</Box> </Box>
@ -164,8 +182,7 @@ export const Settings = () => {
Cancel Cancel
</Button> </Button>
<LoadingButton <Button
loading={applyLoading}
onClick={applyClickEvent} onClick={applyClickEvent}
size="small" size="small"
sx={{ sx={{
@ -174,15 +191,14 @@ export const Settings = () => {
variant="outlined" variant="outlined"
> >
Apply Apply
</LoadingButton> </Button>
<LoadingButton <Button
loading={saveLoading}
onClick={saveClickEvent} onClick={saveClickEvent}
size="small" size="small"
variant="contained" variant="contained"
> >
Save Save
</LoadingButton> </Button>
</Box> </Box>
} }
close={closeSettings} close={closeSettings}

View file

@ -1,8 +1,8 @@
import { DeleteOutline, FileOpenOutlined } from "@mui/icons-material"; import { DeleteOutline, FileOpenOutlined } from "@mui/icons-material";
import { Box, Button, Stack, TextField } from "@mui/material"; import { Box, Button, CircularProgress, LinearProgress, Stack, TextField, Typography } from "@mui/material";
import { open } from "@tauri-apps/api/dialog"; import { open } from "@tauri-apps/plugin-dialog";
import { readBinaryFile } from "@tauri-apps/api/fs"; import { readBinaryFile } from "@tauri-apps/plugin-fs";
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from "@tauri-apps/api/core";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import Image from "next/image"; import Image from "next/image";
import { FC, useEffect, useState } from "react"; import { FC, useEffect, useState } from "react";
@ -18,7 +18,7 @@ interface BackgroundProps {
export const Background: FC<BackgroundProps> = ({ sx }) => { export const Background: FC<BackgroundProps> = ({ sx }) => {
// contexts // contexts
const { settings, updateSettings } = useSettings(); const { settings } = useSettings();
// atoms // atoms
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom); const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
@ -112,6 +112,7 @@ export const Background: FC<BackgroundProps> = ({ sx }) => {
applyWallpaper(); applyWallpaper();
setOldWallpaperPath(settings.background.background_image_path); setOldWallpaperPath(settings.background.background_image_path);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [settings.background.background_image_path]); }, [settings.background.background_image_path]);
// update the preview image when stagedSettings.background.background_image_path changes // update the preview image when stagedSettings.background.background_image_path changes
@ -123,6 +124,8 @@ export const Background: FC<BackgroundProps> = ({ sx }) => {
} }
}, [targetWallpaperPath]); }, [targetWallpaperPath]);
useEffect(() => {});
return ( return (
<Box sx={{ sx }}> <Box sx={{ sx }}>
<CategoryTitle title="Wallpaper" /> <CategoryTitle title="Wallpaper" />
@ -140,11 +143,11 @@ export const Background: FC<BackgroundProps> = ({ sx }) => {
> >
{imageBlob ? ( {imageBlob ? (
<Image <Image
src={imageBlob || "oh no"}
alt="Image not found" alt="Image not found"
// fill the box r/catsareliquid // fill the box r/catsareliquid
layout="fill" layout="fill"
objectFit="cover" objectFit="cover"
src={imageBlob}
/> />
) : ( ) : (
<Box <Box
@ -152,13 +155,33 @@ export const Background: FC<BackgroundProps> = ({ sx }) => {
alignItems: "center", alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.1)", backgroundColor: "rgba(0, 0, 0, 0.1)",
display: "flex", display: "flex",
flexDirection: "column",
height: "100%", height: "100%",
justifyContent: "center", justifyContent: "center",
position: "absolute", position: "absolute",
width: "100%", width: "100%",
}} }}
> >
No image selected {targetWallpaperPath ? (
<CircularProgress color="primary" />
) : (
<>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
alt="Image not found"
src="/images/cry.webp"
style={{
height: "50%",
}}
/>
<Typography
color="text.disabled"
variant="h6"
>
No image selected
</Typography>
</>
)}
</Box> </Box>
)} )}
</Box> </Box>
@ -194,45 +217,6 @@ export const Background: FC<BackgroundProps> = ({ sx }) => {
</Button> </Button>
</Stack> </Stack>
</Box> </Box>
<CategoryTitle title="Colors" />
<SettingsItem
defaultText={defaultSettings.background.background_color}
description="Background color"
input={
<TextField
name="background_color"
onChange={(e) => {
handleSettingsBackgroundValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.background.background_color}
variant="standard"
/>
}
/>
<SettingsItem
defaultText={defaultSettings.background.background_color_popup}
description="Popup background color"
input={
<TextField
name="background_color_popup"
onChange={(e) => {
handleSettingsBackgroundValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.background.background_color_popup}
variant="standard"
/>
}
/>
</Box> </Box>
); );
}; };

View file

@ -0,0 +1,112 @@
import { Box, TextField } from "@mui/material";
import { useAtom } from "jotai";
import { FC } from "react";
import { defaultSettings } from "../../../../lib/settings";
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
import { CategoryTitle } from "../CategoryTitle";
import { SettingsItem } from "../SettingsItem";
interface ColorsProps {
sx?: any;
}
export const Colors: FC<ColorsProps> = ({ sx }) => {
// atoms
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
// states
const handleSettingsColorsValueChange = (settingKey: string, settingValue: boolean | number | string | number[]) => {
const newSettings = {
...stagedSettings,
colors: {
...stagedSettings.colors,
[settingKey]: settingValue,
},
};
setStagedSettings(newSettings);
return newSettings;
};
return (
<Box sx={{ sx }}>
<CategoryTitle title="Colors" />
<SettingsItem
defaultText={defaultSettings.colors.accent_color}
description="Accent color"
input={
<TextField
name="accent_color"
onChange={(e) => {
handleSettingsColorsValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.colors.accent_color}
variant="standard"
/>
}
/>
<SettingsItem
defaultText={defaultSettings.colors.background_color}
description="Background color"
input={
<TextField
name="background_color"
onChange={(e) => {
handleSettingsColorsValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.colors.background_color}
variant="standard"
/>
}
/>
<SettingsItem
defaultText={defaultSettings.colors.background_color_popup}
description="Popup background color"
input={
<TextField
name="background_color_popup"
onChange={(e) => {
handleSettingsColorsValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.colors.background_color_popup}
variant="standard"
/>
}
/>
<SettingsItem
defaultText={defaultSettings.colors.footer_color}
description="Footer color"
input={
<TextField
name="footer_color"
onChange={(e) => {
handleSettingsColorsValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.colors.footer_color}
variant="standard"
/>
}
/>
</Box>
);
};

View file

@ -0,0 +1,52 @@
import { Box, Typography } from "@mui/material";
import { FC } from "react";
import { useSettings } from "../../../../contexts/SettingsContext";
import { CategoryTitle } from "../CategoryTitle";
import { stagedSettingsAtom } from "../../../../lib/store/jotai/settings";
import { useAtom } from "jotai";
import { defaultSettings } from "../../../../lib/settings";
interface DebugProps {
sx?: any;
}
export const Debug: FC<DebugProps> = ({ sx }) => {
// contexts
const { settings, resetSettings } = useSettings();
// atoms
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
return (
<Box sx={{ sx }}>
<CategoryTitle title="Debug Panel" />
<Typography
color="error"
variant="h6"
>
Here do be dragons
</Typography>
<button
onClick={() => {
setStagedSettings(defaultSettings);
}}
>
reset settings
</button>
<button
onClick={() => {
console.log(settings);
}}
>
log settings
</button>
<button
onClick={() => {
console.log(settings.background.background_image_path);
}}
>
testing
</button>
</Box>
);
};

View file

@ -50,25 +50,6 @@ export const Style: FC<StyleProps> = ({ sx }) => {
/> />
} }
/> />
<SettingsItem
defaultText={defaultSettings.style.accent_color}
description="Accent color"
input={
<TextField
name="accent_color"
onChange={(e) => {
handleSettingsStyleValueChange(e.target.name, e.target.value);
}}
sx={{
width: "100%",
}}
size="small"
type="color"
value={stagedSettings.style.accent_color}
variant="standard"
/>
}
/>
<SettingsItem <SettingsItem
defaultText={defaultSettings.style.font_family} defaultText={defaultSettings.style.font_family}
description="Font family" description="Font family"

View file

@ -44,6 +44,19 @@ export const Window: FC<WindowProps> = ({ sx }) => {
} }
/> />
<CategoryTitle title="Titlebar Buttons" /> <CategoryTitle title="Titlebar Buttons" />
<SettingsItem
defaultText={defaultSettings.window.fullscreen_button ? "On" : "Off"}
description="Fullscreen button"
input={
<Switch
checked={stagedSettings.window.fullscreen_button}
name="fullscreen_button"
onChange={(e) => {
handleSettingsWindowValueChange(e.target.name, e.target.checked);
}}
/>
}
/>
<SettingsItem <SettingsItem
defaultText={defaultSettings.window.minimize_button ? "On" : "Off"} defaultText={defaultSettings.window.minimize_button ? "On" : "Off"}
description="Minimize button" description="Minimize button"

View file

@ -1,7 +1,6 @@
import { Close, CloseFullscreen, Minimize } from "@mui/icons-material"; import { Close, CloseFullscreen, Fullscreen, FullscreenExit, Minimize, WebAssetOutlined } from "@mui/icons-material";
import { Box, IconButton, Stack, useTheme } from "@mui/material"; import { Box, IconButton, Stack, useTheme } from "@mui/material";
import { exit } from "@tauri-apps/api/process"; import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
import { WebviewWindow } from "@tauri-apps/api/window";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSettings } from "../../contexts/SettingsContext"; import { useSettings } from "../../contexts/SettingsContext";
@ -24,11 +23,15 @@ export const WindowButtons = () => {
setAppWindow(appWindow); setAppWindow(appWindow);
}; };
const toggleFullscreen = async () => {
appWindow?.setFullscreen(!(await appWindow?.isFullscreen()));
};
const minimize = () => { const minimize = () => {
appWindow?.minimize(); appWindow?.minimize();
}; };
const maximize = () => { const toggleMaximize = () => {
appWindow?.toggleMaximize(); appWindow?.toggleMaximize();
}; };
@ -52,6 +55,17 @@ export const WindowButtons = () => {
direction="row" direction="row"
spacing={1} spacing={1}
> >
{settings.window.fullscreen_button && (
<IconButton
onClick={toggleFullscreen}
size="small"
sx={{
backgroundColor: userTheme.palette.grey[800],
}}
>
{appWindow?.isFullscreen() ? <FullscreenExit fontSize="inherit" /> : <Fullscreen fontSize="inherit" />}
</IconButton>
)}
{settings.window.minimize_button && ( {settings.window.minimize_button && (
<IconButton <IconButton
onClick={minimize} onClick={minimize}
@ -60,23 +74,18 @@ export const WindowButtons = () => {
backgroundColor: userTheme.palette.grey[800], backgroundColor: userTheme.palette.grey[800],
}} }}
> >
<Minimize <Minimize fontSize="inherit" />
fontSize="inherit"
sx={{
backgroundColor: userTheme.palette.grey[800],
}}
/>
</IconButton> </IconButton>
)} )}
{settings.window.maximize_button && ( {settings.window.maximize_button && (
<IconButton <IconButton
onClick={maximize} onClick={toggleMaximize}
size="small" size="small"
sx={{ sx={{
backgroundColor: userTheme.palette.grey[800], backgroundColor: userTheme.palette.grey[800],
}} }}
> >
<CloseFullscreen fontSize="inherit" /> <WebAssetOutlined fontSize="inherit" />
</IconButton> </IconButton>
)} )}
<IconButton <IconButton

View file

@ -1,13 +1,16 @@
import { createContext, FC, ReactNode, useContext, useEffect, useState } from "react"; import { createContext, FC, ReactNode, useContext, useEffect, useState } from "react";
import { logcat } from "../lib/logcatService"; import { logcat } from "../lib/logcatService";
import { defaultSettings, readConfigFile, SettingsType, writeConfigFile } from "../lib/settings"; import { defaultSettings, readConfigFile, SettingsType, writeConfigFile } from "../lib/settings";
import { useAtom } from "jotai";
import { stagedSettingsAtom } from "../lib/store/jotai/settings";
// settings context // settings context
type SettingsContextProps = { type SettingsContextProps = {
fetchSettings: () => void;
resetSettings: () => void;
settings: SettingsType; settings: SettingsType;
settingsLoading: boolean; settingsLoading: boolean;
updateSettings: (updates: SettingsType) => void; updateSettings: (updates: SettingsType) => void;
resetSettings: () => void;
}; };
const SettingsContext = createContext<SettingsContextProps | undefined>(undefined); const SettingsContext = createContext<SettingsContextProps | undefined>(undefined);
@ -15,6 +18,9 @@ const SettingsContext = createContext<SettingsContextProps | undefined>(undefine
export const SettingsProvider: FC<{ children: ReactNode }> = ({ children }) => { export const SettingsProvider: FC<{ children: ReactNode }> = ({ children }) => {
logcat.log("Initializing settings...", "INFO"); logcat.log("Initializing settings...", "INFO");
// atoms
const [stagedSettings, setStagedSettings] = useAtom(stagedSettingsAtom);
// states // states
const [settings, setSettings] = useState<SettingsType>(defaultSettings); const [settings, setSettings] = useState<SettingsType>(defaultSettings);
const [settingsLoading, setSettingsLoading] = useState<boolean>(true); const [settingsLoading, setSettingsLoading] = useState<boolean>(true);
@ -22,7 +28,11 @@ export const SettingsProvider: FC<{ children: ReactNode }> = ({ children }) => {
const fetchSettings = async () => { const fetchSettings = async () => {
try { try {
const existingSettings = await readConfigFile(); const existingSettings = await readConfigFile();
// set settings state to existing settings
setSettings(existingSettings); setSettings(existingSettings);
// also update the settings atom
setStagedSettings(existingSettings);
logcat.log("Settings loaded successfully", "INFO");
} catch (error) { } catch (error) {
logcat.log(`Failed to load settings: ${error}`, "ERROR"); logcat.log(`Failed to load settings: ${error}`, "ERROR");
} finally { } finally {
@ -60,10 +70,11 @@ export const SettingsProvider: FC<{ children: ReactNode }> = ({ children }) => {
return ( return (
<SettingsContext.Provider <SettingsContext.Provider
value={{ value={{
fetchSettings,
resetSettings,
settings, settings,
settingsLoading, settingsLoading,
updateSettings, updateSettings,
resetSettings,
}} }}
> >
{children} {children}

View file

@ -15,12 +15,12 @@ export const UserThemeProvider: FC<UserThemeProviderProps> = ({ children }) => {
const userPalette = { const userPalette = {
primary: { primary: {
// light: '#a1c3f9', // light: '#a1c3f9',
main: settings.style.accent_color || "#8ab4f8", main: settings.colors.accent_color || "#8ab4f8",
// dark: '#4285f4', // dark: '#4285f4',
}, },
secondary: { secondary: {
// light: '#a1c3f9', // light: '#a1c3f9',
main: settings.style.accent_color || "#8ab4f8", main: settings.colors.accent_color || "#8ab4f8",
// dark: '#4285f4', // dark: '#4285f4',
}, },
error: { error: {
@ -60,8 +60,8 @@ export const UserThemeProvider: FC<UserThemeProviderProps> = ({ children }) => {
A700: "#616161", A700: "#616161",
}, },
background: { background: {
paper: settings.style.dark_mode ? settings.background.background_color_popup : "#fff", paper: settings.style.dark_mode ? settings.colors.background_color_popup : "#fff",
default: settings.style.dark_mode ? settings.background.background_color : "#fff", default: settings.style.dark_mode ? settings.colors.background_color : "#fff",
}, },
}; };

View file

@ -3,12 +3,15 @@ import { readTomlFile, writeTomlFile } from "./utils/toml";
export const defaultSettings = { export const defaultSettings = {
background: { background: {
background_color: "#202124" as string,
background_color_popup: "#303134" as string,
background_image_path: "" as string, background_image_path: "" as string,
}, },
style: { colors: {
accent_color: "#8ab4f8" as string, accent_color: "#8ab4f8" as string,
background_color: "#202124" as string,
background_color_popup: "#303134" as string,
footer_color: "#000000" as string,
},
style: {
blur_radius: 10 as number, blur_radius: 10 as number,
dark_mode: true as boolean, dark_mode: true as boolean,
font_family: "monospace" as string, font_family: "monospace" as string,
@ -17,9 +20,10 @@ export const defaultSettings = {
radius: 8 as number, radius: 8 as number,
transition_duration: 200 as number, transition_duration: 200 as number,
window_height: 80 as number, window_height: 80 as number,
window_width: 400 as number, window_width: 500 as number,
}, },
window: { window: {
fullscreen_button: false as boolean,
maximize_button: false as boolean, maximize_button: false as boolean,
minimize_button: false as boolean, minimize_button: false as boolean,
start_fullscreen: false as boolean, // TODO: this should be true on prod start_fullscreen: false as boolean, // TODO: this should be true on prod

View file

@ -1,4 +1,4 @@
import { createDir } from "@tauri-apps/api/fs"; import { createDir } from "@tauri-apps/plugin-fs";
export const testing = async () => { export const testing = async () => {
await createDir("/home/vomitblood/.config/stort/"); await createDir("/home/vomitblood/.config/stort/");

View file

@ -1,4 +1,4 @@
import { readTextFile, writeTextFile } from "@tauri-apps/api/fs"; import { readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
export const readJsonFile = async <T>(path: string): Promise<T | null> => { export const readJsonFile = async <T>(path: string): Promise<T | null> => {
try { try {

View file

@ -1,4 +1,4 @@
import { readTextFile, writeTextFile } from "@tauri-apps/api/fs"; import { readTextFile, writeTextFile } from "@tauri-apps/plugin-fs";
import { parse, stringify } from "smol-toml"; import { parse, stringify } from "smol-toml";
export const readTomlFile = async <T>(path: string): Promise<T | null> => { export const readTomlFile = async <T>(path: string): Promise<T | null> => {

View file

@ -16,6 +16,9 @@
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"noImplicitAny": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
}, },
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",