@@ -188,31 +188,31 @@ function ContainerTable() {
188188 < >
189189 < HostWarning />
190190 { containers . length > 0 ? (
191- < table className = "w-full text-left" >
192- < thead >
193- < tr >
194- < th className = { cx ( tableHeaderClass , "w-1/3" ) } > Container</ th >
195- < th className = { cx ( tableHeaderClass , "w-1/2" ) } > Tailscale URL</ th >
196- < th className = { tableHeaderClass } />
197- </ tr >
198- </ thead >
199- < tbody >
200- { containers . map ( ( c ) => (
201- < ContainerRow
202- key = { c . Id }
203- container = { c }
204- hostIP = { hostIP }
205- host = { host }
206- />
207- ) ) }
208- </ tbody >
209- </ table >
191+ < >
192+ < table className = "w-full text-left" >
193+ < thead >
194+ < tr >
195+ < th className = { cx ( tableHeaderClass , "w-1/3" ) } > Container</ th >
196+ < th className = { cx ( tableHeaderClass , "w-1/2" ) } > Tailscale URL</ th >
197+ < th className = { tableHeaderClass } />
198+ </ tr >
199+ </ thead >
200+ < tbody >
201+ { containers . map ( ( c ) => (
202+ < ContainerRow
203+ key = { c . Id }
204+ container = { c }
205+ hostIP = { hostIP }
206+ host = { host }
207+ />
208+ ) ) }
209+ </ tbody >
210+ </ table >
211+ </ >
210212 ) : (
211213 < div className = "text-center py-12" >
212214 < p className = "text-xl font-medium mb-1" > No containers are running.</ p >
213- < p className = "text-gray-500 dark:text-gray-400" >
214- Go to the Containers tab to get started.
215- </ p >
215+ < p className = "help-text" > Go to the Containers tab to get started.</ p >
216216 </ div >
217217 ) }
218218 </ >
@@ -233,6 +233,9 @@ function ContainerRow(props: {
233233 const tailscaleURL = `http://${ tailscaleIPPort } /`
234234 const tailscaleIPUrl = `http://${ hostIP } :${ publicPort ?. PublicPort } /` . trim ( )
235235
236+ const hasPublicPorts = container . Ports . some ( ( p ) => p . PublicPort )
237+ const online = container . State === "running" && hasPublicPorts
238+
236239 const handleCopyClick = useCallback ( ( ) => {
237240 copyToClipboard ( tailscaleURL )
238241 setShowTooltip ( true )
@@ -256,9 +259,8 @@ function ContainerRow(props: {
256259 < td className = { cx ( tableCellClass , "flex items-center" ) } >
257260 < Icon
258261 className = { cx ( "mr-3" , {
259- "text-emerald-400 dark:text-green-300" :
260- container . State === "running" ,
261- "text-gray-600" : container . State !== "running" ,
262+ "text-emerald-400 dark:text-green-300" : online ,
263+ "text-gray-400 dark:text-gray-600" : ! online ,
262264 } ) }
263265 name = "container"
264266 size = "24"
@@ -268,31 +270,49 @@ function ContainerRow(props: {
268270 </ span >
269271 </ td >
270272 < td className = { cx ( tableCellClass , "min-w-0" ) } >
271- < Tooltip
272- asChild
273- content = {
274- copied || persistTooltipCopy ? "Copied!" : "Copy URL to clipboard"
275- }
276- closeOnClick = { false }
277- open = { showTooltip || copied }
278- onOpenChange = { setShowTooltip }
279- >
280- < button
281- className = { cx ( tableButtonClass , "flex items-center min-w-0" ) }
282- onClick = { handleCopyClick }
273+ { hasPublicPorts ? (
274+ < Tooltip
275+ asChild
276+ content = {
277+ copied || persistTooltipCopy ? "Copied!" : "Copy URL to clipboard"
278+ }
279+ closeOnClick = { false }
280+ open = { showTooltip || copied }
281+ onOpenChange = { setShowTooltip }
283282 >
284- < span className = "truncate" > { tailscaleIPPort } </ span >
285- < Icon
286- className = "ml-1.5 text-gray-500 dark:text-gray-400"
287- name = { copied ? "check" : "clipboard" }
288- size = "14"
289- />
290- </ button >
291- </ Tooltip >
283+ < button
284+ className = { cx ( tableButtonClass , "flex items-center min-w-0" ) }
285+ onClick = { handleCopyClick }
286+ >
287+ < span className = "truncate" > { tailscaleIPPort } </ span >
288+ < Icon
289+ className = "ml-1.5 text-gray-500 dark:text-gray-400"
290+ name = { copied ? "check" : "clipboard" }
291+ size = "14"
292+ />
293+ </ button >
294+ </ Tooltip >
295+ ) : (
296+ < Tooltip
297+ content = {
298+ < >
299+ This container has no public ports. Expose ports using{ " " }
300+ < code className = "code" > docker run</ code > with the{ " " }
301+ < code className = "code" > -p</ code > flag.
302+ </ >
303+ }
304+ >
305+ < div className = "flex items-center min-w-0 help-text" >
306+ < Icon name = "error" size = "14" />
307+ < span className = "truncate ml-1.5" > No public ports</ span >
308+ </ div >
309+ </ Tooltip >
310+ ) }
292311 </ td >
293312 < td className = { cx ( "space-x-3 text-right" , tableButtonCellClass ) } >
294313 < Tooltip asChild content = "Open URL in browser" >
295314 < button
315+ disabled = { ! online }
296316 className = { cx ( tableIconButtonClass ) }
297317 onClick = { ( ) => openBrowser ( tailscaleURL ) }
298318 >
@@ -307,7 +327,10 @@ function ContainerRow(props: {
307327 </ button >
308328 }
309329 >
310- < DropdownMenu . Item onSelect = { ( ) => copyToClipboard ( tailscaleIPUrl ) } >
330+ < DropdownMenu . Item
331+ disabled = { ! online }
332+ onSelect = { ( ) => copyToClipboard ( tailscaleIPUrl ) }
333+ >
311334 Copy IP address
312335 </ DropdownMenu . Item >
313336 < DropdownMenu . Separator />
@@ -332,7 +355,7 @@ const tableHeaderClass = cx(
332355const tableCellClass = cx ( tablePadding , borderColor )
333356const tableButtonCellClass = cx ( "px-2" , borderColor )
334357const tableIconButtonClass =
335- "text-gray-600 dark:text-gray-300 focus:outline-none hover:bg-[rgba(31,41,55,0.05)] dark:hover:bg-[rgba(255,255,255,0.05)] focus-visible:bg-[rgba(31,41,55,0.05)] dark:focus-visible:bg-[rgba(255,255,255,0.05)] px-2 py-2 rounded"
358+ "text-gray-600 dark:text-gray-300 focus:outline-none enabled: hover:bg-[rgba(31,41,55,0.05)] enabled: dark:hover:bg-[rgba(255,255,255,0.05)] focus-visible:bg-[rgba(31,41,55,0.05)] dark:focus-visible:bg-[rgba(255,255,255,0.05)] px-2 py-2 rounded disabled:opacity-50 "
336359const tableButtonClass = "focus:outline-none focus-visible:ring"
337360
338361const hostWarningSelector = ( state : State ) => ( {
0 commit comments