This is an automated email from the ASF dual-hosted git repository.

wangzx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/echarts-theme-builder.git


The following commit(s) were added to refs/heads/master by this push:
     new 18d2cc5  feat: simply support extracting theme colors from an image
     new 0446847  Merge branch 'master' of 
https://github.com/apache/echarts-theme-builder
18d2cc5 is described below

commit 18d2cc5bf47206ed4128bd1277bed60a3c2f6364
Author: plainheart <[email protected]>
AuthorDate: Tue Nov 11 02:01:56 2025 +0800

    feat: simply support extracting theme colors from an image
---
 .vscode/settings.json                |   5 +-
 package-lock.json                    | 819 +++++++++++++++++++++++++++++++++++
 package.json                         |   2 +
 src/components/ChartPreviewPanel.vue |   1 -
 src/components/ThemePanel.vue        | 105 ++++-
 src/locales/en.json                  |   8 +-
 src/locales/zh.json                  |   8 +-
 src/stores/theme.ts                  |   3 -
 8 files changed, 936 insertions(+), 15 deletions(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4e71344..d35cc5a 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,5 +1,6 @@
 {
     "i18n-ally.localesPaths": [
         "src/locales"
-    ]
-}
\ No newline at end of file
+    ],
+    "i18n-ally.keystyle": "nested"
+}
diff --git a/package-lock.json b/package-lock.json
index c69f70e..b7a4cfd 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,12 +8,14 @@
       "name": "echarts-theme-builder",
       "version": "0.0.0",
       "dependencies": {
+        "colorthief": "^2.6.0",
         "vant": "^4.9.21",
         "vue": "^3.5.24",
         "vue-i18n": "^9.14.5",
         "vue3-colorpicker": "^2.3.0"
       },
       "devDependencies": {
+        "@types/colorthief": "^2.6.0",
         "@types/fs-extra": "^11.0.4",
         "@types/node": "^24.10.0",
         "@vitejs/plugin-vue": "^6.0.1",
@@ -79,6 +81,23 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@emnapi/runtime": {
+      "version": "1.7.0",
+      "resolved": 
"https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.0.tgz";,
+      "integrity": 
"sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@emnapi/runtime/node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz";,
+      "integrity": 
"sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD",
+      "optional": true
+    },
     "node_modules/@esbuild/aix-ppc64": {
       "version": "0.25.9",
       "resolved": 
"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz";,
@@ -521,6 +540,367 @@
         "node": ">=18"
       }
     },
+    "node_modules/@img/sharp-darwin-arm64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz";,
+      "integrity": 
"sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-darwin-arm64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-darwin-x64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz";,
+      "integrity": 
"sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-darwin-x64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-libvips-darwin-arm64": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz";,
+      "integrity": 
"sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-darwin-x64": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz";,
+      "integrity": 
"sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-arm": {
+      "version": "1.0.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz";,
+      "integrity": 
"sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-arm64": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz";,
+      "integrity": 
"sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-s390x": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz";,
+      "integrity": 
"sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-linux-x64": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz";,
+      "integrity": 
"sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz";,
+      "integrity": 
"sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz";,
+      "integrity": 
"sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-linux-arm": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz";,
+      "integrity": 
"sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-arm": "1.0.5"
+      }
+    },
+    "node_modules/@img/sharp-linux-arm64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz";,
+      "integrity": 
"sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-arm64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-s390x": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz";,
+      "integrity": 
"sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-s390x": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linux-x64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz";,
+      "integrity": 
"sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linux-x64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linuxmusl-arm64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz";,
+      "integrity": 
"sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-linuxmusl-x64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz";,
+      "integrity": 
"sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+      }
+    },
+    "node_modules/@img/sharp-wasm32": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz";,
+      "integrity": 
"sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+      "cpu": [
+        "wasm32"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+      "optional": true,
+      "dependencies": {
+        "@emnapi/runtime": "^1.2.0"
+      },
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-win32-ia32": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz";,
+      "integrity": 
"sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
+    "node_modules/@img/sharp-win32-x64": {
+      "version": "0.33.5",
+      "resolved": 
"https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz";,
+      "integrity": 
"sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "Apache-2.0 AND LGPL-3.0-or-later",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      }
+    },
     "node_modules/@intlify/core-base": {
       "version": "9.14.5",
       "resolved": 
"https://registry.npmjs.org/@intlify/core-base/-/core-base-9.14.5.tgz";,
@@ -571,6 +951,12 @@
       "integrity": 
"sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
       "license": "MIT"
     },
