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

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

commit ccd9161dd0557422de3ddecc3c9c1562aaa96f64
Author: Ovilia <[email protected]>
AuthorDate: Thu Sep 25 16:31:21 2025 +0800

    chore: update for release
---
 en/body.html                         |   2 +
 package.json                         |   2 +-
 scripts/release.js                   |  62 +++++++++
 src/App.vue                          |   3 +-
 src/components/ChartPreviewPanel.vue |   4 +-
 src/components/ThemePanel.vue        |   5 +-
 src/entries/en.ts                    |  36 ++++++
 src/entries/zh.ts                    |  36 ++++++
 src/stores/theme.ts                  |   4 +-
 src/utils/chartConfigs.ts            |   3 +-
 src/utils/themeGenerator.ts          |  14 +--
 vite.config.ts                       | 237 +++++++++++++++++------------------
 zh/body.html                         |   2 +
 13 files changed, 268 insertions(+), 142 deletions(-)

diff --git a/en/body.html b/en/body.html
new file mode 100644
index 0000000..1db2bb3
--- /dev/null
+++ b/en/body.html
@@ -0,0 +1,2 @@
+<div id="theme-builder"></div>
+<script type="module" src="../src/entries/en.ts"></script>
diff --git a/package.json b/package.json
index f077d96..ffa6e74 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
     "build": "vue-tsc -b && vite build",
     "preview": "http-server app",
     "serve": "http-server app",
