Skip to content

Commit 0aca4d9

Browse files
authored
Update Interactive mode to render different user interfaces based on the environment it's running in (#182)
When running `ie interactive` on azure, Interactive mode will render a simplified user interface that mimics entering the commands with a prompt and displaying the output.
1 parent efbff20 commit 0aca4d9

File tree

4 files changed

+77
-37
lines changed

4 files changed

+77
-37
lines changed

internal/engine/engine.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package engine
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/Azure/InnovationEngine/internal/az"
78
"github.com/Azure/InnovationEngine/internal/engine/environments"
@@ -78,7 +79,21 @@ func (e *Engine) InteractWithScenario(scenario *Scenario) error {
7879
}
7980

8081
program = tea.NewProgram(model, tea.WithAltScreen(), tea.WithMouseCellMotion())
81-
_, err = program.Run()
82+
83+
var finalModel tea.Model
84+
var ok bool
85+
finalModel, err = program.Run()
86+
87+
model, ok = finalModel.(InteractiveModeModel)
88+
89+
if environments.EnvironmentsAzure == e.Configuration.Environment {
90+
if !ok {
91+
return fmt.Errorf("failed to cast tea.Model to InteractiveModeModel")
92+
}
93+
94+
logging.GlobalLogger.Info("Writing session output to stdout")
95+
fmt.Println(strings.Join(model.commandLines, "\n"))
96+
}
8297

8398
switch e.Configuration.Environment {
8499
case environments.EnvironmentsAzure, environments.EnvironmentsOCD:

internal/engine/environments/azure.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func ReportAzureStatus(status AzureDeploymentStatus, environment string) {
7979
} else {
8080
// We add these strings to the output so that the portal can find and parse
8181
// the JSON status.
82-
ocdStatus := fmt.Sprintf("ie_us%sie_ue\n", statusJson)
82+
ocdStatus := fmt.Sprintf("ie_us%sie_ue", statusJson)
8383
fmt.Println(ui.OcdStatusUpdateStyle.Render(ocdStatus))
8484
}
8585
}

internal/engine/interactive.go

+50-34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package engine
22

