Bläddra i källkod

Optimize bundle size and enable gzip compression

- Replace react-color with react-colorful (saves ~144kB)
- Code-split vendor chunks for parallel loading and better caching
- Enable gzip compression in nginx config
- Bump version to 4.1.2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tuanchris 4 månader sedan
förälder
incheckning
31040ae47c

+ 1 - 1
VERSION

@@ -1 +1 @@
-4.1.1
+4.1.2

+ 3 - 108
frontend/package-lock.json

@@ -32,7 +32,6 @@
         "motion": "^12.27.1",
         "next-themes": "^0.4.6",
         "react": "^19.2.0",
-        "react-color": "^2.19.3",
         "react-colorful": "^5.6.1",
         "react-dom": "^19.2.0",
         "react-resizable-panels": "^4.4.0",
@@ -48,7 +47,6 @@
         "@testing-library/user-event": "^14.6.1",
         "@types/node": "^24.10.4",
         "@types/react": "^19.2.5",
-        "@types/react-color": "^3.0.13",
         "@types/react-dom": "^19.2.3",
         "@vitejs/plugin-react": "^5.1.1",
         "@vitest/coverage-v8": "^3.2.4",
@@ -2912,15 +2910,6 @@
         "url": "https://github.com/sponsors/nzakas"
       }
     },
-    "node_modules/@icons/material": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
-      "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
-      "license": "MIT",
-      "peerDependencies": {
-        "react": "*"
-      }
-    },
     "node_modules/@inquirer/ansi": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",
@@ -5474,19 +5463,6 @@
         "csstype": "^3.2.2"
       }
     },
-    "node_modules/@types/react-color": {
-      "version": "3.0.13",
-      "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.13.tgz",
-      "integrity": "sha512-2c/9FZ4ixC5T3JzN0LP5Cke2Mf0MKOP2Eh0NPDPWmuVH3NjPyhEjqNMQpN1Phr5m74egAy+p2lYNAFrX1z9Yrg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/reactcss": "*"
-      },
-      "peerDependencies": {
-        "@types/react": "*"
-      }
-    },
     "node_modules/@types/react-dom": {
       "version": "19.2.3",
       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
@@ -5497,16 +5473,6 @@
         "@types/react": "^19.2.0"
       }
     },
-    "node_modules/@types/reactcss": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.13.tgz",
-      "integrity": "sha512-gi3S+aUi6kpkF5vdhUsnkwbiSEIU/BEJyD7kBy2SudWBUuKmJk8AQKE0OVcQQeEy40Azh0lV6uynxlikYIJuwg==",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "@types/react": "*"
-      }
-    },
     "node_modules/@types/resolve": {
       "version": "1.20.2",
       "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -9823,6 +9789,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/js-yaml": {
@@ -10326,12 +10293,7 @@
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
       "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
-      "license": "MIT"
-    },
-    "node_modules/lodash-es": {
-      "version": "4.17.22",
-      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz",
-      "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/lodash.debounce": {
@@ -10398,18 +10360,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/loose-envify": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
-      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
-      "license": "MIT",
-      "dependencies": {
-        "js-tokens": "^3.0.0 || ^4.0.0"
-      },
-      "bin": {
-        "loose-envify": "cli.js"
-      }
-    },
     "node_modules/loupe": {
       "version": "3.2.1",
       "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
@@ -10498,12 +10448,6 @@
         "node": ">=10"
       }
     },
-    "node_modules/material-colors": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
-      "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==",
-      "license": "ISC"
-    },
     "node_modules/material-icons": {
       "version": "1.13.14",
       "resolved": "https://registry.npmjs.org/material-icons/-/material-icons-1.13.14.tgz",
@@ -10926,6 +10870,7 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
       "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -11584,17 +11529,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/prop-types": {
-      "version": "15.8.1",
-      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
-      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
-      "license": "MIT",
-      "dependencies": {
-        "loose-envify": "^1.4.0",
-        "object-assign": "^4.1.1",
-        "react-is": "^16.13.1"
-      }
-    },
     "node_modules/proxy-addr": {
       "version": "2.0.7",
       "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -11701,24 +11635,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/react-color": {
-      "version": "2.19.3",
-      "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
-      "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
-      "license": "MIT",
-      "dependencies": {
-        "@icons/material": "^0.2.4",
-        "lodash": "^4.17.15",
-        "lodash-es": "^4.17.15",
-        "material-colors": "^1.2.1",
-        "prop-types": "^15.5.10",
-        "reactcss": "^1.2.0",
-        "tinycolor2": "^1.4.1"
-      },
-      "peerDependencies": {
-        "react": "*"
-      }
-    },
     "node_modules/react-colorful": {
       "version": "5.6.1",
       "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz",
@@ -11741,12 +11657,6 @@
         "react": "^19.2.3"
       }
     },
-    "node_modules/react-is": {
-      "version": "16.13.1",
-      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
-      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
-      "license": "MIT"
-    },
     "node_modules/react-refresh": {
       "version": "0.18.0",
       "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
@@ -11874,15 +11784,6 @@
         }
       }
     },
-    "node_modules/reactcss": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
-      "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
-      "license": "MIT",
-      "dependencies": {
-        "lodash": "^4.0.1"
-      }
-    },
     "node_modules/recast": {
       "version": "0.23.11",
       "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz",
@@ -13331,12 +13232,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/tinycolor2": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
-      "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==",
-      "license": "MIT"
-    },
     "node_modules/tinyexec": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",