+    "node_modules/@lokesh.dhakar/quantize": {
+      "version": "1.4.0",
+      "resolved": 
"https://registry.npmjs.org/@lokesh.dhakar/quantize/-/quantize-1.4.0.tgz";,
+      "integrity": 
"sha512-+//cqVWKis//t0YH62EDtwaFSPG/CDtYNg4CZmzNmG2d5W17Iu3fuDAdpQXCDHUDrrU9q0veze4A7tPZXlR/mg==",
+      "license": "MIT"
+    },
     "node_modules/@popperjs/core": {
       "version": "2.11.8",
       "resolved": 
"https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz";,
@@ -868,6 +1254,19 @@
         "win32"
       ]
     },
+    "node_modules/@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": 
"https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz";,
+      "integrity": 
"sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+      "license": "MIT"
+    },
+    "node_modules/@types/colorthief": {
+      "version": "2.6.0",
+      "resolved": 
"https://registry.npmjs.org/@types/colorthief/-/colorthief-2.6.0.tgz";,
+      "integrity": 
"sha512-GZBHPzkgEsyZqO7a9rhqkcW2BJZW+UpRlIUorY8vfOk7BVFVZMuDvZRPyaExv7ML0DaeB/t78PUOTkGo/THDpQ==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.8",
       "resolved": 
"https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz";,
@@ -896,6 +1295,12 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/ndarray": {
+      "version": "1.0.14",
+      "resolved": 
"https://registry.npmjs.org/@types/ndarray/-/ndarray-1.0.14.tgz";,
+      "integrity": 
"sha512-oANmFZMnFQvb219SSBIhI1Ih/r4CvHDOzkWyJS/XRqkMrGH5/kaPSA1hQhdIBzouaE+5KpE/f5ylI9cujmckQg==",
+      "license": "MIT"
+    },
     "node_modules/@types/node": {
       "version": "24.10.0",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz";,
@@ -1210,6 +1615,18 @@
         }
       }
     },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": 
"https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz";,
+      "integrity": 
"sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "license": "MIT",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
     "node_modules/alien-signals": {
       "version": "3.0.6",
       "resolved": 
"https://registry.npmjs.org/alien-signals/-/alien-signals-3.0.6.tgz";,
@@ -1244,6 +1661,26 @@
         "url": "https://github.com/sponsors/jonschlinkert";
       }
     },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz";,
+      "integrity": 
"sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross";
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross";
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support";
+        }
+      ],
+      "license": "MIT"
+    },
     "node_modules/binary-extensions": {
       "version": "2.3.0",
       "resolved": 
"https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz";,
@@ -1270,6 +1707,30 @@
         "node": ">=8"
       }
     },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz";,
+      "integrity": 
"sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross";
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross";
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support";
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
     "node_modules/chokidar": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz";,
@@ -1295,6 +1756,59 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/color": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz";,
+      "integrity": 
"sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1",
+        "color-string": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=12.5.0"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": 
"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz";,
+      "integrity": 
"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": 
"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz";,
+      "integrity": 
"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "license": "MIT"
+    },
+    "node_modules/color-string": {
+      "version": "1.9.1",
+      "resolved": 
"https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz";,
+      "integrity": 
"sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "^1.0.0",
+        "simple-swizzle": "^0.2.2"
+      }
+    },
+    "node_modules/colorthief": {
+      "version": "2.6.0",
+      "resolved": 
"https://registry.npmjs.org/colorthief/-/colorthief-2.6.0.tgz";,
+      "integrity": 
"sha512-yL3B7laeOr4kH9XasFF5rl+9Taz+Pmt/CRbaTI6XepZFyQvk4K/abaGKIAsngVpxKkgFeoJ2IwdRpS228icrig==",
+      "license": "MIT",
+      "dependencies": {
+        "@lokesh.dhakar/quantize": "^1.4.0",
+        "file-type": "^16.5.3",
+        "ndarray-pixels": "^4.1.0",
+        "sharp": "^0.33.5"
+      }
+    },
     "node_modules/copy-dir": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/copy-dir/-/copy-dir-1.3.0.tgz";,
