Skip to content

Commit b84c838

Browse files
committed
byond switching version stuff
1 parent b4c1162 commit b84c838

File tree

4 files changed

+143
-58
lines changed

4 files changed

+143
-58
lines changed

src/app/layout/PanelTree.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Fragment, useCallback, useRef } from 'react'
1+
import { Fragment, useCallback, useMemo, useRef } from 'react'
22
import { Panel as PanelHost } from './Panel'
33
import { Group, Panel as ResizablePanel, Separator, type Layout } from 'react-resizable-panels'
44
import type { LayoutBranch, LayoutLeaf } from './layoutTypes'
@@ -9,17 +9,26 @@ interface PanelTreeProps {
99
}
1010

1111
export function PanelTree({ node }: PanelTreeProps) {
12-
const { updateBranchSizes } = useLayoutContext()
13-
1412
if (node.type === 'leaf') {
15-
return (
16-
<PanelHost id={node.id} />
17-
)
13+
return <PanelHost id={node.id} />
1814
}
1915

16+
return <PanelTreeBranch node={node} />
17+
}
18+
19+
interface PanelTreeBranchProps {
20+
node: LayoutBranch
21+
}
22+
23+
function PanelTreeBranch({ node }: PanelTreeBranchProps) {
24+
const { updateBranchSizes } = useLayoutContext()
25+
2026
const direction = node.split === 'vertical' ? 'horizontal' : 'vertical'
2127
const isVertical = direction === 'vertical'
22-
const panelIds = node.children.map((_, index) => `${node.id}-${index}`)
28+
const panelIds = useMemo(
29+
() => node.children.map((_, index) => `${node.id}-${index}`),
30+
[node.id, node.children],
31+
)
2332
const lastSizesRef = useRef<number[] | null>(null)
2433

2534
const handleLayoutChanged = useCallback(

src/app/panels/ByondPanel.tsx

Lines changed: 97 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
11
import { useEffect, useMemo, useState } from 'react'
2-
import type { ByondStatus } from '../../services/ByondService'
2+
import { ByondStatus } from '../../services/ByondService'
33
import { byondService } from '../../services/ByondService'
44

55
type StatusMap = Record<string, ByondStatus>
66

7+
export function ByondTitle() {
8+
const refresh = async () => {
9+
window.dispatchEvent(new CustomEvent('byond:refresh'))
10+
}
11+
12+
return (
13+
<div className="flex flex-1 items-center gap-2">
14+
<span>BYOND</span>
15+
<button
16+
type="button"
17+
onClick={() => void refresh()}
18+
className="ml-auto rounded border border-slate-700 px-2 py-1 text-xs text-slate-200 hover:border-slate-500"
19+
title="Refresh versions"
20+
>
21+
Refresh
22+
</button>
23+
</div>
24+
)
25+
}
26+
727
export function ByondPanel() {
8-
const [available, setAvailable] = useState<string[]>([])
28+
const [latestVersion, setLatestVersion] = useState<string | null>(null)
929
const [local, setLocal] = useState<string[]>([])
1030
const [activeVersion, setActiveVersion] = useState<string | null>(null)
1131
const [status, setStatus] = useState<StatusMap>({})
1232
const [error, setError] = useState<string | null>(null)
33+
const [customMajor, setCustomMajor] = useState('')
34+
const [customMinor, setCustomMinor] = useState('')
1335

14-
const topVersions = useMemo(() => available.slice(0, 20), [available])
36+
const displayVersions = useMemo(() => {
37+
const list = [...local]
38+
if (latestVersion && !list.includes(latestVersion)) {
39+
list.push(latestVersion)
40+
}
41+
return list
42+
}, [local, latestVersion])
1543

1644
const refresh = async () => {
1745
try {
18-
const [remoteVersions, localVersions] = await Promise.all([
19-
byondService.getAvailableVersions(),
46+
const [latestVersion, localVersions] = await Promise.all([
47+
byondService.getLatestVersion(),
2048
byondService.getLocalVersions(),
2149
])
22-
setAvailable(remoteVersions)
50+
setLatestVersion(latestVersion)
51+
// custom inputs default to latest remote version
52+
if (latestVersion) {
53+
const parts = latestVersion.split('.')
54+
setCustomMajor((prev) => (prev ? prev : parts[0] ?? ''))
55+
setCustomMinor((prev) => (prev ? prev : parts[1] ?? ''))
56+
}
2357
setLocal(localVersions)
2458
const active = byondService.getActiveVersion()
2559
setActiveVersion(active)
@@ -33,15 +67,15 @@ export function ByondPanel() {
3367

3468
if (!active && localVersions.length > 0) {
3569
const latest = localVersions[0]
36-
setStatus((prev) => ({ ...prev, [latest]: 'loading' }))
70+
setStatus((prev) => ({ ...prev, [latest]: ByondStatus.Loading }))
3771
void byondService
3872
.load(latest, true)
39-
.then(() => {
40-
setActiveVersion(latest)
41-
setStatus((prev) => ({ ...prev, [latest]: 'loaded' }))
42-
})
73+
.then(() => {
74+
setActiveVersion(latest)
75+
setStatus((prev) => ({ ...prev, [latest]: ByondStatus.Installed }))
76+
})
4377
.catch((loadError) => {
44-
setStatus((prev) => ({ ...prev, [latest]: 'error' }))
78+
setStatus((prev) => ({ ...prev, [latest]: ByondStatus.Error }))
4579
setError(loadError instanceof Error ? loadError.message : 'Load failed')
4680
})
4781
}
@@ -54,7 +88,14 @@ export function ByondPanel() {
5488
const id = setTimeout(() => {
5589
void refresh()
5690
}, 0)
57-
return () => clearTimeout(id)
91+
const handleRefresh = () => {
92+
void refresh()
93+
}
94+
window.addEventListener('byond:refresh', handleRefresh)
95+
return () => {
96+
clearTimeout(id)
97+
window.removeEventListener('byond:refresh', handleRefresh)
98+
}
5899
}, [])
59100

60101
useEffect(() => {
@@ -76,18 +117,19 @@ export function ByondPanel() {
76117
}, [])
77118

78119
const handleDownload = async (version: string) => {
79-
setStatus((prev) => ({ ...prev, [version]: 'fetching' }))
120+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Fetching }))
80121
setError(null)
81122
try {
82123
await byondService.downloadVersion(version, (value) => {
83124
if (value >= 1) {
84-
setStatus((prev) => ({ ...prev, [version]: 'fetched' }))
125+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Fetched }))
85126
}
86127
})
87-
const localVersions = await byondService.getLocalVersions()
88-
setLocal(localVersions)
128+
// Ensure local state reflects the newly downloaded version immediately
129+
setLocal((prev) => (prev.includes(version) ? prev : [version, ...prev]))
130+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Fetched }))
89131
} catch (downloadError) {
90-
setStatus((prev) => ({ ...prev, [version]: 'error' }))
132+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Error }))
91133
setError(downloadError instanceof Error ? downloadError.message : 'Download failed')
92134
}
93135
}
@@ -106,29 +148,52 @@ export function ByondPanel() {
106148
}
107149