+ 0 - 2
frontend/package.json

@@ -40,7 +40,6 @@
     "motion": "^12.27.1",
     "next-themes": "^0.4.6",
     "react": "^19.2.0",
-    "react-color": "^2.19.3",
     "react-colorful": "^5.6.1",
     "react-dom": "^19.2.0",
     "react-resizable-panels": "^4.4.0",
@@ -56,7 +55,6 @@
     "@testing-library/user-event": "^14.6.1",
     "@types/node": "^24.10.4",
     "@types/react": "^19.2.5",
-    "@types/react-color": "^3.0.13",
     "@types/react-dom": "^19.2.3",
     "@vitejs/plugin-react": "^5.1.1",
     "@vitest/coverage-v8": "^3.2.4",

+ 19 - 13
frontend/src/components/ui/color-picker.tsx

@@ -1,6 +1,5 @@
 import { useState } from 'react'
-import { SketchPicker } from 'react-color'
-import type { ColorResult } from 'react-color'
+import { HexColorPicker } from 'react-colorful'
 import {
   Popover,
   PopoverContent,
@@ -39,10 +38,6 @@ export function ColorPicker({
 }: ColorPickerProps) {
   const [open, setOpen] = useState(false)
 
-  const handleChange = (color: ColorResult) => {
-    onChange(color.hex)
-  }
-
   return (
     <Popover open={open} onOpenChange={setOpen}>
       <PopoverTrigger asChild>
@@ -57,13 +52,24 @@ export function ColorPicker({
           <span className="sr-only">Pick a color</span>
         </Button>
       </PopoverTrigger>
-      <PopoverContent className="w-auto p-0" align="start">
-        <SketchPicker
-          color={value}
-          onChange={handleChange}
-          presetColors={presets}
-          disableAlpha={true}
-        />
+      <PopoverContent className="w-auto p-3" align="start">
+        <HexColorPicker color={value} onChange={onChange} />
+        <div className="flex flex-wrap gap-1.5 mt-3 max-w-[200px]">
+          {presets.map((preset) => (
+            <button
+              key={preset}
+              type="button"
+              className={cn(
+                'w-6 h-6 rounded-full border-2 cursor-pointer transition-transform hover:scale-110',
+                value.toLowerCase() === preset.toLowerCase()
+                  ? 'border-foreground scale-110'
+                  : 'border-transparent'
+              )}
+              style={{ backgroundColor: preset }}
+              onClick={() => onChange(preset)}
+            />
+          ))}
+        </div>
       </PopoverContent>
     </Popover>
   )

+ 23 - 0
frontend/vite.config.ts

@@ -171,5 +171,28 @@ export default defineConfig({
   build: {
     outDir: '../static/dist',
     emptyOutDir: true,
+    rollupOptions: {
+      output: {
+        manualChunks(id) {
+          if (id.includes('node_modules')) {
+            if (id.includes('react-dom') || id.includes('/react/') || id.includes('react-router')) {
+              return 'vendor-react'
+            }
+            if (id.includes('motion') || id.includes('framer-motion')) {
+              return 'vendor-motion'
+            }
+            if (id.includes('@radix-ui') || id.includes('@floating-ui') || id.includes('react-remove-scroll')) {
+              return 'vendor-radix'
+            }
+            if (id.includes('@dnd-kit')) {
+              return 'vendor-dnd'
+            }
+            if (id.includes('@tanstack') || id.includes('zustand')) {
+              return 'vendor-query'
+            }
+          }
+        },
+      },
+    },
   },
 })

+ 6 - 0
nginx/dune-weaver.conf

@@ -5,6 +5,12 @@ server {
     # Increase max upload size for pattern files
     client_max_body_size 10M;
 
+    # Gzip compression for faster page loads (especially on Pi WiFi)
+    gzip on;
+    gzip_vary on;
+    gzip_min_length 1024;
+    gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
+
     # Frontend - serve static files
     location / {
         root INSTALL_DIR_PLACEHOLDER/static/dist;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/index-BngDXd-M.css


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/index-DvzRKNdC.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/index-bDuzJAeR.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/index-iGL2FzQP.css


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/vendor-dnd-CIuglpGI.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/vendor-motion-BglLd8Or.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/vendor-query-Do4U4P_7.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/vendor-radix-B9aLzj-s.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/assets/vendor-react--zkW7TCA.js


+ 7 - 2
static/dist/index.html

@@ -58,8 +58,13 @@
           .catch(function() {});
       })();
     </script>
-    <script type="module" crossorigin src="/assets/index-bDuzJAeR.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-iGL2FzQP.css">
+    <script type="module" crossorigin src="/assets/index-DvzRKNdC.js"></script>
+    <link rel="modulepreload" crossorigin href="/assets/vendor-radix-B9aLzj-s.js">
+    <link rel="modulepreload" crossorigin href="/assets/vendor-react--zkW7TCA.js">
+    <link rel="modulepreload" crossorigin href="/assets/vendor-query-Do4U4P_7.js">
+    <link rel="modulepreload" crossorigin href="/assets/vendor-dnd-CIuglpGI.js">
+    <link rel="modulepreload" crossorigin href="/assets/vendor-motion-BglLd8Or.js">
+    <link rel="stylesheet" crossorigin href="/assets/index-BngDXd-M.css">
   <script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
   <body>
     <div id="root"></div>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/dist/sw.js


Vissa filer visades inte eftersom för många filer har ändrats