@@ -1308,6 +1822,24 @@
       "integrity": 
"sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
       "license": "MIT"
     },
+    "node_modules/cwise-compiler": {
+      "version": "1.1.3",
+      "resolved": 
"https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz";,
+      "integrity": 
"sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==",
+      "license": "MIT",
+      "dependencies": {
+        "uniq": "^1.0.0"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": 
"https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz";,
+      "integrity": 
"sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/echarts": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz";,
@@ -1379,6 +1911,24 @@
       "integrity": 
"sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
       "license": "MIT"
     },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": 
"https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz";,
+      "integrity": 
"sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz";,
+      "integrity": 
"sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
     "node_modules/fdir": {
       "version": "6.5.0",
       "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz";,
@@ -1397,6 +1947,23 @@
         }
       }
     },
+    "node_modules/file-type": {
+      "version": "16.5.4",
+      "resolved": 
"https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz";,
+      "integrity": 
"sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+      "license": "MIT",
+      "dependencies": {
+        "readable-web-to-node-stream": "^3.0.0",
+        "strtok3": "^6.2.4",
+        "token-types": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/file-type?sponsor=1";
+      }
+    },
     "node_modules/fill-range": {
       "version": "7.1.1",
       "resolved": 
"https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz";,
@@ -1468,6 +2035,38 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz";,
+      "integrity": 
"sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross";
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross";
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support";
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/iota-array": {
+      "version": "1.0.0",
+      "resolved": 
"https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz";,
+      "integrity": 
"sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==",
+      "license": "MIT"
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.3.4",
+      "resolved": 
"https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz";,
+      "integrity": 
"sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
+      "license": "MIT"
+    },
     "node_modules/is-binary-path": {
       "version": "2.1.0",
       "resolved": 
"https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz";,
@@ -1481,6 +2080,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz";,
+      "integrity": 
"sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "license": "MIT"
+    },
     "node_modules/is-extglob": {
       "version": "2.1.1",
       "resolved": 
"https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz";,
@@ -1576,6 +2181,37 @@
         "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
       }
     },
+    "node_modules/ndarray": {
+      "version": "1.0.19",
+      "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz";,
+      "integrity": 
"sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==",
+      "license": "MIT",
+      "dependencies": {
+        "iota-array": "^1.0.0",
+        "is-buffer": "^1.0.2"
+      }
+    },
+    "node_modules/ndarray-ops": {
+      "version": "1.2.2",
+      "resolved": 
"https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz";,
+      "integrity": 
"sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==",
+      "license": "MIT",
+      "dependencies": {
+        "cwise-compiler": "^1.0.0"
+      }
+    },
+    "node_modules/ndarray-pixels": {
+      "version": "4.1.0",
+      "resolved": 
"https://registry.npmjs.org/ndarray-pixels/-/ndarray-pixels-4.1.0.tgz";,
+      "integrity": 
"sha512-xKPI4zXJ2pkUcVX24zIN1AWqqPWvRWWhRuO6PlY4EdB2VNRauNwA6rDdsAQG/ldQp0sU7nTXgPR/io1duy3Zyg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/ndarray": "^1.0.14",
+        "ndarray": "^1.0.19",
+        "ndarray-ops": "^1.2.2",
+        "sharp": "^0.33.4"
+      }
+    },
     "node_modules/normalize-path": {
       "version": "3.0.0",
       "resolved": 
"https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz";,
@@ -1606,6 +2242,19 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/peek-readable": {
+      "version": "4.1.0",
+      "resolved": 
"https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz";,
+      "integrity": 
"sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit";
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.1.1",
       "resolved": 
"https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz";,
@@ -1653,6 +2302,47 @@
         "node": "^10 || ^12 || >=14"
       }
     },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz";,
