add reduce motion toggle and bump to v1.0.1

Add in-app reduce motion setting under Settings > Appearance so users
can disable animations without changing their OS preference. Applies a
.reduce-motion CSS class to kill all CSS transitions/animations and
wraps the app in MotionConfig to globally disable Framer Motion springs,
layout animations, and enter/exit transitions. Setting persists to disk.

Also removes leftover default Square*.png icons and bumps version to
1.0.1.
This commit is contained in:
Your Name
2026-02-16 17:51:23 +02:00
parent d1636745f1
commit 30263d6ac7
21 changed files with 1339 additions and 9 deletions

1282
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "openpylon",
"private": true,
"version": "1.0.0",
"version": "1.0.1",
"type": "module",
"scripts": {
"dev": "vite",
@@ -45,7 +45,10 @@
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0",
"png-to-ico": "^3.0.1",
"puppeteer-core": "^24.37.3",
"shadcn": "^3.8.4",
"sharp": "^0.34.5",
"tailwindcss": "^4.1.18",
"tw-animate-css": "^1.4.0",
"typescript": "~5.8.3",

2
src-tauri/Cargo.lock generated
View File

@@ -2360,7 +2360,7 @@ dependencies = [
[[package]]
name = "openpylon"
version = "0.1.0"
version = "1.0.1"
dependencies = [
"serde",
"serde_json",

View File

@@ -1,6 +1,6 @@
[package]
name = "openpylon"
version = "1.0.0"
version = "1.0.1"
description = "A Tauri App"
authors = ["you"]
edition = "2021"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "openpylon",
"version": "1.0.0",
"version": "1.0.1",
"identifier": "com.openpylon.app",
"build": {
"beforeDevCommand": "npm run dev",

View File

@@ -1,6 +1,6 @@
import { useState, useEffect, useCallback } from "react";
import { getCurrentWindow, LogicalSize, LogicalPosition } from "@tauri-apps/api/window";
import { AnimatePresence, motion } from "framer-motion";
import { AnimatePresence, motion, MotionConfig } from "framer-motion";
import { springs, fadeSlideLeft, fadeSlideRight } from "@/lib/motion";
import { useAppStore } from "@/stores/app-store";
import { useBoardStore } from "@/stores/board-store";
@@ -18,6 +18,7 @@ export default function App() {
const initialized = useAppStore((s) => s.initialized);
const init = useAppStore((s) => s.init);
const view = useAppStore((s) => s.view);
const reduceMotion = useAppStore((s) => s.settings.reduceMotion);
const [settingsOpen, setSettingsOpen] = useState(false);
const [shortcutHelpOpen, setShortcutHelpOpen] = useState(false);
@@ -127,7 +128,7 @@ export default function App() {
}
return (
<>
<MotionConfig reducedMotion={reduceMotion ? "always" : "user"}>
<AppShell>
<AnimatePresence mode="wait">
{view.type === "board-list" ? (
@@ -161,6 +162,6 @@ export default function App() {
<SettingsDialog open={settingsOpen} onOpenChange={setSettingsOpen} />
<ToastContainer />
<ShortcutHelpModal open={shortcutHelpOpen} onOpenChange={setShortcutHelpOpen} />
</>
</MotionConfig>
);
}

View File

@@ -92,6 +92,7 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
const setAccentColor = useAppStore((s) => s.setAccentColor);
const setUiZoom = useAppStore((s) => s.setUiZoom);
const setDensity = useAppStore((s) => s.setDensity);
const setReduceMotion = useAppStore((s) => s.setReduceMotion);
const setDefaultColumnWidth = useAppStore((s) => s.setDefaultColumnWidth);
const roRef = useRef<ResizeObserver | null>(null);
@@ -268,6 +269,30 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
))}
</div>
</div>
<Separator />
{/* Reduce Motion */}
<div>
<SectionLabel>Reduce Motion</SectionLabel>
<p className="mb-2 text-xs text-pylon-text-secondary">
Reduces animations and transitions for accessibility.
</p>
<div className="flex gap-2">
{([false, true] as const).map((value) => (
<Button
key={String(value)}
type="button"
variant={settings.reduceMotion === value ? "default" : "outline"}
size="sm"
onClick={() => setReduceMotion(value)}
className="flex-1"
>
{value ? "Reduced" : "Normal"}
</Button>
))}
</div>
</div>
</>
)}

View File

@@ -200,3 +200,12 @@
scroll-behavior: auto !important;
}
}
.reduce-motion *,
.reduce-motion *::before,
.reduce-motion *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}

View File

@@ -87,4 +87,5 @@ export const appSettingsSchema = z.object({
boardSortOrder: z.enum(["manual", "title", "created", "updated"]).default("updated"),
boardManualOrder: z.array(z.string()).default([]),
lastNotificationCheck: z.string().nullable().default(null),
reduceMotion: z.boolean().default(false),
});

View File

@@ -21,6 +21,7 @@ interface AppState {
setView: (view: View) => void;
refreshBoards: () => Promise<void>;
addRecentBoard: (boardId: string) => void;
setReduceMotion: (reduceMotion: boolean) => void;
setBoardSortOrder: (order: BoardSortOrder) => void;
setBoardManualOrder: (ids: string[]) => void;
getSortedBoards: () => BoardMeta[];
@@ -36,6 +37,10 @@ function applyTheme(theme: AppSettings["theme"]): void {
}
}
function applyReduceMotion(on: boolean): void {
document.documentElement.classList.toggle("reduce-motion", on);
}
function applyAppearance(settings: AppSettings): void {
const root = document.documentElement;
root.style.fontSize = `${settings.uiZoom * 16}px`;
@@ -70,6 +75,7 @@ export const useAppStore = create<AppState>((set, get) => ({
boardSortOrder: "updated",
boardManualOrder: [],
lastNotificationCheck: null,
reduceMotion: false,
},
boards: [],
view: { type: "board-list" },
@@ -82,6 +88,7 @@ export const useAppStore = create<AppState>((set, get) => ({
set({ settings, boards, initialized: true });
applyTheme(settings.theme);
applyAppearance(settings);
applyReduceMotion(settings.reduceMotion);
// Due date notifications (once per hour)
const lastCheck = settings.lastNotificationCheck;
@@ -148,6 +155,11 @@ export const useAppStore = create<AppState>((set, get) => ({
updateAndSave(get, set, { defaultColumnWidth });
},
setReduceMotion: (reduceMotion) => {
updateAndSave(get, set, { reduceMotion });
applyReduceMotion(reduceMotion);
},
setView: (view) => set({ view }),
refreshBoards: async () => {

View File

@@ -22,4 +22,5 @@ export interface AppSettings {
boardSortOrder: BoardSortOrder;
boardManualOrder: string[];
lastNotificationCheck: string | null;
reduceMotion: boolean;
}