-    "release": "RELEASE=true vue-tsc -b && RELEASE=true vite build"
+    "release": "npm run build && node scripts/release.js"
   },
   "dependencies": {
     "echarts": "^6.0.0",
diff --git a/scripts/release.js b/scripts/release.js
new file mode 100644
index 0000000..83ad7c0
--- /dev/null
+++ b/scripts/release.js
@@ -0,0 +1,62 @@
+import fs from 'fs-extra';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import config from '../config/env.asf.js';
+
+// Get __dirname equivalent in ESM
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const appDir = path.resolve(__dirname, '../app');
+const releaseDestDir = config.releaseDestDir;
+const ecWWWGeneratedDir = config.ecWWWGeneratedDir;
+
+console.log('Releasing theme builder...');
+
+// Make sure directories exist
+fs.ensureDirSync(path.join(ecWWWGeneratedDir, 'en', 'theme-builder'));
+fs.ensureDirSync(path.join(ecWWWGeneratedDir, 'zh', 'theme-builder'));
+fs.ensureDirSync(path.join(releaseDestDir, 'en', 'theme-builder'));
+fs.ensureDirSync(path.join(releaseDestDir, 'zh', 'theme-builder'));
+
+// Clean up existing JS and CSS files in the releaseDestDir
+console.log('Cleaning up old CSS and JS files...');
+['en', 'zh'].forEach(lang => {
+  const themeBuilderDir = path.join(releaseDestDir, lang, 'theme-builder');
+  if (fs.existsSync(themeBuilderDir)) {
+    const files = fs.readdirSync(themeBuilderDir);
+    files.forEach(file => {
+      if (file.endsWith('.js') || file.endsWith('.css')) {
+        const filePath = path.join(themeBuilderDir, file);
+        console.log(`Removing: ${filePath}`);
+        fs.removeSync(filePath);
+      }
+    });
+  }
+});
+
+// Move body.html files
+console.log('Moving body.html files to echarts-www...');
+fs.copySync(
+    path.join(appDir, 'en', 'body.html'),
+    path.join(ecWWWGeneratedDir, 'en', 'theme-builder', 'body.html')
+);
+fs.copySync(
+    path.join(appDir, 'zh', 'body.html'),
+    path.join(ecWWWGeneratedDir, 'zh', 'theme-builder', 'body.html')
+);
+
+// Move theme-builder files to website
+console.log('Moving theme-builder files to echarts-website...');
+fs.copySync(
+    path.join(appDir, 'en', 'theme-builder'),
+    path.join(releaseDestDir, 'en', 'theme-builder'),
+    { overwrite: true }
+);
+fs.copySync(
+    path.join(appDir, 'zh', 'theme-builder'),
+    path.join(releaseDestDir, 'zh', 'theme-builder'),
+    { overwrite: true }
+);
+
+console.log('Release completed successfully!');
diff --git a/src/App.vue b/src/App.vue
index 2e38523..3867abf 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -11,7 +11,8 @@ const { switchLanguage, currentLanguage, 
getAvailableLanguages } = useLocalizati
 const currentLang = ref(currentLanguage)
 const availableLocales = getAvailableLanguages()
 // Only show language selector in dev/preview mode
-const showLanguageSelector = availableLocales.length > 0
+// Use import.meta.env.DEV to only show in development mode
+const showLanguageSelector = import.meta.env.DEV && availableLocales.length > 0
 
 const onLanguageChange = (lang: string) => {
   switchLanguage(lang)
diff --git a/src/components/ChartPreviewPanel.vue 
b/src/components/ChartPreviewPanel.vue
index 37e0919..e68123e 100644
--- a/src/components/ChartPreviewPanel.vue
+++ b/src/components/ChartPreviewPanel.vue
@@ -65,7 +65,7 @@ function setChartRef(el: any, index: number) {
 
 // Register and apply current theme
 function registerCurrentTheme() {
-  const currentTheme = themeStore.getEChartsTheme(false)
+  const currentTheme = themeStore.getEChartsTheme()
   echarts.registerTheme('customized', currentTheme)
 }
 
@@ -110,7 +110,7 @@ function _updateChartsImpl() {
   }
 
   // Get current theme and register with unique ID to force refresh
-  const currentTheme = themeStore.getEChartsTheme(false)
+  const currentTheme = themeStore.getEChartsTheme()
   const themeId = `customized-${Date.now()}`
   echarts.registerTheme(themeId, currentTheme)
 
diff --git a/src/components/ThemePanel.vue b/src/components/ThemePanel.vue
index bd360df..2e226bc 100644
--- a/src/components/ThemePanel.vue
+++ b/src/components/ThemePanel.vue
@@ -670,7 +670,7 @@ const validateGridValue = (position: 'left' | 'right' | 
'top' | 'bottom') => {
 // Methods
 const downloadTheme = async () => {
   try {
-    const themeConfig = themeStore.getEChartsTheme(true)
+    const themeConfig = themeStore.getEChartsTheme()
     const jsContent = themeStore.getThemeJsFile()
     const filename = themeName.value || 'customized'
 
@@ -953,7 +953,8 @@ const handleFileImport = async (event: Event) => {
   flex-wrap: wrap;
   justify-content: space-between;
   width: auto;
-  height: 22px;
+  box-sizing: border-box;
+  height: 32px;
   margin-bottom: 5px;
   overflow: hidden;
   border: 1px solid #eee;
diff --git a/src/entries/en.ts b/src/entries/en.ts
new file mode 100644
index 0000000..ca3adfd
--- /dev/null
+++ b/src/entries/en.ts
@@ -0,0 +1,36 @@
+// English Entry Point
+import { createApp } from 'vue'
+import {
+  Col,
+  Row,
+  Collapse,
+  CollapseItem,
+  Field,
+  Button,
+  Icon,
+  Checkbox,
+  RadioGroup,
+  Radio
+} from 'vant'
+import 'vant/lib/index.css'
+import '../style.css'
+import App from '../App.vue'
+import i18n from '../i18n'
+import { setLocale } from '../i18n'
+
+// Force English locale
+setLocale('en')
+
+const app = createApp(App)
+app.use(i18n)
+app.use(Col)
+app.use(Row)
+app.use(Collapse)
+app.use(CollapseItem)
+app.use(Field)
+app.use(Button)
+app.use(Icon)
+app.use(Checkbox)
+app.use(RadioGroup)
+app.use(Radio)
+app.mount('#theme-builder')
diff --git a/src/entries/zh.ts b/src/entries/zh.ts
new file mode 100644
index 0000000..ae51117
--- /dev/null
+++ b/src/entries/zh.ts
@@ -0,0 +1,36 @@
+// Chinese Entry Point
+import { createApp } from 'vue'
+import {
+  Col,
+  Row,
+  Collapse,
+  CollapseItem,
+  Field,
+  Button,
+  Icon,
+  Checkbox,
+  RadioGroup,
+  Radio
+} from 'vant'
+import 'vant/lib/index.css'
+import '../style.css'
+import App from '../App.vue'
+import i18n from '../i18n'
+import { setLocale } from '../i18n'
+
+// Force Chinese locale
+setLocale('zh')
+
+const app = createApp(App)
+app.use(i18n)
+app.use(Col)
+app.use(Row)
+app.use(Collapse)
+app.use(CollapseItem)
+app.use(Field)
+app.use(Button)
+app.use(Icon)
+app.use(Checkbox)
+app.use(RadioGroup)
+app.use(Radio)
+app.mount('#theme-builder')
diff --git a/src/stores/theme.ts b/src/stores/theme.ts
index 4545de9..97e1550 100644
--- a/src/stores/theme.ts
+++ b/src/stores/theme.ts
@@ -315,10 +315,10 @@ const createThemeStore = () => {
     return cleanedData
   }
 
-  const getEChartsTheme = (isToExport: boolean = false) => {
+  const getEChartsTheme = () => {
     // Convert reactive object to plain object to ensure proper data passing
     const plainTheme = JSON.parse(JSON.stringify(theme))
-    return generateEChartsTheme(plainTheme, isToExport)
+    return generateEChartsTheme(plainTheme)
   }
 
   const getThemeJsFile = () => {
diff --git a/src/utils/chartConfigs.ts b/src/utils/chartConfigs.ts
index 643c43a..0360c93 100644
--- a/src/utils/chartConfigs.ts
+++ b/src/utils/chartConfigs.ts
@@ -430,7 +430,8 @@ export function getChartConfigs(seriesCnt: number = 4): 
ChartConfig[] {
           layout: 'force',
           roam: true,
           label: {
-            show: true
+            show: true,
+            color: 'auto'
           },
           force: {
             repulsion: 400,
diff --git a/src/utils/themeGenerator.ts b/src/utils/themeGenerator.ts
index 6ef822d..8b162c5 100644
--- a/src/utils/themeGenerator.ts
+++ b/src/utils/themeGenerator.ts
@@ -3,13 +3,9 @@ import type { ThemeData } from '../types/theme'
 /**
  * Generate ECharts theme configuration based on theme data
  * @param themeData - Theme configuration data
- * @param isToExport - Reserved for backward compatibility (no longer used)
  * @returns ECharts theme configuration object
  */
-export function generateEChartsTheme(themeData: ThemeData, isToExport: boolean 
= false) {
-  // Halloween pumpkin symbol path
-  const pumpkin = 
'path://M237.062,81.761L237.062,81.761c-12.144-14.24-25.701-20.1-40.68-19.072 
c-10.843,0.747-20.938,5.154-30.257,13.127c-9.51-5.843-19.8-9.227-30.859-10.366c0.521-3.197,1.46-6.306,2.85-9.363
 
c3.458-7.038,8.907-12.741,16.331-17.296c-5.609-3.384-11.227-6.799-16.854-10.279c-16.257,8.104-25.06,20.601-26.463,38.417
 
c-7.599,1.705-14.685,4.486-21.247,8.437c-9.164-7.677-18.996-11.917-29.496-12.632c-14.819-0.998-28.467,4.787-40.938,18.827
 C6.445,96.182,0,114.867,0,136.242c-0.007 [...]
-
+export function generateEChartsTheme(themeData: ThemeData) {
   // Generate series style configuration
   const seriesStyle = {
     itemStyle: {
@@ -33,12 +29,6 @@ export function generateEChartsTheme(themeData: ThemeData, 
isToExport: boolean =
     itemStyle: itemStyle
   } as any
 
-  // Special case for Halloween theme
-  if (themeData.symbol === 'halloween') {
-    seriesStyle.symbol = pumpkin
-    border.symbol = pumpkin
-  }
-
   // Generate map configuration
   const mapConfig = {
     itemStyle: {
@@ -271,7 +261,7 @@ export function generateEChartsTheme(themeData: ThemeData, 
isToExport: boolean =
  * @returns JavaScript file content as string
  */
 export function generateThemeJsFile(themeData: ThemeData, themeName: string): 
string {
-  const themeConfig = generateEChartsTheme(themeData, true)
+  const themeConfig = generateEChartsTheme(themeData)
 
   // Format theme with 4 spaces indentation
   let themeJson = JSON.stringify(themeConfig, null, '    ')
diff --git a/vite.config.ts b/vite.config.ts
index 7159e53..23acac6 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,150 +1,145 @@
-import { defineConfig } from 'vite'
+import { defineConfig, type Plugin } from 'vite'
 import vue from '@vitejs/plugin-vue'
 import path from 'path'
 import fs from 'fs'
 import fse from 'fs-extra'
 
-/**
- * Load release config from env.asf.js
- */
-function loadReleaseConfig() {
-  const configModule = require('./config/env.asf.js')
-  return configModule.default || configModule
-}
-
-// Check if release mode is enabled with RELEASE=true environment variable
-const isRelease = process.env.RELEASE === 'true'
-
-// https://vite.dev/config/
-export default defineConfig({
-  define: {
-    // 设置环境变量,在前端代码中可以通过import.meta.env访问
-    'import.meta.env.VITE_MODE': JSON.stringify(isRelease ? 'release' : 
'development')
-  },
-  plugins: [
-    vue(),
-    {
-      name: 'theme-builder-processor',
-      closeBundle: async () => {
-        // Handle HTML files - Create language-specific index.html files
-        if (fs.existsSync('app/index.html')) {
-          console.log('Processing HTML output...')
-
-          // Create directories if they don't exist
-          fs.mkdirSync('app/en', { recursive: true })
-          fs.mkdirSync('app/zh', { recursive: true })
-          fs.mkdirSync('app/en/theme-builder', { recursive: true })
-          fs.mkdirSync('app/zh/theme-builder', { recursive: true })
-
-          // Generate HTML content - just div + script tag
-          const divContent = '<div id="theme-builder"></div>'
-          const scriptTag = '<script type="module" 
src="./theme-builder/app.min.js"></script>'
-
-          // Create simplified index.html for both languages
-          const indexHTML = `${divContent}\n${scriptTag}`
-
-          // Write index.html files (only these, no body.html)
-          fs.writeFileSync('app/en/index.html', indexHTML, 'utf-8')
-          fs.writeFileSync('app/zh/index.html', indexHTML, 'utf-8')
-
-          // Remove the original index.html
-          fs.unlinkSync('app/index.html')
+const SUPPORTED_LANGUAGES: string[] = ['en', 'zh'];
+const OUTPUT_DIR: string = 'app';
+
+// Custom plugin to handle HTML output for different languages
+function createHtmlOutputPlugin(): Plugin {
+  return {
+    name: 'html-output-plugin',
+    enforce: 'post',
+    apply: 'build',
+    closeBundle: async () => {
+      const distDir = path.resolve(__dirname, 'dist');
+      const publicDir = path.resolve(__dirname, 'public');
+
+      // Create language directories if they don't exist
+      for (const lang of SUPPORTED_LANGUAGES) {
+        const langDir = path.join(OUTPUT_DIR, lang);
+        const assetDir = path.join(langDir, 'theme-builder');
+        const themesDir = path.join(assetDir, 'themes');
+
+        // Ensure directories exist and clean any existing assets
+        await fse.ensureDir(langDir);
+        await fse.ensureDir(assetDir);
+
+        // Clean up existing JS and CSS files before copying new ones
+        const existingFiles = await fse.readdir(assetDir);
+        for (const file of existingFiles) {
+          if (file.endsWith('.js') || file.endsWith('.css')) {
+            await fse.remove(path.join(assetDir, file));
+          }
         }
 
-        // Move CSS to shared styles directory
-        console.log('Processing CSS and shared resources...')
-        fs.mkdirSync('app/styles', { recursive: true })
+        await fse.ensureDir(themesDir);
 
-        if (fs.existsSync('app/styles/main.css')) {
-          // CSS is already in the correct location from the build output
-          console.log('CSS output already in correct location')
-        }
+        // Find JS and CSS files in the dist directory
+        const files = await fse.readdir(distDir);
+        const jsFiles = files.filter(file => file.endsWith('.js'));
+        const cssFiles = files.filter(file => file.endsWith('.css'));
 
-        // Copy theme files from public to app/themes (common resource)
-        const themesDir = 'public/themes'
-        const themesDestination = 'app/themes'
+        // Read the HTML file to extract scripts and links to include in 
body.html
+        if (fs.existsSync(path.join(distDir, 'index.html'))) {
+          const htmlContent = await fse.readFile(path.join(distDir, 
'index.html'), 'utf-8');
 
-        if (fs.existsSync(themesDir)) {
-          fs.mkdirSync(themesDestination, { recursive: true })
+          // Extract only the content within the body tag
+          let bodyContent = 
htmlContent.match(/<body[^>]*>([\s\S]*)<\/body>/i)?.[1] || '';
 
-          // Copy theme JSON files
-          fs.readdirSync(themesDir).forEach((file) => {
-            if (file.endsWith('.json')) {
-              fs.copyFileSync(`${themesDir}/${file}`, 
`${themesDestination}/${file}`)
-            }
-          })
-        }
+          // Get all script and link tags from head
+          const headScripts = 
(htmlContent.match(/<script[^>]*src="\.\/([^"]+)"[^>]*><\/script>/g) || [])
+            .map(script => script.replace(/src="\.\//g, 
'src="theme-builder/'));
 
-        // Handle release mode - copy files from app to ecWWWGeneratedDir
-        if (isRelease) {
-          console.log('Starting release process...')
-          const config = loadReleaseConfig()
+          const headLinks = 
(htmlContent.match(/<link[^>]*href="\.\/([^"]+)"[^>]*>/g) || [])
+            .map(link => link.replace(/href="\.\//g, 'href="theme-builder/'));
 
-          // Validate target directories
-          if (!config.ecWWWGeneratedDir) {
-            console.error('Error: ecWWWGeneratedDir not defined in config')
-            return
-          }
+          // Create a complete body.html with necessary script and link tags
+          let finalContent = [...headLinks, ...headScripts, 
bodyContent.trim()].join('\n');
 
-          const ecWWWBaseDir = config.ecWWWGeneratedDir.replace('_generated', 
'')
-          if (!fs.existsSync(ecWWWBaseDir)) {
-            console.error(`Error: ECharts www project not found: 
${ecWWWBaseDir}`)
-            return
-          }
+          // Replace any references to themes in the content
+          finalContent = finalContent.replace(/(['"])\.?\/themes\//g, 
'$1./theme-builder/themes/');
 
-          // Create destination directory if needed
-          fse.ensureDirSync(config.ecWWWGeneratedDir)
+          // Write the content to body.html
+          await fse.writeFile(path.join(langDir, 'body.html'), finalContent);
+        }
 
-          // Copy the entire app directory to ecWWWGeneratedDir
-          console.log(`Copying app contents to ${config.ecWWWGeneratedDir}`)
+        // Copy JS and CSS files to the theme-builder directory
+        for (const jsFile of jsFiles) {
+          await fse.copy(
+            path.join(distDir, jsFile),
+            path.join(assetDir, jsFile)
+          );
+        }
 
-          // Copy app directory to ecWWWGeneratedDir
-          fse.copySync('app', config.ecWWWGeneratedDir)
+        for (const cssFile of cssFiles) {
+          await fse.copy(
+            path.join(distDir, cssFile),
+            path.join(assetDir, cssFile)
+          );
+        }
 
-          console.log('Release process completed successfully!')
+        // Copy themes from public directory
+        if (fs.existsSync(path.join(publicDir, 'themes'))) {
+          await fse.copy(
+            path.join(publicDir, 'themes'),
+            themesDir
+          );
         }
       }
+
+      // Clean up the original dist directory
+      await fse.remove(distDir);
+    }
+  };
+}
+
+// Custom plugin to rewrite theme paths in JS files
+function createThemePathRewritePlugin(): Plugin {
+  return {
+    name: 'theme-path-rewrite',
+    transform(code, id) {
+      if (id.endsWith('.js') || id.endsWith('.ts')) {
+        // Replace theme path references
+        return code.replace(/(['"`])\.?\/themes\//g, 
'$1./theme-builder/themes/');
+      }
+      return code;
     }
+  };
+}
+
+export default defineConfig({
+  plugins: [
+    vue({
+      template: {
+        transformAssetUrls: {
+          base: '/src',
+          includeAbsolute: false,
+        },
+      }
+    }),
+    createThemePathRewritePlugin(),
+    createHtmlOutputPlugin()
   ],
   build: {
-    outDir: 'app',
+    outDir: 'dist', // Temporary build directory
     emptyOutDir: true,
+    assetsDir: '.', // This will ensure assets are placed at the root level of 
the output directory
     rollupOptions: {
-      input: {
-        'en': path.resolve(process.cwd(), 'index.html'),
-        'zh': path.resolve(process.cwd(), 'index.html')
-      },
       output: {
-        entryFileNames: () => {
-          // 统一放在各自语言目录下的theme-builder目录中
-          return `[name]/theme-builder/app.min.js`
-        },
-        chunkFileNames: (chunkInfo: any) => {
-          const name = chunkInfo.name || ''
-          // 根据名称推断语言
-          if (name.startsWith('en') || name.includes('en-')) {
-            return `en/theme-builder/chunks/[name]-[hash].js`
-          } else {
-            return `zh/theme-builder/chunks/[name]-[hash].js`
-          }
-        },
-        assetFileNames: (assetInfo: any) => {
-          const info = assetInfo.name || ''
-
-          // For CSS files, put them in shared styles directory
-          if (info.endsWith('.css')) {
-            return `styles/main.css`
-          }
-
-          // Common assets go to app root, others to language-specific 
directories
-          if (info.includes('assets/') || info.includes('images/')) {
-            return `assets/[name]-[hash][extname]`
-          }
-
-          return `en/theme-builder/assets/[name]-[hash][extname]`
-        },
-        manualChunks: undefined
+        // Customize output file names
+        entryFileNames: '[name]-[hash].js',
+        chunkFileNames: '[name]-[hash].js',
+        assetFileNames: '[name]-[hash].[ext]'
       }
     }
+  },
+  base: './', // Make sure assets use relative paths
+  resolve: {
+    alias: {
+      '@': path.resolve(__dirname, './src')
+    }
   }
-})
+});
diff --git a/zh/body.html b/zh/body.html
new file mode 100644
index 0000000..42fe349
--- /dev/null
+++ b/zh/body.html
@@ -0,0 +1,2 @@
+<div id="theme-builder"></div>
+<script type="module" src="../src/entries/zh.ts"></script>


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

Reply via email to