Files
desktop/src/components/led-strip-configuration/led-strip-configuration.tsx
Ivan Li 2c6b777fa6 feat: implement comprehensive i18n internationalization support
- Add custom i18n infrastructure with TypeScript support
- Support Chinese (zh-CN) and English (en-US) languages
- Implement language switching with localStorage persistence
- Update all components with translation keys:
  * System info components (board-info-panel, board-index)
  * Display management components (display-state-index, display-state-card)
  * LED strip configuration components (led-strip-configuration, led-count-control-panel)
  * White balance component with detailed usage instructions
  * LED test component with test pattern descriptions
- Add comprehensive translation coverage for:
  * Navigation menus and page titles
  * Common UI elements (buttons, status, actions)
  * Hardware information and connection status
  * Display configuration options
  * LED strip settings and controls
  * White balance adjustment instructions and tips
  * LED test modes and descriptions
  * Error messages and status indicators
- Features:
  * Dynamic language switching without app restart
  * Type-safe translation keys with full TypeScript support
  * Modular design for easy language extension
  * Responsive UI updates using SolidJS reactivity
2025-07-08 16:55:12 +08:00

173 lines
5.7 KiB
TypeScript

import { createEffect, onCleanup } from 'solid-js';
import { invoke } from '@tauri-apps/api/core';
import { DisplayView } from './display-view';
import { DisplayListContainer } from './display-list-container';
import { displayStore, setDisplayStore } from '../../stores/display.store';
import { LedStripConfigContainer } from '../../models/led-strip-config';
import { setLedStripStore } from '../../stores/led-strip.store';
import { listen } from '@tauri-apps/api/event';
import { LedStripPartsSorter } from './led-strip-parts-sorter';
import { LedCountControlPanel } from './led-count-control-panel';
import { createStore } from 'solid-js/store';
import {
LedStripConfigurationContext,
LedStripConfigurationContextType,
} from '../../contexts/led-strip-configuration.context';
import { useLanguage } from '../../i18n/index';
export const LedStripConfiguration = () => {
const { t } = useLanguage();
createEffect(() => {
invoke<string>('list_display_info').then((displays) => {
const parsedDisplays = JSON.parse(displays);
setDisplayStore({
displays: parsedDisplays,
});
}).catch((error) => {
console.error('Failed to load displays:', error);
});
invoke<LedStripConfigContainer>('read_led_strip_configs').then((configs) => {
setLedStripStore(configs);
}).catch((error) => {
console.error('Failed to load LED strip configs:', error);
});
});
// listen to config_changed event
createEffect(() => {
const unlisten = listen('config_changed', (event) => {
const { strips, mappers } = event.payload as LedStripConfigContainer;
setLedStripStore({
strips,
mappers,
});
});
onCleanup(() => {
unlisten.then((unlisten) => unlisten());
});
});
// listen to led_colors_changed event
createEffect(() => {
const unlisten = listen<Uint8ClampedArray>('led_colors_changed', (event) => {
if (!window.document.hidden) {
const colors = event.payload;
setLedStripStore({
colors,
});
}
});
onCleanup(() => {
unlisten.then((unlisten) => unlisten());
});
});
// listen to led_sorted_colors_changed event
createEffect(() => {
const unlisten = listen<Uint8ClampedArray>('led_sorted_colors_changed', (event) => {
if (!window.document.hidden) {
const sortedColors = event.payload;
setLedStripStore({
sortedColors,
});
}
});
onCleanup(() => {
unlisten.then((unlisten) => unlisten());
});
});
const [ledStripConfiguration, setLedStripConfiguration] = createStore<
LedStripConfigurationContextType[0]
>({
selectedStripPart: null,
hoveredStripPart: null,
});
const ledStripConfigurationContextValue: LedStripConfigurationContextType = [
ledStripConfiguration,
{
setSelectedStripPart: (v) => {
setLedStripConfiguration({
selectedStripPart: v,
});
},
setHoveredStripPart: (v) => {
setLedStripConfiguration({
hoveredStripPart: v,
});
},
},
];
return (
<div class="space-y-4">
<div class="flex items-center justify-between">
<h1 class="text-xl font-bold text-base-content">{t('ledConfig.title')}</h1>
<div class="stats shadow">
<div class="stat py-2 px-4">
<div class="stat-title text-xs">{t('displays.displayCount')}</div>
<div class="stat-value text-primary text-lg">{displayStore.displays.length}</div>
</div>
</div>
</div>
<LedStripConfigurationContext.Provider value={ledStripConfigurationContextValue}>
<div class="space-y-4">
{/* LED Strip Sorter Panel */}
<div class="card bg-base-200 shadow-lg">
<div class="card-body p-3">
<div class="card-title text-sm mb-2">
<span>{t('ledConfig.stripSorting')}</span>
<div class="badge badge-info badge-outline text-xs">{t('ledConfig.realtimePreview')}</div>
</div>
<LedStripPartsSorter />
<div class="text-xs text-base-content/50 mt-2">
💡 {t('ledConfig.sortingTip')}
</div>
</div>
</div>
{/* Display Configuration Panel - Auto height based on content */}
<div class="card bg-base-200 shadow-lg">
<div class="card-body p-3">
<div class="card-title text-sm mb-2">
<span>{t('ledConfig.displayConfiguration')}</span>
<div class="badge badge-secondary badge-outline text-xs">{t('ledConfig.visualEditor')}</div>
</div>
<div class="mb-3">
<DisplayListContainer>
{displayStore.displays.map((display) => (
<DisplayView display={display} />
))}
</DisplayListContainer>
</div>
<div class="text-xs text-base-content/50">
💡 {t('ledConfig.displayTip')}
</div>
</div>
</div>
{/* LED Count Control Panels */}
<div class="flex-shrink-0">
<div class="flex items-center gap-2 mb-2">
<h2 class="text-base font-semibold text-base-content">{t('ledConfig.ledCountControl')}</h2>
<div class="badge badge-info badge-outline text-xs">{t('ledConfig.realtimeAdjustment')}</div>
</div>
<div class="led-control-grid">
{displayStore.displays.map((display) => (
<LedCountControlPanel display={display} />
))}
</div>
</div>
</div>
</LedStripConfigurationContext.Provider>
</div>
);
};