@@ -64,10 +64,16 @@ import kotlinx.coroutines.cancel
6464import kotlinx.coroutines.delay
6565import kotlinx.coroutines.isActive
6666import kotlinx.coroutines.launch
67+ import kotlinx.coroutines.runBlocking
6768import kotlinx.coroutines.withContext
6869import org.zeroturnaround.exec.ProcessExecutor
6970import java.awt.Component
7071import java.awt.Dimension
72+ import java.awt.event.MouseEvent
73+ import java.awt.event.MouseListener
74+ import java.awt.event.MouseMotionListener
75+ import java.awt.font.TextAttribute
76+ import java.awt.font.TextAttribute.UNDERLINE_ON
7177import javax.swing.Icon
7278import javax.swing.JTable
7379import javax.swing.JTextField
@@ -80,6 +86,8 @@ private const val CODER_URL_KEY = "coder-url"
8086
8187private const val SESSION_TOKEN = " session-token"
8288
89+ private const val MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW = " MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW"
90+
8391class CoderWorkspacesStepView (val enableNextButtonCallback : (Boolean ) -> Unit ) : CoderWorkspacesWizardStep, Disposable {
8492 private val cs = CoroutineScope (Dispatchers .Main )
8593 private var localWizardModel = CoderWorkspacesWizardModel ()
@@ -123,6 +131,49 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
123131 }
124132 updateWorkspaceActions()
125133 }
134+
135+ addMouseListener(object : MouseListener {
136+ override fun mouseClicked (e : MouseEvent ? ) {
137+ if (e?.source is TableView <* >) {
138+ val tblView = e.source as TableView <WorkspaceAgentModel >
139+ val col = tblView.selectedColumn
140+ val workspace = tblView.selectedObject
141+
142+ if (col == 2 && workspace != null ) {
143+ BrowserUtil .browse(coderClient.coderURL.toURI().resolve(" /templates/${workspace.templateName} " ))
144+ }
145+ }
146+ }
147+
148+ override fun mousePressed (e : MouseEvent ? ) {
149+ }
150+
151+ override fun mouseReleased (e : MouseEvent ? ) {
152+ }
153+
154+ override fun mouseEntered (e : MouseEvent ? ) {
155+ }
156+
157+ override fun mouseExited (e : MouseEvent ? ) {
158+ }
159+ })
160+ addMouseMotionListener(object : MouseMotionListener {
161+ override fun mouseMoved (e : MouseEvent ? ) {
162+ if (e?.source is TableView <* >) {
163+ val tblView = e.source as TableView <WorkspaceAgentModel >
164+ val row = tblView.rowAtPoint(e.point)
165+ if (tblView.columnAtPoint(e.point) == 2 && row in 0 until tblView.listTableModel.rowCount) {
166+ tblView.putClientProperty(MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW , row)
167+ } else {
168+ tblView.putClientProperty(MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW , - 1 )
169+ }
170+ }
171+
172+ }
173+
174+ override fun mouseDragged (e : MouseEvent ? ) {
175+ }
176+ })
126177 }
127178
128179 private val goToDashboardAction = GoToDashboardAction ()
@@ -349,12 +400,20 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
349400
350401 val authTask = object : Task .Modal (null , CoderGatewayBundle .message(" gateway.connector.view.coder.workspaces.cli.downloader.dialog.title" ), false ) {
351402 override fun run (pi : ProgressIndicator ) {
352-
353403 pi.apply {
354404 isIndeterminate = false
355- text = " Downloading coder cli ..."
405+ text = " Retrieving Workspaces ..."
356406 fraction = 0.1
357407 }
408+ runBlocking {
409+ loadWorkspaces()
410+ }
411+
412+ pi.apply {
413+ isIndeterminate = false
414+ text = " Downloading Coder CLI..."
415+ fraction = 0.3
416+ }
358417
359418 cliManager.downloadCLI()
360419 if (getOS() != OS .WINDOWS ) {
@@ -363,7 +422,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
363422 logger.info(" chmod +x ${cliManager.localCli.toAbsolutePath()} $chmodOutput " )
364423 }
365424 pi.apply {
366- text = " Configuring coder cli ..."
425+ text = " Configuring Coder CLI ..."
367426 fraction = 0.5
368427 }
369428
@@ -374,7 +433,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
374433 logger.info(" Result of `${localWizardModel.localCliPath} config-ssh --yes --use-previous-options`: $sshConfigOutput " )
375434
376435 pi.apply {
377- text = " Remove old coder cli versions..."
436+ text = " Remove old Coder CLI versions..."
378437 fraction = 0.9
379438 }
380439 cliManager.removeOldCli()
@@ -417,35 +476,50 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
417476
418477 poller = cs.launch {
419478 while (isActive) {
420- loadWorkspaces()
421479 delay(5000 )
480+ loadWorkspaces()
422481 }
423482 }
424483 }
425484
426485 private suspend fun loadWorkspaces () {
427- val workspaceList = withContext(Dispatchers .IO ) {
486+ withContext(Dispatchers .IO ) {
487+ val timeBeforeRequestingWorkspaces = System .currentTimeMillis()
428488 try {
429- return @withContext coderClient.workspaces().collectAgents()
489+ val ws = coderClient.workspaces()
490+ val timeAfterRequestingWorkspaces = System .currentTimeMillis()
491+ logger.info(" Retrieving the workspaces took: ${timeAfterRequestingWorkspaces - timeBeforeRequestingWorkspaces} millis" )
492+ ws.resolveAndDisplayAgents()
430493 } catch (e: Exception ) {
431494 logger.error(" Could not retrieve workspaces for ${coderClient.me.username} on ${coderClient.coderURL} . Reason: $e " )
432- emptyList()
433495 }
434496 }
497+ }
435498
436- withContext(Dispatchers .Main ) {
437- val selectedWorkspace = tableOfWorkspaces.selectedObject?.name
438- listTableModelOfWorkspaces.items = workspaceList
439- if (selectedWorkspace != null ) {
440- tableOfWorkspaces.selectItem(selectedWorkspace)
499+ private fun List<Workspace>.resolveAndDisplayAgents () {
500+ this .forEach { workspace ->
501+ cs.launch(Dispatchers .IO ) {
502+ val timeBeforeRequestingAgents = System .currentTimeMillis()
503+ workspace.agentModels().forEach { am ->
504+ withContext(Dispatchers .Main ) {
505+ val selectedWorkspace = tableOfWorkspaces.selectedObject?.name
506+ if (listTableModelOfWorkspaces.indexOf(am) >= 0 ) {
507+ val index = listTableModelOfWorkspaces.indexOf(am)
508+ listTableModelOfWorkspaces.setItem(index, am)
509+ } else {
510+ listTableModelOfWorkspaces.addRow(am)
511+ }
512+ if (selectedWorkspace != null ) {
513+ tableOfWorkspaces.selectItem(selectedWorkspace)
514+ }
515+ }
516+ }
517+ val timeAfterRequestingAgents = System .currentTimeMillis()
518+ logger.info(" Retrieving the agents for ${workspace.name} took: ${timeAfterRequestingAgents - timeBeforeRequestingAgents} millis" )
441519 }
442520 }
443521 }
444522
445- private fun List<Workspace>.collectAgents (): List <WorkspaceAgentModel > {
446- return this .flatMap { it.agentModels() }.toList()
447- }
448-
449523 private fun Workspace.agentModels (): List <WorkspaceAgentModel > {
450524 return try {
451525 val agents = coderClient.workspaceAgentsByTemplate(this )
@@ -569,9 +643,10 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
569643 override fun isCenterAlignment () = true
570644
571645 override fun getTableCellRendererComponent (table : JTable ? , value : Any? , selected : Boolean , focus : Boolean , row : Int , column : Int ): Component {
572- return super .getTableCellRendererComponent(table, value, selected, focus, row, column).apply {
573- border = JBUI .Borders .empty(10 , 10 )
646+ super .getTableCellRendererComponent(table, value, selected, focus, row, column).apply {
647+ border = JBUI .Borders .empty(10 )
574648 }
649+ return this
575650 }
576651 }
577652 }
@@ -590,6 +665,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
590665 text = value
591666 }
592667 font = JBFont .h3().asBold()
668+ border = JBUI .Borders .empty()
593669 return this
594670 }
595671 }
@@ -602,13 +678,27 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
602678 }
603679
604680 override fun getRenderer (item : WorkspaceAgentModel ? ): TableCellRenderer {
681+ val simpleH3 = JBFont .h3()
682+
683+ val h3AttributesWithUnderlining = simpleH3.attributes as MutableMap <TextAttribute , Any >
684+ h3AttributesWithUnderlining[TextAttribute .UNDERLINE ] = UNDERLINE_ON
685+ val underlinedH3 = JBFont .h3().deriveFont(h3AttributesWithUnderlining)
605686 return object : DefaultTableCellRenderer () {
606687 override fun getTableCellRendererComponent (table : JTable , value : Any , isSelected : Boolean , hasFocus : Boolean , row : Int , column : Int ): Component {
607688 super .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column)
608689 if (value is String ) {
609690 text = value
610691 }
611- font = JBFont .h3()
692+ border = JBUI .Borders .empty()
693+
694+ if (table.getClientProperty(MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW ) != null ) {
695+ val mouseOverRow = table.getClientProperty(MOUSE_OVER_TEMPLATE_NAME_COLUMN_ON_ROW ) as Int
696+ if (mouseOverRow >= 0 && mouseOverRow == row) {
697+ font = underlinedH3
698+ return this
699+ }
700+ }
701+ font = simpleH3
612702 return this
613703 }
614704 }
@@ -628,6 +718,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
628718 text = value
629719 }
630720 font = JBFont .h3()
721+ border = JBUI .Borders .empty()
631722 return this
632723 }
633724 }
@@ -647,6 +738,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
647738 text = value
648739 }
649740 font = JBFont .h3()
741+ border = JBUI .Borders .empty()
650742 foreground = (table.model as ListTableModel <WorkspaceAgentModel >).getRowValue(row).statusColor()
651743 return this
652744 }
0 commit comments