Compare commits
5 Commits
d1fc5713a1
...
4a3d7681d6
Author | SHA1 | Date | |
---|---|---|---|
4a3d7681d6 | |||
2834b7fe57 | |||
c57f52ea74 | |||
9f37b4043c | |||
92349eebb6 |
@@ -194,25 +194,42 @@ impl UdpRpc {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store previous board states to detect changes
|
||||||
|
let prev_boards = boards
|
||||||
|
.values()
|
||||||
|
.map(|it| async move { it.info.read().await.clone() });
|
||||||
|
let prev_boards = join_all(prev_boards).await;
|
||||||
|
|
||||||
|
// Check all boards
|
||||||
for board in boards.values() {
|
for board in boards.values() {
|
||||||
if let Err(err) = board.check().await {
|
if let Err(err) = board.check().await {
|
||||||
error!("failed to check board: {:?}", err);
|
error!("failed to check board: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tx_boards = boards
|
// Get current board states after check
|
||||||
|
let current_boards = boards
|
||||||
.values()
|
.values()
|
||||||
.map(|it| async move { it.info.read().await.clone() });
|
.map(|it| async move { it.info.read().await.clone() });
|
||||||
let tx_boards = join_all(tx_boards).await;
|
let current_boards = join_all(current_boards).await;
|
||||||
|
|
||||||
drop(boards);
|
drop(boards);
|
||||||
|
|
||||||
let board_change_sender = self.boards_change_sender.clone();
|
// Only send update if there are actual changes
|
||||||
if let Err(err) = board_change_sender.send(tx_boards) {
|
let has_changes = prev_boards.len() != current_boards.len() ||
|
||||||
error!("failed to send board change: {:?}", err);
|
prev_boards.iter().zip(current_boards.iter()).any(|(prev, current)| {
|
||||||
}
|
prev.connect_status != current.connect_status ||
|
||||||
|
prev.ttl != current.ttl ||
|
||||||
|
prev.checked_at != current.checked_at
|
||||||
|
});
|
||||||
|
|
||||||
drop(board_change_sender);
|
if has_changes {
|
||||||
|
let board_change_sender = self.boards_change_sender.clone();
|
||||||
|
if let Err(err) = board_change_sender.send(current_boards) {
|
||||||
|
error!("failed to send board change: {:?}", err);
|
||||||
|
}
|
||||||
|
drop(board_change_sender);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
src/App.tsx
24
src/App.tsx
@@ -46,22 +46,22 @@ function App() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="min-h-screen bg-base-100" data-theme="dark">
|
<div class="h-screen bg-base-100 flex flex-col" data-theme="dark">
|
||||||
{/* Fixed Navigation */}
|
{/* Fixed Navigation */}
|
||||||
<div class="navbar bg-base-200 shadow-lg fixed top-0 left-0 right-0 z-50">
|
<div class="navbar bg-base-200 shadow-lg flex-shrink-0 z-50">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<div class="dropdown">
|
<div class="dropdown dropdown-hover">
|
||||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden" onClick={(e) => e.currentTarget.focus()}>
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<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>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
<ul class="menu menu-sm dropdown-content mt-3 z-[100] p-2 shadow bg-base-100 rounded-box w-52 border border-base-300">
|
||||||
<li><A href="/info" class="text-base-content">基本信息</A></li>
|
<li><A href="/info" class="text-base-content hover:bg-base-200">基本信息</A></li>
|
||||||
<li><A href="/displays" class="text-base-content">显示器信息</A></li>
|
<li><A href="/displays" class="text-base-content hover:bg-base-200">显示器信息</A></li>
|
||||||
<li><A href="/led-strips-configuration" class="text-base-content">灯条配置</A></li>
|
<li><A href="/led-strips-configuration" class="text-base-content hover:bg-base-200">灯条配置</A></li>
|
||||||
<li><A href="/white-balance" class="text-base-content">白平衡</A></li>
|
<li><A href="/white-balance" class="text-base-content hover:bg-base-200">白平衡</A></li>
|
||||||
<li><A href="/led-strip-test" class="text-base-content">灯带测试</A></li>
|
<li><A href="/led-strip-test" class="text-base-content hover:bg-base-200">灯带测试</A></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-ghost text-xl text-primary font-bold">环境光控制</a>
|
<a class="btn btn-ghost text-xl text-primary font-bold">环境光控制</a>
|
||||||
@@ -80,8 +80,8 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content with top padding to account for fixed navbar */}
|
{/* Main Content - fills remaining height */}
|
||||||
<main class="container mx-auto p-4 pt-20">
|
<main class="flex-1 container mx-auto px-2 sm:px-4 py-4 max-w-full overflow-x-auto min-h-0">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/info" component={InfoIndex} />
|
<Route path="/info" component={InfoIndex} />
|
||||||
<Route path="/displays" component={DisplayStateIndex} />
|
<Route path="/displays" component={DisplayStateIndex} />
|
||||||
|
@@ -75,7 +75,7 @@ export const DisplayListContainer: ParentComponent = (props) => {
|
|||||||
createEffect(() => {});
|
createEffect(() => {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section ref={root!} class="relative bg-gray-400/30" style={rootStyle()}>
|
<section ref={root!} class="relative bg-gray-400/30 h-full w-full" style={rootStyle()}>
|
||||||
<ol class="absolute" style={olStyle()}>
|
<ol class="absolute" style={olStyle()}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</ol>
|
</ol>
|
||||||
|
@@ -95,7 +95,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="card bg-base-100 border border-base-300/50 p-2 transition-all duration-200 cursor-pointer"
|
class="card bg-base-100 border border-base-300/50 p-1.5 transition-all duration-200 cursor-pointer"
|
||||||
classList={{
|
classList={{
|
||||||
'ring-2 ring-primary bg-primary/20 border-primary':
|
'ring-2 ring-primary bg-primary/20 border-primary':
|
||||||
stripConfiguration.hoveredStripPart?.border === props.border &&
|
stripConfiguration.hoveredStripPart?.border === props.border &&
|
||||||
@@ -111,9 +111,9 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-0.5">
|
||||||
<button
|
<button
|
||||||
class="btn btn-xs btn-circle btn-outline flex-shrink-0"
|
class="btn btn-xs btn-circle btn-outline flex-shrink-0 min-h-0 h-6 w-6"
|
||||||
onClick={handleDecrease}
|
onClick={handleDecrease}
|
||||||
disabled={!config() || (config()?.len || 0) <= 0}
|
disabled={!config() || (config()?.len || 0) <= 0}
|
||||||
title="减少LED数量"
|
title="减少LED数量"
|
||||||
@@ -123,7 +123,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
|
|||||||
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
class="input input-xs flex-1 text-center min-w-0 text-sm font-medium [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
|
class="input input-xs flex-1 text-center min-w-0 text-xs font-medium [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none h-6 px-1"
|
||||||
value={config()?.len || 0}
|
value={config()?.len || 0}
|
||||||
min="0"
|
min="0"
|
||||||
max="1000"
|
max="1000"
|
||||||
@@ -136,7 +136,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-xs btn-circle btn-outline flex-shrink-0"
|
class="btn btn-xs btn-circle btn-outline flex-shrink-0 min-h-0 h-6 w-6"
|
||||||
onClick={handleIncrease}
|
onClick={handleIncrease}
|
||||||
disabled={!config() || (config()?.len || 0) >= 1000}
|
disabled={!config() || (config()?.len || 0) >= 1000}
|
||||||
title="增加LED数量"
|
title="增加LED数量"
|
||||||
@@ -147,7 +147,7 @@ const LedCountControlItem: Component<LedCountControlItemProps> = (props) => {
|
|||||||
|
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<select
|
<select
|
||||||
class="select select-xs w-full text-xs"
|
class="select select-xs w-full text-xs h-6 min-h-0"
|
||||||
value={config()?.led_type || LedType.RGB}
|
value={config()?.led_type || LedType.RGB}
|
||||||
onChange={handleLedTypeChange}
|
onChange={handleLedTypeChange}
|
||||||
title="LED类型"
|
title="LED类型"
|
||||||
@@ -177,13 +177,13 @@ export const LedCountControlPanel: Component<LedCountControlPanelProps> = (props
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div {...rootProps} class={'card bg-base-200 shadow-lg border border-base-300 ' + (rootProps.class || '')}>
|
<div {...rootProps} class={'card bg-base-200 shadow-lg border border-base-300 ' + (rootProps.class || '')}>
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-3">
|
||||||
<div class="card-title text-base mb-3 flex items-center justify-between">
|
<div class="card-title text-sm mb-2 flex items-center justify-between">
|
||||||
<span>LED数量控制</span>
|
<span>LED数量控制</span>
|
||||||
<div class="badge badge-info badge-outline">显示器 {localProps.display.id}</div>
|
<div class="badge badge-info badge-outline text-xs">显示器 {localProps.display.id}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-4 gap-2">
|
<div class="grid grid-cols-2 sm:grid-cols-4 gap-2">
|
||||||
<For each={borders}>
|
<For each={borders}>
|
||||||
{(item) => (
|
{(item) => (
|
||||||
<LedCountControlItem
|
<LedCountControlItem
|
||||||
@@ -195,7 +195,7 @@ export const LedCountControlPanel: Component<LedCountControlPanelProps> = (props
|
|||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-xs text-base-content/50 mt-3 p-2 bg-base-300/50 rounded">
|
<div class="text-xs text-base-content/50 mt-2 p-1.5 bg-base-300/50 rounded">
|
||||||
💡 提示:点击 +/- 按钮或直接输入数值来调整LED数量(范围:0-1000)
|
💡 提示:点击 +/- 按钮或直接输入数值来调整LED数量(范围:0-1000)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -104,62 +104,64 @@ export const LedStripConfiguration = () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="space-y-6">
|
<div class="space-y-4">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h1 class="text-2xl font-bold text-base-content">灯条配置</h1>
|
<h1 class="text-xl font-bold text-base-content">灯条配置</h1>
|
||||||
<div class="stats shadow">
|
<div class="stats shadow">
|
||||||
<div class="stat">
|
<div class="stat py-2 px-4">
|
||||||
<div class="stat-title">显示器数量</div>
|
<div class="stat-title text-xs">显示器数量</div>
|
||||||
<div class="stat-value text-primary">{displayStore.displays.length}</div>
|
<div class="stat-value text-primary text-lg">{displayStore.displays.length}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LedStripConfigurationContext.Provider value={ledStripConfigurationContextValue}>
|
<LedStripConfigurationContext.Provider value={ledStripConfigurationContextValue}>
|
||||||
{/* 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>
|
|
||||||
<div class="h-96 mb-4">
|
|
||||||
<DisplayListContainer>
|
|
||||||
{displayStore.displays.map((display) => (
|
|
||||||
<DisplayView display={display} />
|
|
||||||
))}
|
|
||||||
</DisplayListContainer>
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-base-content/50">
|
|
||||||
💡 提示:悬停显示器查看详细信息,使用下方控制面板调整LED数量
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* LED Count Control Panels */}
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="flex items-center gap-2 mb-3">
|
{/* LED Strip Sorter Panel */}
|
||||||
<h2 class="text-lg font-semibold text-base-content">LED数量控制</h2>
|
<div class="card bg-base-200 shadow-lg">
|
||||||
<div class="badge badge-info badge-outline">实时调整</div>
|
<div class="card-body p-3">
|
||||||
|
<div class="card-title text-sm mb-2">
|
||||||
|
<span>灯条排序</span>
|
||||||
|
<div class="badge badge-info badge-outline text-xs">实时预览</div>
|
||||||
|
</div>
|
||||||
|
<LedStripPartsSorter />
|
||||||
|
<div class="text-xs text-base-content/50 mt-2">
|
||||||
|
💡 提示:拖拽灯条段落来调整顺序,双击可反转方向
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
{displayStore.displays.map((display) => (
|
{/* Display Configuration Panel - Auto height based on content */}
|
||||||
<LedCountControlPanel display={display} />
|
<div class="card bg-base-200 shadow-lg">
|
||||||
))}
|
<div class="card-body p-3">
|
||||||
|
<div class="card-title text-sm mb-2">
|
||||||
|
<span>显示器配置</span>
|
||||||
|
<div class="badge badge-secondary badge-outline text-xs">可视化编辑</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<DisplayListContainer>
|
||||||
|
{displayStore.displays.map((display) => (
|
||||||
|
<DisplayView display={display} />
|
||||||
|
))}
|
||||||
|
</DisplayListContainer>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-base-content/50">
|
||||||
|
💡 提示:悬停显示器查看详细信息,使用下方控制面板调整LED数量
|
||||||
|
</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">LED数量控制</h2>
|
||||||
|
<div class="badge badge-info badge-outline text-xs">实时调整</div>
|
||||||
|
</div>
|
||||||
|
<div class="led-control-grid">
|
||||||
|
{displayStore.displays.map((display) => (
|
||||||
|
<LedCountControlPanel display={display} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</LedStripConfigurationContext.Provider>
|
</LedStripConfigurationContext.Provider>
|
||||||
|
@@ -58,7 +58,10 @@ export const LedStripTest = () => {
|
|||||||
board.port === currentBoard.port
|
board.port === currentBoard.port
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!stillExists) {
|
if (stillExists) {
|
||||||
|
// Update to the new board object to reflect any status changes
|
||||||
|
setSelectedBoard(stillExists);
|
||||||
|
} else {
|
||||||
// Current board is no longer available, select first available or null
|
// Current board is no longer available, select first available or null
|
||||||
setSelectedBoard(boardList.length > 0 ? boardList[0] : null);
|
setSelectedBoard(boardList.length > 0 ? boardList[0] : null);
|
||||||
}
|
}
|
||||||
|
@@ -5,6 +5,16 @@ type Props = {
|
|||||||
} & JSX.HTMLAttributes<HTMLInputElement>;
|
} & JSX.HTMLAttributes<HTMLInputElement>;
|
||||||
|
|
||||||
export const ColorSlider: Component<Props> = (props) => {
|
export const ColorSlider: Component<Props> = (props) => {
|
||||||
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
// 阻止事件冒泡到父元素,避免触发面板拖拽
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
// 阻止事件冒泡到父元素
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
@@ -17,6 +27,8 @@ export const ColorSlider: Component<Props> = (props) => {
|
|||||||
'range range-primary w-full bg-gradient-to-r ' +
|
'range range-primary w-full bg-gradient-to-r ' +
|
||||||
props.class
|
props.class
|
||||||
}
|
}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -56,13 +56,17 @@ export const WhiteBalance = () => {
|
|||||||
|
|
||||||
// 拖拽处理函数
|
// 拖拽处理函数
|
||||||
const handleMouseDown = (e: MouseEvent) => {
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
// 确保只有在标题栏区域点击时才触发拖拽
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
|
const panelRect = (e.currentTarget as HTMLElement).closest('.fixed')?.getBoundingClientRect();
|
||||||
setDragOffset({
|
if (panelRect) {
|
||||||
x: e.clientX - rect.left,
|
setDragOffset({
|
||||||
y: e.clientY - rect.top
|
x: e.clientX - panelRect.left,
|
||||||
});
|
y: e.clientY - panelRect.top
|
||||||
|
});
|
||||||
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
@@ -338,22 +342,24 @@ export const WhiteBalance = () => {
|
|||||||
|
|
||||||
{/* 可拖拽的RGB控制面板 */}
|
{/* 可拖拽的RGB控制面板 */}
|
||||||
<div
|
<div
|
||||||
class="fixed w-80 bg-base-200/95 backdrop-blur-sm rounded-lg shadow-xl z-60 cursor-move select-none"
|
class="fixed w-80 bg-base-200/95 backdrop-blur-sm rounded-lg shadow-xl z-60 select-none"
|
||||||
style={{
|
style={{
|
||||||
left: `${panelPosition().x}px`,
|
left: `${panelPosition().x}px`,
|
||||||
top: `${panelPosition().y}px`,
|
top: `${panelPosition().y}px`,
|
||||||
transform: 'none'
|
transform: 'none'
|
||||||
}}
|
}}
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
>
|
>
|
||||||
<div class="card-body p-4">
|
<div class="card-body p-4">
|
||||||
<div class="card-title text-base mb-3 flex justify-between items-center">
|
<div
|
||||||
|
class="card-title text-base mb-3 flex justify-between items-center cursor-move"
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-xs opacity-60">⋮⋮</span>
|
<span class="text-xs opacity-60">⋮⋮</span>
|
||||||
<span>RGB调节</span>
|
<span>RGB调节</span>
|
||||||
<div class="badge badge-secondary badge-outline">可拖拽</div>
|
<div class="badge badge-secondary badge-outline">可拖拽</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-ghost btn-xs" onClick={toggleFullscreen} title="退出全屏">
|
<button class="btn btn-ghost btn-xs cursor-pointer" onClick={toggleFullscreen} title="退出全屏">
|
||||||
<BsFullscreenExit size={14} />
|
<BsFullscreenExit size={14} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,2 +1,24 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@config "../tailwind.config.js";
|
@config "../tailwind.config.js";
|
||||||
|
|
||||||
|
/* Custom responsive styles for small windows */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.container {
|
||||||
|
max-width: 100%;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure LED control panels are responsive */
|
||||||
|
.led-control-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.led-control-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user