+      "integrity": 
"sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "4.7.0",
+      "resolved": 
"https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz";,
+      "integrity": 
"sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+      "license": "MIT",
+      "dependencies": {
+        "abort-controller": "^3.0.0",
+        "buffer": "^6.0.3",
+        "events": "^3.3.0",
+        "process": "^0.11.10",
+        "string_decoder": "^1.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/readable-web-to-node-stream": {
+      "version": "3.0.4",
+      "resolved": 
"https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz";,
+      "integrity": 
"sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==",
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^4.7.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit";
+      }
+    },
     "node_modules/readdirp": {
       "version": "3.6.0",
       "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz";,
@@ -1719,6 +2409,86 @@
         "fsevents": "~2.3.2"
       }
     },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": 
"https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz";,
+      "integrity": 
"sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross";
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross";
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support";
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/semver": {
+      "version": "7.7.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz";,
+      "integrity": 
"sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/sharp": {
+      "version": "0.33.5",
+      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz";,
+      "integrity": 
"sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "color": "^4.2.3",
+        "detect-libc": "^2.0.3",
+        "semver": "^7.6.3"
+      },
+      "engines": {
+        "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/libvips";
+      },
+      "optionalDependencies": {
+        "@img/sharp-darwin-arm64": "0.33.5",
+        "@img/sharp-darwin-x64": "0.33.5",
+        "@img/sharp-libvips-darwin-arm64": "1.0.4",
+        "@img/sharp-libvips-darwin-x64": "1.0.4",
+        "@img/sharp-libvips-linux-arm": "1.0.5",
+        "@img/sharp-libvips-linux-arm64": "1.0.4",
+        "@img/sharp-libvips-linux-s390x": "1.0.4",
+        "@img/sharp-libvips-linux-x64": "1.0.4",
+        "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
+        "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
+        "@img/sharp-linux-arm": "0.33.5",
+        "@img/sharp-linux-arm64": "0.33.5",
+        "@img/sharp-linux-s390x": "0.33.5",
+        "@img/sharp-linux-x64": "0.33.5",
+        "@img/sharp-linuxmusl-arm64": "0.33.5",
+        "@img/sharp-linuxmusl-x64": "0.33.5",
+        "@img/sharp-wasm32": "0.33.5",
+        "@img/sharp-win32-ia32": "0.33.5",
+        "@img/sharp-win32-x64": "0.33.5"
+      }
+    },
+    "node_modules/simple-swizzle": {
+      "version": "0.2.4",
+      "resolved": 
"https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz";,
+      "integrity": 
"sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.3.1"
+      }
+    },
     "node_modules/source-map-js": {
       "version": "1.2.1",
       "resolved": 
"https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz";,
@@ -1728,6 +2498,32 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": 
"https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz";,
+      "integrity": 
"sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/strtok3": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz";,
+      "integrity": 
"sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+      "license": "MIT",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "peek-readable": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit";
+      }
+    },
     "node_modules/tinycolor2": {
       "version": "1.6.0",
       "resolved": 
"https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz";,
@@ -1764,6 +2560,23 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/token-types": {
+      "version": "4.2.1",
+      "resolved": 
"https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz";,
+      "integrity": 
"sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit";
+      }
+    },
     "node_modules/tslib": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz";,
