This commit is contained in:
2026-08-03 14:06:06 +09:00
parent 107ccd151e
commit 99cfef1004
37 changed files with 3273 additions and 356 deletions
@@ -19,6 +19,7 @@ type ModuleListRowProps<Module extends ModuleListRowItem> = {
module: Module
installing: boolean
controlling: boolean
bulkControlling: boolean
onAddModuleTab: (module: Module) => void
onOpenSource: (module: Module) => void
onOpenParameters: (module: Module) => void
@@ -80,6 +81,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
module,
installing,
controlling,
bulkControlling,
onAddModuleTab,
onOpenSource,
onOpenParameters,
@@ -92,6 +94,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
}: ModuleListRowProps<Module>): React.JSX.Element {
const selectable = module.state !== 'uninstalled'
const busy = installing || controlling
const parameters = Array.isArray(module.parameters) ? module.parameters : []
const stateStyle: Record<ModuleListRowItem['state'], string> = {
running: dark ? 'bg-[#12372d] text-[#72e2b5]' : 'bg-[#dff8ed] text-[#177553]',
stopped: dark ? 'bg-white/8 text-white/45' : 'bg-[#edf0f5] text-[#687284]',
@@ -121,6 +124,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
const installAction = module.state === 'uninstalled'
? { label: '설치', Icon: Download, tone: 'install' as ModuleActionTone }
: { label: '삭제', Icon: Trash2, tone: 'delete' as ModuleActionTone }
const installActionDisabled = busy || (module.state !== 'uninstalled' && module.state !== 'stopped')
return (
<div
@@ -128,7 +132,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
if (selectable) onOpenSource(module)
}}
className={[
'grid h-[58px] grid-cols-[78px_100px_minmax(150px,1fr)_minmax(104px,0.45fr)_minmax(104px,0.45fr)_84px_104px_62px] items-center px-3 text-[12px] transition',
'grid h-[58px] grid-cols-[88px_115px_minmax(220px,1fr)_105px_132px_70px] items-center px-3 text-[12px] transition',
'cursor-default',
selectable
? dark ? 'text-white/70 hover:bg-white/[0.045]' : 'text-[#4f596b] hover:bg-[#f5f7fb]'
@@ -158,37 +162,9 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
dark ? 'text-white/32 hover:text-white/55' : 'text-[#8a94a5] hover:text-[#4f596b]'
].join(' ')}
>
{module.parameters.length ? module.parameters.join(' ') : '파라미터 설정'}
{parameters.length ? parameters.join(' ') : '파라미터 설정'}
</button>
</span>
<span className="flex items-center justify-center gap-1.5">
{module.cpu === null ? (
<span className={dark ? 'text-white/28' : 'text-[#a0a7b4]'}>-</span>
) : (
<>
<span className={['w-10 text-right font-mono text-[10px]', dark ? 'text-white/58' : 'text-[#687284]'].join(' ')}>
{module.cpu.toFixed(module.cpu >= 10 ? 0 : 1)}%
</span>
<span className={['h-1.5 w-[42px] overflow-hidden rounded-full', dark ? 'bg-white/10' : 'bg-[#e2e7f0]'].join(' ')}>
<span className="block h-full rounded-full bg-[#2f8dff]" style={{ width: `${Math.min(100, module.cpu)}%` }} />
</span>
</>
)}
</span>
<span className="flex items-center justify-center gap-1.5">
{module.memory === null ? (
<span className={dark ? 'text-white/28' : 'text-[#a0a7b4]'}>-</span>
) : (
<>
<span className={['w-10 text-right font-mono text-[10px]', dark ? 'text-white/58' : 'text-[#687284]'].join(' ')}>
{module.memory.toFixed(module.memory >= 10 ? 0 : 1)}%
</span>
<span className={['h-1.5 w-[42px] overflow-hidden rounded-full', dark ? 'bg-white/10' : 'bg-[#e2e7f0]'].join(' ')}>
<span className="block h-full rounded-full bg-[#2fbda0]" style={{ width: `${Math.min(100, module.memory)}%` }} />
</span>
</>
)}
</span>
<span className={['text-center', module.runtime === '-' ? (dark ? 'text-white/28' : 'text-[#a0a7b4]') : (dark ? 'font-mono text-white/58' : 'font-mono text-[#687284]')].join(' ')}>
{module.runtime}
</span>
@@ -215,7 +191,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
{controlActions.map(({ label, Icon, tone, action }) => (
(() => {
const canCancelBuild = module.state === 'stopped' && controlling && label === '중지'
const disabled = busy && !canCancelBuild
const disabled = bulkControlling || (busy && !canCancelBuild)
return (
<button
key={`${module.name}-${label}`}
@@ -238,7 +214,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
<span className="flex justify-center">
<button
type="button"
disabled={busy}
disabled={installActionDisabled}
onClick={(event) => {
event.stopPropagation()
if (module.state === 'uninstalled') {
@@ -248,7 +224,7 @@ export default function ModuleListRow<Module extends ModuleListRowItem>({
onDeleteModule(module)
}}
aria-label={installAction.label}
className={actionButtonClass(dark, installAction.tone, busy)}
className={actionButtonClass(dark, installAction.tone, installActionDisabled)}
>
{busy ? <RefreshCw size={14} className="animate-spin" /> : <installAction.Icon size={14} />}
<TooltipLabel dark={dark}>{installAction.label}</TooltipLabel>