33
import (
44
"fmt"
5+
"strings"
56
"time"
67

78
"github.com/Azure/InnovationEngine/internal/az"
@@ -41,9 +42,10 @@ type CodeBlockState struct {
4142
}
4243

4344
type interactiveModeComponents struct {
44-
paginator paginator.Model
45-
stepViewport viewport.Model
46-
outputViewport viewport.Model
45+
paginator paginator.Model
46+
stepViewport viewport.Model
47+
outputViewport viewport.Model
48+
azureCLIViewport viewport.Model
4749
}
4850

4951
type InteractiveModeModel struct {
@@ -62,6 +64,7 @@ type InteractiveModeModel struct {
6264
scenarioCompleted bool
6365
components interactiveModeComponents
6466
ready bool
67+
commandLines []string
6568
}
6669

6770
// Initialize the intractive mode model
@@ -91,11 +94,13 @@ func initializeComponents(model InteractiveModeModel, width, height int) interac
9194

9295
stepViewport := viewport.New(width, 4)
9396
outputViewport := viewport.New(width, 2)
97+
azureCLIViewport := viewport.New(width, height)
9498

9599
components := interactiveModeComponents{
96-
paginator: p,
97-
stepViewport: stepViewport,
98-
outputViewport: outputViewport,
100+
paginator: p,
101+
stepViewport: stepViewport,
102+
outputViewport: outputViewport,
103+
azureCLIViewport: azureCLIViewport,
99104
}
100105

101106
components.updateViewportHeight(height)
@@ -207,6 +212,7 @@ func (components *interactiveModeComponents) updateViewportHeight(terminalHeight
207212

208213
components.stepViewport.Height = stepViewportHeight
209214
components.outputViewport.Height = outputViewportHeight
215+
components.azureCLIViewport.Height = terminalHeight - 1
210216
}
211217

212218
// Updates the intractive mode model
@@ -225,6 +231,7 @@ func (model InteractiveModeModel) Update(message tea.Msg) (tea.Model, tea.Cmd) {
225231
} else {
226232
model.components.stepViewport.Width = message.Width
227233
model.components.outputViewport.Width = message.Width
234+
model.components.azureCLIViewport.Width = message.Width
228235
model.components.updateViewportHeight(message.Height)
229236
}
230237

@@ -255,10 +262,18 @@ func (model InteractiveModeModel) Update(message tea.Msg) (tea.Model, tea.Cmd) {
255262
model.resourceGroupName = tmpResourceGroup
256263
}
257264
}
265+
model.commandLines = append(model.commandLines, codeBlockState.StdOut)
258266

259267
// Increment the codeblock and update the viewport content.
260268
model.currentCodeBlock++
261269

270+
if model.currentCodeBlock < len(model.codeBlockState) {
271+
nextCommand := model.codeBlockState[model.currentCodeBlock].CodeBlock.Content
272+
nextLanguage := model.codeBlockState[model.currentCodeBlock].CodeBlock.Language
273+
274+
model.commandLines = append(model.commandLines, ui.CommandPrompt(nextLanguage)+nextCommand)
275+
}
276+
262277
// Only increment the step for azure if the step name has changed.
263278
nextCodeBlockState := model.codeBlockState[model.currentCodeBlock]
264279

@@ -301,6 +316,7 @@ func (model InteractiveModeModel) Update(message tea.Msg) (tea.Model, tea.Cmd) {
301316
codeBlockState.Success = false
302317

303318
model.codeBlockState[step] = codeBlockState
319+
model.commandLines = append(model.commandLines, codeBlockState.StdErr)
304320

305321
// Report the error
306322
model.executingCommand = false
@@ -381,6 +397,8 @@ func (model InteractiveModeModel) Update(message tea.Msg) (tea.Model, tea.Cmd) {
381397
model.components.outputViewport.SetContent(block.StdErr)
382398
}
383399

400+
model.components.azureCLIViewport.SetContent(strings.Join(model.commandLines, "\n"))
401+
384402
// Update all the viewports and append resulting commands.
385403
var command tea.Cmd
386404

@@ -392,6 +410,9 @@ func (model InteractiveModeModel) Update(message tea.Msg) (tea.Model, tea.Cmd) {
392410
model.components.outputViewport, command = model.components.outputViewport.Update(message)
393411
commands = append(commands, command)
394412

413+
model.components.azureCLIViewport, command = model.components.azureCLIViewport.Update(message)
414+
commands = append(commands, command)
415+
395416
return model, tea.Batch(commands...)
396417
}
397418

@@ -419,44 +440,33 @@ func (model InteractiveModeModel) helpView() string {
419440

420441
// Renders the interactive mode model.
421442
func (model InteractiveModeModel) View() string {
443+
// When running in the portal, we only want to show the Azure CLI viewport
444+
// which mimics a command line interface during execution.
445+
if model.environment == "azure" {
446+
return model.components.azureCLIViewport.View()
447+
}
448+
422449
scenarioTitle := ui.ScenarioTitleStyle.Width(model.width).
423450
Align(lipgloss.Center).
424451
Render(model.scenarioTitle)
425-
var stepTitle string
426-
var stepView string
427-
var stepSection string
428-
stepTitle = ui.StepTitleStyle.Render(
452+
453+
border := lipgloss.NewStyle().
454+
Width(model.components.stepViewport.Width - 2).
455+
Border(lipgloss.NormalBorder())
456+
457+
stepTitle := ui.StepTitleStyle.Render(
429458
fmt.Sprintf(
430459
"Step %d - %s",
431460
model.currentCodeBlock+1,
432461
model.codeBlockState[model.currentCodeBlock].StepName,
433462
),
434463
)
464+
stepView := border.Render(model.components.stepViewport.View())
465+
stepSection := fmt.Sprintf("%s\n%s\n\n", stepTitle, stepView)
435466

436-
border := lipgloss.NewStyle().
437-
Width(model.components.stepViewport.Width - 2)
438-
// Border(lipgloss.NormalBorder())
439-
440-
stepView = border.Render(model.components.stepViewport.View())
441-
442-
if model.environment != "azure" {
443-
stepSection = fmt.Sprintf("%s\n%s\n\n", stepTitle, stepView)
444-
} else {
445-
stepSection = fmt.Sprintf("%s\n%s\n", stepTitle, stepView)
446-
}
447-
448-
var outputTitle string
449-
var outputView string
450-
var outputSection string
451-
if model.environment != "azure" {
452-
outputTitle = ui.StepTitleStyle.Render("Output")
453-
outputView = border.Render(model.components.outputViewport.View())
454-
outputSection = fmt.Sprintf("%s\n%s\n\n", outputTitle, outputView)
455-
} else {
456-
outputTitle = ""
457-
outputView = ""
458-
outputSection = ""
459-
}
467+
outputTitle := ui.StepTitleStyle.Render("Output")
468+
outputView := border.Render(model.components.outputViewport.View())
469+
outputSection := fmt.Sprintf("%s\n%s\n\n", outputTitle, outputView)
460470

461471
paginator := lipgloss.NewStyle().
462472
Width(model.width).
@@ -532,6 +542,11 @@ func NewInteractiveModeModel(
532542
azureStatus.AddStep(fmt.Sprintf("%d. %s", stepNumber+1, step.Name), azureCodeBlocks)
533543
}
534544

545+
language := codeBlockState[0].CodeBlock.Language
546+
commandLines := []string{
547+
ui.CommandPrompt(language) + codeBlockState[0].CodeBlock.Content,
548+
}
549+
535550
return InteractiveModeModel{
536551
scenarioTitle: title,
537552
commands: InteractiveModeCommands{
@@ -562,5 +577,6 @@ func NewInteractiveModeModel(
562577
environment: engine.Configuration.Environment,
563578
scenarioCompleted: false,
564579
ready: false,
580+
commandLines: commandLines,
565581
}, nil
566582
}

internal/ui/text.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,18 @@ var (
4444
b.Left = "┤"
4545
return InteractiveModeStepTitleStyle.Copy().BorderStyle(b)
4646
}().Foreground(lipgloss.Color("#fff"))
47+
48+
promptTextStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#6CB6FF"))
49+
promptDollarStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#32CD32"))
4750
)
4851

52+
// Command prompt for interactive environments
53+
func CommandPrompt(language string) string {
54+
promptText := promptTextStyle.Render(language)
55+
promptDollar := promptDollarStyle.Render("$")
56+
return promptText + ":" + promptDollar + " "
57+
}
58+
4959
// Indents a multi-line command to be nested under the first line of the
5060
// command.
5161
func IndentMultiLineCommand(content string, indentation int) string {
@@ -56,7 +66,6 @@ func IndentMultiLineCommand(content string, indentation int) string {
5666
} else if strings.TrimSpace(lines[i]) != "" {
5767
lines[i] = strings.Repeat(" ", indentation) + lines[i]
5868
}
59-
6069
}
6170
return strings.Join(lines, "\n")
6271
}

0 commit comments

Comments
 (0)