@@ -1793,6 +2606,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/uniq": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz";,
+      "integrity": 
"sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==",
+      "license": "MIT"
+    },
     "node_modules/universalify": {
       "version": "2.0.1",
       "resolved": 
"https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz";,
diff --git a/package.json b/package.json
index 8af3dce..623d1c4 100644
--- a/package.json
+++ b/package.json
@@ -11,12 +11,14 @@
     "release": "vue-tsc -b && vite build --mode app && node scripts/release.js"
   },
   "dependencies": {
+    "colorthief": "^2.6.0",
     "vant": "^4.9.21",
     "vue": "^3.5.24",
     "vue-i18n": "^9.14.5",
     "vue3-colorpicker": "^2.3.0"
   },
   "devDependencies": {
+    "@types/colorthief": "^2.6.0",
     "@types/fs-extra": "^11.0.4",
     "@types/node": "^24.10.0",
     "@vitejs/plugin-vue": "^6.0.1",
diff --git a/src/components/ChartPreviewPanel.vue 
b/src/components/ChartPreviewPanel.vue
index 588b2d3..ffd685d 100644
--- a/src/components/ChartPreviewPanel.vue
+++ b/src/components/ChartPreviewPanel.vue
@@ -7,7 +7,6 @@
     <div class="charts-grid">
       <div
         v-for="(config, index) in displayedCharts"
-        v-once
         :key="config.type + index"
         class="chart-item"
       >
diff --git a/src/components/ThemePanel.vue b/src/components/ThemePanel.vue
index d69c55e..2dc8285 100644
--- a/src/components/ThemePanel.vue
+++ b/src/components/ThemePanel.vue
@@ -80,6 +80,14 @@
                   :style="{ backgroundColor: color }"
                 />
               </div>
+              <div
+                class="theme-item new-theme"
+                :title="$t('panel.extractFromImageTitle')"
+                @click="uploadImageInputRef?.click()"
+              >
+                <van-icon name="plus" />
+                <span>{{ $t('panel.extractFromImage') }}</span>
+              </div>
             </div>
           </div>
         </div>
@@ -457,12 +465,20 @@
 
     <!-- Hidden file input for import -->
     <input
-      ref="fileInput"
+      ref="importFileInput"
       type="file"
       accept=".json"
       style="display: none"
       @change="handleFileImport"
     />
+
+    <input
+      ref="uploadImageInput"
+      type="file"
+      accept="image/jpeg, image/png, image/webp, image/avif, image/apng, 
image/svg+xml"
+      style="display: none"
+      @change="handleImageUpload"
+    />
   </div>
 </template>
 
@@ -474,9 +490,10 @@ import ColorPicker from './ColorPicker.vue'
 import ColorList from './ColorList.vue'
 import type ChartPreviewPanel from './ChartPreviewPanel.vue'
 import { downloadJsonFile, downloadJsFile } from '../utils/download'
-import { showToast, showDialog } from 'vant'
+import { showToast, showDialog, showLoadingToast } from 'vant'
 import { useI18n } from 'vue-i18n'
 import * as echarts from 'echarts'
+import ColorThief from 'colorthief'
 
 // Initialize i18n and localization
 const { t } = useI18n()
@@ -489,7 +506,8 @@ const props = defineProps<Props>()
 
 // Component state
 const activeNames = ref(['functions'])
-const fileInputRef = useTemplateRef('fileInput')
+const importFileInputRef = useTemplateRef('importFileInput')
+const uploadImageInputRef = useTemplateRef('uploadImageInput')
 
 // Theme store
 const themeStore = useThemeStore()
@@ -750,7 +768,7 @@ const showUsageInstructions = (format: 'js' | 'json', 
filename: string) => {
 }
 
 const importConfig = () => {
-  fileInputRef.value?.click()
+  importFileInputRef.value?.click()
 }
 
 const exportConfig = async () => {
@@ -800,7 +818,6 @@ const resetTheme = async () => {
   }
 }
 
-
 const showHelp = () => {
   showDialog({
     title: t('modals.helpTitle'),
@@ -927,6 +944,62 @@ const handleFileImport = async (event: Event) => {
   // Clear input
   target.value = ''
 }
+
+const handleImageUpload = async (event: Event) => {
+  const target = event.target as HTMLInputElement
+  const file = target.files?.[0]
+  if (!file) return
+
+  const loader = showLoadingToast({
+    message: t('modals.extractingColors'),
+    duration: 0,
+    forbidClick: true,
+    loadingType: 'spinner',
+    wordBreak: 'break-word',
+    className: 'ec-loading-toast'
+  })
+
+  let imgURL: string | undefined;
+  try {
+    imgURL = URL.createObjectURL(file)
+
+    const img = new Image()
+    img.src = imgURL
+
+    await new Promise((resolve, reject) => {
+      img.onload = resolve
+      img.onerror = (e) => {
+        console.error('Failed to load image')
+        reject(e)
+      }
+    })
+
+    const colorThief = new ColorThief()
+    const colors = colorThief.getPalette(img, 10, 1).map(
+      (rgb) => `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`
+    )
+
+    loader.close()
+
+    themeName.value = 'image-based'
+    theme.color = colors
+    // PENDING
+    theme.visualMapColor = colors.slice(0, 2)
+
+    props.chartPreviewRef?.updateCharts()
+
+    themeStore.activePreDefinedThemeIndex.value = null
+  } catch (e) {
+    console.error('Failed to extract image color:', e)
+    showToast({
+      message: t('modals.extractColorFailed'),
+      type: 'fail'
+    })
+  } finally {
+    imgURL && URL.revokeObjectURL(imgURL)
+    target.value = ''
+  }
+}
 </script>
 
 <style scoped>
@@ -992,6 +1065,18 @@ const handleFileImport = async (event: Event) => {
   box-shadow: 0 0 4px var(--van-primary-color);
 }
 
+.new-theme {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 5px;
+  height: auto;
+  font-size: 85%;
+  line-height: 1;
+  white-space: nowrap;
+  cursor: pointer;
+}
+
 .color-dot {
   width: 20px;
   height: 20px;
@@ -1014,6 +1099,12 @@ const handleFileImport = async (event: Event) => {
   color: rgb(41, 60, 85);
 }
 
+@media (max-width: 1200px) {
+  .new-theme span {
+    display: none;
+  }
+}
+
 /* Custom Vant styles */
 :deep(.van-collapse) {
   border: none;
@@ -1176,4 +1267,8 @@ const handleFileImport = async (event: Event) => {
 :global(.modal-body a:hover) {
   text-decoration: underline;
 }
+
+:global(.ec-loading-toast) {
+  width: 200px;
+}
 </style>
diff --git a/src/locales/en.json b/src/locales/en.json
index 8a86800..45f822b 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -36,7 +36,9 @@
     "seriesCount": "Series",
     "seriesPlaceholder": "Please enter series count",
     "preDefinedThemes": "Predefined Themes",
-    "separateAxisSetting": "Configure different axis types separately"
+    "separateAxisSetting": "Configure different axis types separately",
+    "extractFromImage": "From Image",
+    "extractFromImageTitle": "Extract theme colors from an image"
   },
   "colors": {
     "background": "Background",
@@ -182,6 +184,8 @@
     "oldVersionPrompt": "The imported theme is from an older version, some 
properties may not be correctly set. Continue importing?",
     "importSuccess": "Theme imported successfully!",
     "invalidFormat": "Configuration file format error, please use a JSON file 
exported from this site!",
-    "fileReadFailed": "File read failed, please try again"
+    "fileReadFailed": "File read failed, please try again",
+    "extractingColors": "Please wait when extracting colors from image...",
+    "extractColorFailed": "Failed to extract colors from image"
   }
 }
diff --git a/src/locales/zh.json b/src/locales/zh.json
index 0132c1b..fb6daae 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -36,7 +36,9 @@
     "seriesCount": "系列数量",
     "seriesPlaceholder": "请输入系列数量",
     "preDefinedThemes": "默认方案",
-    "separateAxisSetting": "为不同类型坐标轴分别设置"
+    "separateAxisSetting": "为不同类型坐标轴分别设置",
+    "extractFromImage": "从图片提取",
+    "extractFromImageTitle": "上传一张图片并从中提取颜色"
   },
   "colors": {
     "background": "背景",
@@ -182,6 +184,8 @@
     "oldVersionPrompt": "导入的主题版本较低,某些属性可能无法正确设置。是否继续导入?",
     "importSuccess": "主题导入成功!",
     "invalidFormat": "配置文件格式错误,请使用从本网站导出的 JSON 文件!",
-    "fileReadFailed": "文件读取失败,请重试"
+    "fileReadFailed": "文件读取失败,请重试",
+    "extractingColors": "正在从图片中提取颜色...",
+    "extractColorFailed": "从图片提取颜色失败"
   }
 }
diff --git a/src/stores/theme.ts b/src/stores/theme.ts
index 05134dd..be26e92 100644
--- a/src/stores/theme.ts
+++ b/src/stores/theme.ts
@@ -284,9 +284,6 @@ const createThemeStore = () => {
 
         // Update axis settings based on axisSeperateSetting
         updateAxisSetting()
-
-        // Force trigger reactive update by modifying a dummy property
-        ;(theme as any).__forceUpdate = Date.now()
       }
     } catch (error) {
       console.error('Error loading predefined theme:', error)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to