feat: Add Daisy-UI and optimize LED strip configuration UI
- Install and configure Tailwind CSS 4.1 with Daisy-UI plugin - Redesign main navigation with responsive navbar and dark theme - Optimize LED strip configuration layout with modern card components - Improve screen preview performance with frame-based rendering - Reduce LED pixel size for better visual appearance - Remove excessive debug logging for better performance - Fix Tailwind CSS ESM compatibility issues with dynamic imports
This commit is contained in:
@ -21,10 +21,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tauri-apps/cli": "^2.6.2",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^24.0.7",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"daisyui": "^5.0.43",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"typescript": "^4.9.5",
|
||||
|
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@ -33,6 +33,9 @@ importers:
|
||||
'@tailwindcss/postcss':
|
||||
specifier: ^4.1.11
|
||||
version: 4.1.11
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.1.11
|
||||
version: 4.1.11(vite@6.3.5(@types/node@24.0.7)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))
|
||||
'@tauri-apps/cli':
|
||||
specifier: ^2.6.2
|
||||
version: 2.6.2
|
||||
@ -45,6 +48,9 @@ importers:
|
||||
autoprefixer:
|
||||
specifier: ^10.4.21
|
||||
version: 10.4.21(postcss@8.5.6)
|
||||
daisyui:
|
||||
specifier: ^5.0.43
|
||||
version: 5.0.43
|
||||
postcss:
|
||||
specifier: ^8.5.6
|
||||
version: 8.5.6
|
||||
@ -511,6 +517,11 @@ packages:
|
||||
'@tailwindcss/postcss@4.1.11':
|
||||
resolution: {integrity: sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==}
|
||||
|
||||
'@tailwindcss/vite@4.1.11':
|
||||
resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==}
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7
|
||||
|
||||
'@tauri-apps/api@2.6.0':
|
||||
resolution: {integrity: sha512-hRNcdercfgpzgFrMXWwNDBN0B7vNzOzRepy6ZAmhxi5mDLVPNrTpo9MGg2tN/F7JRugj4d2aF7E1rtPXAHaetg==}
|
||||
|
||||
@ -644,6 +655,9 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
daisyui@5.0.43:
|
||||
resolution: {integrity: sha512-2pshHJ73vetSpsbAyaOncGnNYL0mwvgseS1EWy1I9Qpw8D11OuBoDNIWrPIME4UFcq2xuff3A9x+eXbuFR9fUQ==}
|
||||
|
||||
debug@4.4.1:
|
||||
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
@ -1344,6 +1358,13 @@ snapshots:
|
||||
postcss: 8.5.6
|
||||
tailwindcss: 4.1.11
|
||||
|
||||
'@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.0.7)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))':
|
||||
dependencies:
|
||||
'@tailwindcss/node': 4.1.11
|
||||
'@tailwindcss/oxide': 4.1.11
|
||||
tailwindcss: 4.1.11
|
||||
vite: 6.3.5(@types/node@24.0.7)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
|
||||
|
||||
'@tauri-apps/api@2.6.0': {}
|
||||
|
||||
'@tauri-apps/cli-darwin-arm64@2.6.2':
|
||||
@ -1466,6 +1487,8 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
daisyui@5.0.43: {}
|
||||
|
||||
debug@4.4.1:
|
||||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
@ -1,6 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
52
src/App.tsx
52
src/App.tsx
@ -21,19 +21,47 @@ function App() {
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<a href="/info">基本信息</a>
|
||||
<a href="/displays">显示器信息</a>
|
||||
<a href="/led-strips-configuration">灯条配置</a>
|
||||
<a href="/white-balance">白平衡</a>
|
||||
<div class="min-h-screen bg-base-100" data-theme="dark">
|
||||
{/* Navigation */}
|
||||
<div class="navbar bg-base-200 shadow-lg">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
||||
<li><a href="/info" class="text-base-content">基本信息</a></li>
|
||||
<li><a href="/displays" class="text-base-content">显示器信息</a></li>
|
||||
<li><a href="/led-strips-configuration" class="text-base-content">灯条配置</a></li>
|
||||
<li><a href="/white-balance" class="text-base-content">白平衡</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="btn btn-ghost text-xl text-primary font-bold">环境光控制</a>
|
||||
</div>
|
||||
<div class="navbar-center hidden lg:flex">
|
||||
<ul class="menu menu-horizontal px-1">
|
||||
<li><a href="/info" class="btn btn-ghost text-base-content hover:text-primary">基本信息</a></li>
|
||||
<li><a href="/displays" class="btn btn-ghost text-base-content hover:text-primary">显示器信息</a></li>
|
||||
<li><a href="/led-strips-configuration" class="btn btn-ghost text-base-content hover:text-primary">灯条配置</a></li>
|
||||
<li><a href="/white-balance" class="btn btn-ghost text-base-content hover:text-primary">白平衡</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="badge badge-primary badge-outline">v1.0</div>
|
||||
</div>
|
||||
</div>
|
||||
<Routes>
|
||||
<Route path="/info" component={InfoIndex} />
|
||||
<Route path="/displays" component={DisplayStateIndex} />
|
||||
<Route path="/led-strips-configuration" component={LedStripConfiguration} />
|
||||
<Route path="/white-balance" component={WhiteBalance} />
|
||||
</Routes>
|
||||
|
||||
{/* Main Content */}
|
||||
<main class="container mx-auto p-4">
|
||||
<Routes>
|
||||
<Route path="/info" component={InfoIndex} />
|
||||
<Route path="/displays" component={DisplayStateIndex} />
|
||||
<Route path="/led-strips-configuration" component={LedStripConfiguration} />
|
||||
<Route path="/white-balance" component={WhiteBalance} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -11,26 +11,59 @@ type ItemProps = {
|
||||
|
||||
const Item: ParentComponent<ItemProps> = (props) => {
|
||||
return (
|
||||
<dl class="flex">
|
||||
<dt class="w-20">{props.label}</dt>
|
||||
<dd class="flex-auto">{props.children}</dd>
|
||||
</dl>
|
||||
<div class="flex justify-between items-center py-1">
|
||||
<dt class="text-sm font-medium text-base-content/70">{props.label}</dt>
|
||||
<dd class="text-sm font-mono text-base-content">{props.children}</dd>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const DisplayStateCard: Component<DisplayStateCardProps> = (props) => {
|
||||
return (
|
||||
<section class="p-2 rounded shadow">
|
||||
<Item label="Brightness">{props.state.brightness}</Item>
|
||||
<Item label="Max Brightness">{props.state.max_brightness}</Item>
|
||||
<Item label="Min Brightness">{props.state.min_brightness}</Item>
|
||||
<Item label="Contrast">{props.state.contrast}</Item>
|
||||
<Item label="Max Contrast">{props.state.max_contrast}</Item>
|
||||
<Item label="Min Contrast">{props.state.min_contrast}</Item>
|
||||
<Item label="Max Mode">{props.state.max_mode}</Item>
|
||||
<Item label="Min Mode">{props.state.min_mode}</Item>
|
||||
<Item label="Mode">{props.state.mode}</Item>
|
||||
<Item label="Last Modified At">{props.state.last_modified_at.toISOString()}</Item>
|
||||
</section>
|
||||
<div class="card bg-base-200 shadow-lg hover:shadow-xl transition-shadow duration-200">
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-base mb-3 flex items-center justify-between">
|
||||
<span>显示器状态</span>
|
||||
<div class="badge badge-primary badge-outline">实时</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3">
|
||||
{/* 亮度信息 */}
|
||||
<div class="bg-base-100 rounded-lg p-3">
|
||||
<h4 class="text-sm font-semibold text-base-content mb-2">亮度设置</h4>
|
||||
<div class="space-y-1">
|
||||
<Item label="当前亮度">{props.state.brightness}</Item>
|
||||
<Item label="最大亮度">{props.state.max_brightness}</Item>
|
||||
<Item label="最小亮度">{props.state.min_brightness}</Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 对比度信息 */}
|
||||
<div class="bg-base-100 rounded-lg p-3">
|
||||
<h4 class="text-sm font-semibold text-base-content mb-2">对比度设置</h4>
|
||||
<div class="space-y-1">
|
||||
<Item label="当前对比度">{props.state.contrast}</Item>
|
||||
<Item label="最大对比度">{props.state.max_contrast}</Item>
|
||||
<Item label="最小对比度">{props.state.min_contrast}</Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 模式信息 */}
|
||||
<div class="bg-base-100 rounded-lg p-3">
|
||||
<h4 class="text-sm font-semibold text-base-content mb-2">模式设置</h4>
|
||||
<div class="space-y-1">
|
||||
<Item label="当前模式">{props.state.mode}</Item>
|
||||
<Item label="最大模式">{props.state.max_mode}</Item>
|
||||
<Item label="最小模式">{props.state.min_mode}</Item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 更新时间 */}
|
||||
<div class="text-xs text-base-content/50 text-center pt-2 border-t border-base-300">
|
||||
最后更新: {props.state.last_modified_at.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -36,17 +36,37 @@ export const DisplayStateIndex: Component = () => {
|
||||
};
|
||||
});
|
||||
return (
|
||||
<ol class="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 p-2 gap-2">
|
||||
<For each={states()}>
|
||||
{(state, index) => (
|
||||
<li class="bg-slate-50 text-gray-800 relative border-2 border-slate-50 hover:border-sky-300 focus:border-sky-300 transition">
|
||||
<DisplayStateCard state={state} />
|
||||
<span class="absolute left-2 -top-3 bg-sky-300 text-white px-1 py-0.5 text-xs rounded-sm font-mono">
|
||||
#{index() + 1}
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ol>
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold text-base-content">显示器状态</h1>
|
||||
<div class="stats shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-title">显示器数量</div>
|
||||
<div class="stat-value text-primary">{states().length}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
<For each={states()}>
|
||||
{(state, index) => (
|
||||
<div class="relative">
|
||||
<DisplayStateCard state={state} />
|
||||
<div class="absolute -top-2 -left-2 w-6 h-6 bg-primary text-primary-content rounded-full flex items-center justify-center text-xs font-bold">
|
||||
{index() + 1}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
{states().length === 0 && (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-6xl mb-4">🖥️</div>
|
||||
<h3 class="text-lg font-semibold text-base-content mb-2">未检测到显示器</h3>
|
||||
<p class="text-base-content/70">请检查显示器连接状态</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -26,17 +26,37 @@ export const BoardIndex: Component = () => {
|
||||
};
|
||||
});
|
||||
return (
|
||||
<ol class="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 p-2 gap-2">
|
||||
<For each={boards()}>
|
||||
{(board, index) => (
|
||||
<li class="bg-slate-50 text-gray-800 relative border-2 border-slate-50 hover:border-sky-300 focus:border-sky-300 transition">
|
||||
<BoardInfoPanel board={board} />
|
||||
<span class="absolute left-2 -top-3 bg-sky-300 text-white px-1 py-0.5 text-xs rounded-sm font-mono">
|
||||
#{index() + 1}
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ol>
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold text-base-content">设备信息</h1>
|
||||
<div class="stats shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-title">设备总数</div>
|
||||
<div class="stat-value text-primary">{boards().length}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<For each={boards()}>
|
||||
{(board, index) => (
|
||||
<div class="relative">
|
||||
<BoardInfoPanel board={board} />
|
||||
<div class="absolute -top-2 -left-2 w-6 h-6 bg-primary text-primary-content rounded-full flex items-center justify-center text-xs font-bold">
|
||||
{index() + 1}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
{boards().length === 0 && (
|
||||
<div class="text-center py-12">
|
||||
<div class="text-6xl mb-4">🔍</div>
|
||||
<h3 class="text-lg font-semibold text-base-content mb-2">未发现设备</h3>
|
||||
<p class="text-base-content/70">请检查设备连接状态</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -7,10 +7,10 @@ type ItemProps = {
|
||||
|
||||
const Item: ParentComponent<ItemProps> = (props) => {
|
||||
return (
|
||||
<dl class="flex">
|
||||
<dt class="w-20">{props.label}</dt>
|
||||
<dd class="flex-auto">{props.children}</dd>
|
||||
</dl>
|
||||
<div class="flex justify-between items-center py-1">
|
||||
<dt class="text-sm font-medium text-base-content/70">{props.label}</dt>
|
||||
<dd class="text-sm font-mono text-base-content">{props.children}</dd>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -41,20 +41,31 @@ export const BoardInfoPanel: Component<{ board: BoardInfo }> = (props) => {
|
||||
}
|
||||
});
|
||||
|
||||
const statusBadgeClass = createMemo(() => {
|
||||
const status = connectStatus();
|
||||
if (status === 'Connected') {
|
||||
return 'badge badge-success badge-sm';
|
||||
} else if (status?.startsWith('Connecting')) {
|
||||
return 'badge badge-warning badge-sm';
|
||||
} else {
|
||||
return 'badge badge-error badge-sm';
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<section class="p-2 rounded shadow">
|
||||
<Item label="Host">{props.board.fullname}</Item>
|
||||
<Item label="Host">{props.board.host}</Item>
|
||||
<Item label="Ip Addr">
|
||||
<span class="font-mono">{props.board.address}</span>
|
||||
</Item>
|
||||
<Item label="Port">
|
||||
<span class="font-mono">{props.board.port}</span>
|
||||
</Item>
|
||||
<Item label="Status">
|
||||
<span class="font-mono">{connectStatus()}</span>
|
||||
</Item>
|
||||
<Item label="TTL">{ttl()}</Item>
|
||||
</section>
|
||||
<div class="card bg-base-200 shadow-lg hover:shadow-xl transition-shadow duration-200">
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-base mb-3 flex items-center justify-between">
|
||||
<span class="truncate">{props.board.fullname}</span>
|
||||
<div class={statusBadgeClass()}>{connectStatus()}</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Item label="主机名">{props.board.host}</Item>
|
||||
<Item label="IP地址">{props.board.address}</Item>
|
||||
<Item label="端口">{props.board.port}</Item>
|
||||
<Item label="延迟">{ttl()}</Item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -7,10 +7,10 @@ type DisplayInfoItemProps = {
|
||||
|
||||
export const DisplayInfoItem: ParentComponent<DisplayInfoItemProps> = (props) => {
|
||||
return (
|
||||
<dl class="px-3 py-1 flex hover:bg-slate-900/50 gap-2 text-white drop-shadow-[0_2px_2px_rgba(0,0,0,0.8)] rounded">
|
||||
<dt class="uppercase w-1/2 select-all whitespace-nowrap">{props.label}</dt>
|
||||
<dd class="select-all w-1/2 whitespace-nowrap">{props.children}</dd>
|
||||
</dl>
|
||||
<div class="flex justify-between items-center py-1 px-2 hover:bg-base-300/50 rounded transition-colors">
|
||||
<dt class="text-sm font-medium text-base-content/80 uppercase">{props.label}</dt>
|
||||
<dd class="text-sm font-mono text-base-content select-all">{props.children}</dd>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -21,22 +21,29 @@ type DisplayInfoPanelProps = {
|
||||
export const DisplayInfoPanel: Component<DisplayInfoPanelProps> = (props) => {
|
||||
const [localProps, rootProps] = splitProps(props, ['display']);
|
||||
return (
|
||||
<section {...rootProps} class={'m-2 flex flex-col gap-1 py-2 ' + rootProps.class}>
|
||||
<DisplayInfoItem label="ID">
|
||||
<code>{localProps.display.id}</code>
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="Position">
|
||||
({localProps.display.x}, {localProps.display.y})
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="Size">
|
||||
{localProps.display.width} x {localProps.display.height}
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="Scale Factor">
|
||||
{localProps.display.scale_factor}
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="is Primary">
|
||||
{localProps.display.is_primary ? 'True' : 'False'}
|
||||
</DisplayInfoItem>
|
||||
</section>
|
||||
<div {...rootProps} class={'card bg-base-100/95 backdrop-blur shadow-lg border border-base-300 ' + rootProps.class}>
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-sm mb-3 flex items-center justify-between">
|
||||
<span>显示器信息</span>
|
||||
{localProps.display.is_primary && (
|
||||
<div class="badge badge-primary badge-sm">主显示器</div>
|
||||
)}
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<DisplayInfoItem label="ID">
|
||||
<code class="bg-base-200 px-1 rounded text-xs">{localProps.display.id}</code>
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="位置">
|
||||
({localProps.display.x}, {localProps.display.y})
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="尺寸">
|
||||
{localProps.display.width} × {localProps.display.height}
|
||||
</DisplayInfoItem>
|
||||
<DisplayInfoItem label="缩放">
|
||||
{localProps.display.scale_factor}×
|
||||
</DisplayInfoItem>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -91,14 +91,49 @@ export const LedStripConfiguration = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold text-base-content">灯条配置</h1>
|
||||
<div class="stats shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-title">显示器数量</div>
|
||||
<div class="stat-value text-primary">{displayStore.displays.length}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LedStripConfigurationContext.Provider value={ledStripConfigurationContextValue}>
|
||||
<LedStripPartsSorter />
|
||||
<DisplayListContainer>
|
||||
{displayStore.displays.map((display) => {
|
||||
return <DisplayView display={display} />;
|
||||
})}
|
||||
</DisplayListContainer>
|
||||
{/* LED Strip Sorter Panel */}
|
||||
<div class="card bg-base-200 shadow-lg">
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-base mb-3">
|
||||
<span>灯条排序</span>
|
||||
<div class="badge badge-info badge-outline">实时预览</div>
|
||||
</div>
|
||||
<LedStripPartsSorter />
|
||||
<div class="text-xs text-base-content/50 mt-2">
|
||||
💡 提示:拖拽灯条段落来调整顺序,双击可反转方向
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Display Configuration Panel */}
|
||||
<div class="card bg-base-200 shadow-lg">
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-base mb-3">
|
||||
<span>显示器配置</span>
|
||||
<div class="badge badge-secondary badge-outline">可视化编辑</div>
|
||||
</div>
|
||||
<DisplayListContainer>
|
||||
{displayStore.displays.map((display) => {
|
||||
return <DisplayView display={display} />;
|
||||
})}
|
||||
</DisplayListContainer>
|
||||
<div class="text-xs text-base-content/50 mt-2">
|
||||
💡 提示:鼠标滚轮调整灯条长度,悬停查看详细信息
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LedStripConfigurationContext.Provider>
|
||||
</div>
|
||||
);
|
||||
|
@ -34,7 +34,7 @@ export const Pixel: Component<PixelProps> = (props) => {
|
||||
title={props.color}
|
||||
>
|
||||
<div
|
||||
class="absolute top-1/2 -translate-y-1/2 h-2 w-2 rounded-full ring-1 ring-stone-300/30"
|
||||
class="absolute top-1/2 -translate-y-1/2 h-1.5 w-1.5 rounded-full ring-1 ring-stone-300/30"
|
||||
style={style()}
|
||||
/>
|
||||
</div>
|
||||
@ -60,32 +60,16 @@ export const LedStripPart: Component<LedStripPartProps> = (props) => {
|
||||
);
|
||||
|
||||
if (index === -1) {
|
||||
console.log('🔍 LED: Strip config not found', {
|
||||
displayId: localProps.config.display_id,
|
||||
border: localProps.config.border,
|
||||
availableStrips: ledStripStore.strips.length
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const mapper = ledStripStore.mappers[index];
|
||||
if (!mapper) {
|
||||
console.log('🔍 LED: Mapper not found', { index, mappersCount: ledStripStore.mappers.length });
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = mapper.start * 3;
|
||||
|
||||
console.log('🎨 LED: Updating colors', {
|
||||
displayId: localProps.config.display_id,
|
||||
border: localProps.config.border,
|
||||
stripLength: localProps.config.len,
|
||||
mapperPos: mapper.pos,
|
||||
offset,
|
||||
colorsArrayLength: ledStripStore.colors.length,
|
||||
firstFewColors: Array.from(ledStripStore.colors.slice(offset, offset + 9))
|
||||
});
|
||||
|
||||
const colors = new Array(localProps.config.len).fill(null).map((_, i) => {
|
||||
const index = offset + i * 3;
|
||||
const r = ledStripStore.colors[index] || 0;
|
||||
@ -94,12 +78,6 @@ export const LedStripPart: Component<LedStripPartProps> = (props) => {
|
||||
return `rgb(${r}, ${g}, ${b})`;
|
||||
});
|
||||
|
||||
console.log('🎨 LED: Generated colors', {
|
||||
border: localProps.config.border,
|
||||
colorsCount: colors.length,
|
||||
sampleColors: colors.slice(0, 3)
|
||||
});
|
||||
|
||||
setColors(colors);
|
||||
});
|
||||
|
||||
@ -143,7 +121,7 @@ export const LedStripPart: Component<LedStripPartProps> = (props) => {
|
||||
{...rootProps}
|
||||
ref={setAnchor}
|
||||
class={
|
||||
'flex rounded-full flex-nowrap justify-around items-center overflow-hidden bg-gray-800/20 border border-gray-600/30 min-h-[16px] min-w-[16px] ' +
|
||||
'flex rounded-full flex-nowrap justify-around items-center overflow-hidden bg-gray-800/20 border border-gray-600/30 min-h-[32px] min-w-[32px] ' +
|
||||
rootProps.class
|
||||
}
|
||||
classList={{
|
||||
|
@ -36,18 +36,10 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
const [isLoading, setIsLoading] = createSignal(false);
|
||||
let isMounted = true;
|
||||
|
||||
// Fetch screenshot data from backend
|
||||
// Fetch screenshot data from backend with frame-based rendering
|
||||
const fetchScreenshot = async () => {
|
||||
console.log('📸 FETCH: Starting screenshot fetch', {
|
||||
isLoading: isLoading(),
|
||||
isMounted,
|
||||
hidden: hidden(),
|
||||
timestamp: new Date().toLocaleTimeString()
|
||||
});
|
||||
|
||||
if (isLoading()) {
|
||||
console.log('⏳ FETCH: Already loading, skipping');
|
||||
return; // Skip if already loading
|
||||
return; // Skip if already loading - frame-based approach
|
||||
}
|
||||
|
||||
try {
|
||||
@ -57,9 +49,7 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
const response = await fetch(`ambient-light://displays/${localProps.displayId}?width=400&height=225&t=${timestamp}`);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('❌ FETCH: Screenshot fetch failed', response.status, response.statusText);
|
||||
const errorText = await response.text();
|
||||
console.error('❌ FETCH: Error response body:', errorText);
|
||||
console.error('Screenshot fetch failed:', response.status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -69,71 +59,43 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
const buffer = new Uint8ClampedArray(arrayBuffer);
|
||||
const expectedSize = width * height * 4;
|
||||
|
||||
|
||||
|
||||
// Validate buffer size
|
||||
if (buffer.length !== expectedSize) {
|
||||
console.error('❌ FETCH: Invalid buffer size!', {
|
||||
received: buffer.length,
|
||||
expected: expectedSize,
|
||||
ratio: buffer.length / expectedSize
|
||||
});
|
||||
console.error('Invalid buffer size:', buffer.length, 'expected:', expectedSize);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('📊 FETCH: Setting image data', { width, height, bufferSize: buffer.length });
|
||||
|
||||
setImageData({
|
||||
buffer,
|
||||
width,
|
||||
height
|
||||
});
|
||||
|
||||
// Use setTimeout to ensure the signal update has been processed
|
||||
// Draw immediately after data is set
|
||||
setTimeout(() => {
|
||||
console.log('🖼️ FETCH: Triggering draw after data set');
|
||||
draw(false);
|
||||
}, 0);
|
||||
|
||||
// Schedule next frame after rendering is complete
|
||||
// Frame-based rendering: wait for current frame to complete before scheduling next
|
||||
const shouldContinue = !hidden() && isMounted;
|
||||
console.log('🔄 FETCH: Scheduling next frame', {
|
||||
hidden: hidden(),
|
||||
isMounted,
|
||||
shouldContinue,
|
||||
nextFrameDelay: '1000ms'
|
||||
});
|
||||
|
||||
if (shouldContinue) {
|
||||
setTimeout(() => {
|
||||
if (isMounted) {
|
||||
console.log('🔄 FETCH: Starting next frame');
|
||||
fetchScreenshot();
|
||||
} else {
|
||||
console.log('❌ FETCH: Component unmounted, stopping loop');
|
||||
fetchScreenshot(); // Start next frame only after current one completes
|
||||
}
|
||||
}, 1000); // Wait 1 second before next frame
|
||||
} else {
|
||||
console.log('❌ FETCH: Loop stopped - component hidden or unmounted');
|
||||
}, 500); // Reduced frequency to 500ms for better performance
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ FETCH: Error fetching screenshot:', error);
|
||||
// Even on error, schedule next frame
|
||||
console.error('Error fetching screenshot:', error);
|
||||
// On error, wait longer before retry
|
||||
const shouldContinueOnError = !hidden() && isMounted;
|
||||
console.log('🔄 FETCH: Error recovery - scheduling next frame', {
|
||||
error: error.message,
|
||||
shouldContinue: shouldContinueOnError,
|
||||
nextFrameDelay: '2000ms'
|
||||
});
|
||||
|
||||
if (shouldContinueOnError) {
|
||||
setTimeout(() => {
|
||||
if (isMounted) {
|
||||
console.log('🔄 FETCH: Retrying after error');
|
||||
fetchScreenshot();
|
||||
}
|
||||
}, 2000); // Wait longer on error
|
||||
}, 2000);
|
||||
}
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@ -141,22 +103,10 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
};
|
||||
|
||||
const resetSize = () => {
|
||||
console.log('📏 CANVAS: Resizing', {
|
||||
rootClientWidth: root.clientWidth,
|
||||
rootClientHeight: root.clientHeight,
|
||||
oldCanvasWidth: canvas.width,
|
||||
oldCanvasHeight: canvas.height
|
||||
});
|
||||
|
||||
// Set canvas size first
|
||||
canvas.width = root.clientWidth;
|
||||
canvas.height = root.clientHeight;
|
||||
|
||||
console.log('📏 CANVAS: Size set', {
|
||||
newCanvasWidth: canvas.width,
|
||||
newCanvasHeight: canvas.height
|
||||
});
|
||||
|
||||
// Use a default aspect ratio if canvas dimensions are invalid
|
||||
const aspectRatio = (canvas.width > 0 && canvas.height > 0)
|
||||
? canvas.width / canvas.height
|
||||
@ -179,30 +129,15 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
drawHeight,
|
||||
});
|
||||
|
||||
|
||||
|
||||
draw(true);
|
||||
};
|
||||
|
||||
const draw = (cached: boolean = false) => {
|
||||
const { drawX, drawY, drawWidth, drawHeight } = drawInfo();
|
||||
|
||||
let _ctx = ctx();
|
||||
let raw = imageData();
|
||||
|
||||
console.log('🖼️ DRAW: Called with', {
|
||||
cached,
|
||||
hasContext: !!_ctx,
|
||||
hasImageData: !!raw,
|
||||
imageDataSize: raw ? `${raw.width}x${raw.height}` : 'none',
|
||||
drawInfo: { drawX, drawY, drawWidth, drawHeight },
|
||||
canvasSize: `${canvas.width}x${canvas.height}`,
|
||||
contextType: _ctx ? 'valid' : 'null',
|
||||
rawBufferSize: raw ? raw.buffer.length : 0
|
||||
});
|
||||
|
||||
if (_ctx && raw) {
|
||||
console.log('🖼️ DRAW: Starting to draw image');
|
||||
_ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Apply transparency effect for cached images if needed
|
||||
@ -220,34 +155,24 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
|
||||
// If the image size matches the draw size, use putImageData directly
|
||||
if (raw.width === drawWidth && raw.height === drawHeight) {
|
||||
console.log('🖼️ DRAW: Using putImageData directly');
|
||||
_ctx.putImageData(img, drawX, drawY);
|
||||
console.log('✅ DRAW: putImageData completed');
|
||||
} else {
|
||||
console.log('🖼️ DRAW: Using scaling with temp canvas');
|
||||
// Otherwise, use cached temporary canvas for scaling
|
||||
if (!tempCanvas || tempCanvas.width !== raw.width || tempCanvas.height !== raw.height) {
|
||||
tempCanvas = document.createElement('canvas');
|
||||
tempCanvas.width = raw.width;
|
||||
tempCanvas.height = raw.height;
|
||||
tempCtx = tempCanvas.getContext('2d');
|
||||
console.log('🖼️ DRAW: Created new temp canvas');
|
||||
}
|
||||
|
||||
if (tempCtx) {
|
||||
tempCtx.putImageData(img, 0, 0);
|
||||
_ctx.drawImage(tempCanvas, drawX, drawY, drawWidth, drawHeight);
|
||||
console.log('✅ DRAW: Scaled drawing completed');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ DRAW: Error in draw():', error);
|
||||
console.error('Error in draw():', error);
|
||||
}
|
||||
} else {
|
||||
console.log('❌ DRAW: Cannot draw - missing context or image data', {
|
||||
hasContext: !!_ctx,
|
||||
hasImageData: !!raw
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -255,11 +180,8 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
|
||||
// Initialize canvas and resize observer
|
||||
onMount(() => {
|
||||
console.log('🚀 CANVAS: Component mounted');
|
||||
const context = canvas.getContext('2d');
|
||||
console.log('🚀 CANVAS: Context obtained', !!context);
|
||||
setCtx(context);
|
||||
console.log('🚀 CANVAS: Context signal set');
|
||||
|
||||
// Initial size setup
|
||||
resetSize();
|
||||
@ -270,16 +192,13 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
|
||||
resizeObserver.observe(root);
|
||||
|
||||
// Start screenshot fetching after context is ready
|
||||
console.log('🚀 SCREENSHOT: Starting screenshot fetching');
|
||||
setTimeout(() => {
|
||||
console.log('🚀 SCREENSHOT: Context ready, starting fetch');
|
||||
fetchScreenshot(); // Initial fetch - will self-schedule subsequent frames
|
||||
}, 100); // Small delay to ensure context is ready
|
||||
|
||||
onCleanup(() => {
|
||||
isMounted = false; // Stop scheduling new frames
|
||||
resizeObserver?.unobserve(root);
|
||||
console.log('🧹 CLEANUP: Component unmounted');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -14,7 +14,7 @@ export const ColorSlider: Component<Props> = (props) => {
|
||||
step={0.01}
|
||||
value={props.value}
|
||||
class={
|
||||
'w-full h-2 bg-gradient-to-r rounded-lg appearance-none cursor-pointer dark:bg-gray-700 drop-shadow ' +
|
||||
'range range-primary w-full bg-gradient-to-r ' +
|
||||
props.class
|
||||
}
|
||||
/>
|
||||
|
@ -11,10 +11,9 @@ import transparentBg from '../../assets/transparent-grid-background.svg?url';
|
||||
|
||||
const Value: Component<{ value: number }> = (props) => {
|
||||
return (
|
||||
<span class="w-10 text-sm block font-mono text-right ">
|
||||
{(props.value * 100).toFixed(0)}
|
||||
<span class="text-xs text-stone-600">%</span>
|
||||
</span>
|
||||
<div class="badge badge-outline badge-sm font-mono">
|
||||
{(props.value * 100).toFixed(0)}%
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -55,77 +54,118 @@ export const WhiteBalance = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<section class="select-none text-stone-800">
|
||||
<div
|
||||
class="absolute top-0 left-0 right-0 bottom-0"
|
||||
style={{
|
||||
'background-image': `url(${transparentBg})`,
|
||||
}}
|
||||
>
|
||||
<TestColorsBg />
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-2xl font-bold text-base-content">白平衡调节</h1>
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-outline btn-sm" onClick={reset} title="重置到100%">
|
||||
<BiRegularReset size={16} />
|
||||
重置
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm" onClick={exit} title="返回">
|
||||
<VsClose size={16} />
|
||||
返回
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-10/12 max-w-lg bg-stone-100/20 backdrop-blur p-5 rounded-xl shadow-lg">
|
||||
<label class="flex items-center gap-2">
|
||||
<span class="w-3 block">R:</span>
|
||||
<ColorSlider
|
||||
class="from-cyan-500 to-red-500"
|
||||
value={ledStripStore.colorCalibration.r}
|
||||
onInput={(ev) =>
|
||||
updateColorCalibration(
|
||||
'r',
|
||||
(ev.target as HTMLInputElement).valueAsNumber ?? 1,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Value value={ledStripStore.colorCalibration.r} />
|
||||
</label>
|
||||
<label class="flex items-center gap-2">
|
||||
<span class="w-3 block">G:</span>
|
||||
<ColorSlider
|
||||
class="from-pink-500 to-green-500"
|
||||
value={ledStripStore.colorCalibration.g}
|
||||
onInput={(ev) =>
|
||||
updateColorCalibration(
|
||||
'g',
|
||||
(ev.target as HTMLInputElement).valueAsNumber ?? 1,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Value value={ledStripStore.colorCalibration.g} />
|
||||
</label>
|
||||
<label class="flex items-center gap-2">
|
||||
<span class="w-3 block">B:</span>
|
||||
<ColorSlider
|
||||
class="from-yellow-500 to-blue-500"
|
||||
value={ledStripStore.colorCalibration.b}
|
||||
onInput={(ev) =>
|
||||
updateColorCalibration(
|
||||
'b',
|
||||
(ev.target as HTMLInputElement).valueAsNumber ?? 1,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Value value={ledStripStore.colorCalibration.b} />
|
||||
</label>
|
||||
<label class="flex items-center gap-2">
|
||||
<span class="w-3 block">W:</span>
|
||||
<ColorSlider class="from-yellow-50 to-cyan-50" />
|
||||
</label>
|
||||
<button
|
||||
class="absolute -right-4 -top-4 rounded-full aspect-square bg-stone-100/20 backdrop-blur p-1 shadow hover:bg-stone-200/20 active:bg-stone-300"
|
||||
onClick={exit}
|
||||
title="Go Back"
|
||||
>
|
||||
<VsClose size={24} />
|
||||
</button>
|
||||
<button
|
||||
class="absolute -right-4 -bottom-4 rounded-full aspect-square bg-stone-100/20 backdrop-blur p-1 shadow hover:bg-stone-200/20 active:bg-stone-300"
|
||||
onClick={reset}
|
||||
title="Reset to 100%"
|
||||
>
|
||||
<BiRegularReset size={24} />
|
||||
</button>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* 颜色测试区域 */}
|
||||
<div class="card bg-base-200 shadow-lg">
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-base mb-3">
|
||||
<span>颜色测试</span>
|
||||
<div class="badge badge-info badge-outline">点击测试</div>
|
||||
</div>
|
||||
<div
|
||||
class="aspect-square rounded-lg overflow-hidden border border-base-300"
|
||||
style={{
|
||||
'background-image': `url(${transparentBg})`,
|
||||
}}
|
||||
>
|
||||
<TestColorsBg />
|
||||
</div>
|
||||
<div class="text-xs text-base-content/50 mt-2">
|
||||
💡 提示:点击颜色块进行单色测试,再次点击返回多色模式
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 白平衡控制面板 */}
|
||||
<div class="card bg-base-200 shadow-lg">
|
||||
<div class="card-body p-4">
|
||||
<div class="card-title text-base mb-3">
|
||||
<span>RGB调节</span>
|
||||
<div class="badge badge-secondary badge-outline">实时调节</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold text-red-500">红色 (R)</span>
|
||||
<Value value={ledStripStore.colorCalibration.r} />
|
||||
</label>
|
||||
<ColorSlider
|
||||
class="from-cyan-500 to-red-500"
|
||||
value={ledStripStore.colorCalibration.r}
|
||||
onInput={(ev) =>
|
||||
updateColorCalibration(
|
||||
'r',
|
||||
(ev.target as HTMLInputElement).valueAsNumber ?? 1,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold text-green-500">绿色 (G)</span>
|
||||
<Value value={ledStripStore.colorCalibration.g} />
|
||||
</label>
|
||||
<ColorSlider
|
||||
class="from-pink-500 to-green-500"
|
||||
value={ledStripStore.colorCalibration.g}
|
||||
onInput={(ev) =>
|
||||
updateColorCalibration(
|
||||
'g',
|
||||
(ev.target as HTMLInputElement).valueAsNumber ?? 1,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold text-blue-500">蓝色 (B)</span>
|
||||
<Value value={ledStripStore.colorCalibration.b} />
|
||||
</label>
|
||||
<ColorSlider
|
||||
class="from-yellow-500 to-blue-500"
|
||||
value={ledStripStore.colorCalibration.b}
|
||||
onInput={(ev) =>
|
||||
updateColorCalibration(
|
||||
'b',
|
||||
(ev.target as HTMLInputElement).valueAsNumber ?? 1,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text font-semibold text-base-content/70">白色 (W)</span>
|
||||
<div class="badge badge-outline badge-sm">暂未启用</div>
|
||||
</label>
|
||||
<ColorSlider class="from-yellow-50 to-cyan-50" disabled />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-base-content/50 mt-4">
|
||||
💡 提示:调节RGB滑块来校正LED灯条的白平衡,使白色更加纯净
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,3 +1,2 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@config "../tailwind.config.js";
|
@ -1,9 +1,20 @@
|
||||
import daisyui from 'daisyui';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
export default {
|
||||
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [daisyui],
|
||||
daisyui: {
|
||||
themes: ["dark", "light"],
|
||||
darkTheme: "dark",
|
||||
base: true,
|
||||
styled: true,
|
||||
utils: true,
|
||||
prefix: "",
|
||||
logs: true,
|
||||
themeRoot: ":root",
|
||||
},
|
||||
};
|
||||
|
@ -6,8 +6,14 @@ const mobile =
|
||||
process.env.TAURI_PLATFORM === "ios";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [solidPlugin()],
|
||||
export default defineConfig(async () => {
|
||||
const tailwindcss = (await import("@tailwindcss/vite")).default;
|
||||
|
||||
return {
|
||||
plugins: [
|
||||
solidPlugin(),
|
||||
tailwindcss(),
|
||||
],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
@ -28,4 +34,5 @@ export default defineConfig(async () => ({
|
||||
// produce sourcemaps for debug builds
|
||||
sourcemap: !!process.env.TAURI_DEBUG,
|
||||
},
|
||||
}));
|
||||
};
|
||||
});
|
||||
|
Reference in New Issue
Block a user