108150
const handleSetActive = (version: string) => {
109-
setStatus((prev) => ({ ...prev, [version]: 'loading' }))
151+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Loading }))
110152
void byondService
111153
.load(version, true)
112154
.then(() => {
113155
setActiveVersion(version)
114-
setStatus((prev) => ({ ...prev, [version]: 'loaded' }))
156+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Installed }))
115157
})
116158
.catch((loadError) => {
117-
setStatus((prev) => ({ ...prev, [version]: 'error' }))
159+
setStatus((prev) => ({ ...prev, [version]: ByondStatus.Error }))
118160
setError(loadError instanceof Error ? loadError.message : 'Load failed')
119161
})
120162
}
121163

122164
return (
123165
<div className="flex h-full flex-col gap-3 text-sm text-slate-300">
124-
<div className="flex items-center justify-between">
125-
<span className="text-xs text-slate-400">BYOND versions</span>
166+
<div className="flex items-center gap-2">
167+
<label className="text-xs text-slate-400">BYOND Version:</label>
168+
<input
169+
type="number"
170+
min={0}
171+
value={customMajor}
172+
onChange={(e) => setCustomMajor(e.target.value)}
173+
className="w-20 rounded border border-slate-700 bg-transparent px-2 py-1 text-xs text-slate-200"
174+
placeholder="major"
175+
/>
176+
<input
177+
type="number"
178+
min={0}
179+
value={customMinor}
180+
onChange={(e) => setCustomMinor(e.target.value)}
181+
className="w-24 rounded border border-slate-700 bg-transparent px-2 py-1 text-xs text-slate-200"
182+
placeholder="minor"
183+
/>
126184
<button
127185
type="button"
128-
onClick={() => void refresh()}
129-
className="rounded border border-slate-700 px-2 py-1 text-xs text-slate-200 hover:border-slate-500"
186+
onClick={() => {
187+
const version = `${customMajor}.${customMinor}`
188+
if (!customMajor || !customMinor) return
189+
if (local.includes(version)) return
190+
void handleDownload(version)
191+
}}
192+
disabled={!customMajor || !customMinor || local.includes(`${customMajor}.${customMinor}`)}
193+
title={local.includes(`${customMajor}.${customMinor}`) ? 'Version already installed' : undefined}
194+
className="rounded border border-slate-700 px-2 py-1 text-xs text-slate-200 hover:border-slate-500 disabled:opacity-50"
130195
>
131-
Refresh
196+
Fetch
132197
</button>
133198
</div>
134199

@@ -144,10 +209,11 @@ export function ByondPanel() {
144209
</tr>
145210
</thead>
146211
<tbody>
147-
{topVersions.map((version) => {
212+
{displayVersions.map((version) => {
148213
const isLocal = local.includes(version)
149214
const isActive = activeVersion === version
150-
const versionStatus = status[version] ?? byondService.getStatus(version)
215+
const isLatest = version === latestVersion
216+
const versionStatus = status[version] ?? (isLocal ? ByondStatus.Installed : ByondStatus.Idle)
151217

152218
return (
153219
<tr key={version} className="border-t border-slate-800">
@@ -180,7 +246,9 @@ export function ByondPanel() {
180246
<button
181247
type="button"
182248
onClick={() => void handleDelete(version)}
183-
className="rounded border border-slate-700 px-2 py-1 text-[11px] text-slate-200 hover:border-slate-500"
249+
disabled={isLatest || isActive}
250+
title={isLatest || isActive ? 'Cannot delete the latest or active version' : undefined}
251+
className="rounded border border-slate-700 px-2 py-1 text-[11px] text-slate-200 hover:border-slate-500 disabled:opacity-50"
184252
>
185253
Delete
186254
</button>

src/app/panels/PanelRegistry.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ReactNode } from 'react'
22
import { PanelId } from '../layout/layoutTypes'
3-
import { ByondPanel } from './ByondPanel'
3+
import { ByondPanel, ByondTitle } from './ByondPanel'
44
import { ConsolePanel } from './ConsolePanel'
55
import { ControllerPanel, ControllerTitle } from './ControllerPanel'
66
import { EditorPanel } from './EditorPanel'
@@ -29,7 +29,7 @@ export const PanelRegistry: Record<PanelId, PanelDescriptor> = {
2929
render: () => <OutputPanel />,
3030
},
3131
[PanelId.Byond]: {
32-
title: 'BYOND',
32+
title: <ByondTitle />,
3333
render: () => <ByondPanel />,
3434
},
3535
}

0 commit comments

Comments
 (0)