Posey's Tips & Tricks

PowerShell Desktop AI Revisited, Part 1: Code Refinement

Let's revisits my PowerShell-based desktop AI project, this time with a revamped script for improved performance.

In a recent series of blog posts, I created what might best be described as a PowerShell front end for Ollama. For anyone who might not be familiar with Ollama, it's a tool that allows you to run large language model AI on your own hardware, as opposed to you needing to query a cloud service such as ChatGPT.

The PowerShell script that I created as a part of that series certainly got the job done, but I always thought of it as being version 1.0. Even as I was writing that initial script, I was thinking of a million features that I would like to add and ways that the script should be improved. Even so, I knew that a “version 2.0” script would be really long and complicated and so I decided to create the updated script as a part of a completely separate series. In retrospect, that turned out to be a good decision, because my new script turned out to be quite a bit longer and more elaborate than I had anticipated. You can see what the new script looks like in Figure 1.

[Click on image for larger view.] Figure 1. This is my revised Large Language Model chatbot script.

At first glance, this script probably does not look all that different from the one that I created in my original blog series. However, there are some major differences between the two scripts.

One of the most significant differences is that my new script gives you the ability to use a variety of Large Language Models, all at the same time!

As previously noted, my script uses Ollama as the Large Language Model host. Ollama supports a variety of different Large Language Models, from several different vendors. Each of these models has its own unique strengths and weaknesses. Some models are good at trivia and general knowledge. Others are better suited to reasoning through complex problems. Still other Large Language Models are designed to write code.

Ollama will allow you to download as many models as you like, but you can only run one model at a time. My script allows you to switch from one model to another on demand. The really cool thing about this capability is that switching models does not cause the chat history to be cleared. In other words, your chat history, which you can export to a text file if you like, can contain outputs from a variety of different AI models.

If you look back at the previous figure, you will notice that there is a drop down menu in the top left corner. You can select from any of the models that you have downloaded and the next query that you perform will use your selected model. The query responses shown in the output pane will always tell you which model was used to produce the response. It is worth noting however, that whenever you switch models, the next query will take a bit longer than normal because of the time required to load the new model.

If you look at Figure 2, you can see that my output window contains responses from two different models.

[Click on image for larger view.] Figure 2. The output contains responses from more than one Large Language Model.

The output contains responses from more than one Large Language Model.

This brings me to the next major enhancement that I made to my script. The empty text box on the right side of the window is a full featured text editor. The reason why I created a text editor was because I often use AI for research purposes. It can be handy to copy the portion of a response that meets your needs, and ignore the rest. Likewise, you can add your own notes to anything that you might have copied to the editor. As you add content to the editor, you can change fonts, text color, highlights, and anything else that you want. You can see a couple of examples in Figures 3 and 4.

[Click on image for larger view.] Figure 3. The editor panel allows you to change fonts.
[Click on image for larger view.] Figure 4. You can also highlight text and change the text color.

You can save your notes as a text file or as an RTF file. And if after exiting the script, you think of something else that you wanted to include in your notes, you can use the Open function, found on the File menu, to reopen your notes page.

The editor can also be helpful for coding. To show you what I mean, take a look at Figure 5. I asked one of the Large Language Models to write a PowerShell script. As you can see in the figure, the output contains more than just the script. There is also text describing what the script does and how to use the script. That being the case, I can copy the actual code to the editor and then refine the code before eventually saving it.

[Click on image for larger view.] Figure 5. You can extract code from the AI output and paste it directly into the editor.

This brings up another important point. I have created a series of context menus (shortcut menus) that you can use to copy and paste text from one window to the other. Both windows also contain a Find function, and the Editor window actually has find and replace capabilities. You can see what that looks like in Figure 6.

[Click on image for larger view.] Figure 6. You can find and replace text within the editor window.

Although you can't see it in any of the screen captures, there is another major enhancement to my script. My original script was built with the assumption that Ollama would be running locally on the same machine as the script. My new script can be used when Ollama is running on a different machine.

Perhaps more importantly, the new script delivers better overall performance. With the way that my original script was written, Ollama had to reload the model every time that you issued a query, meaning that responses were slow. The new script initially loads a model, but allows that model to remain in memory for subsequent queries, unless you switch to a different model, That means that the first query with a given model may be slow, but any additional queries should complete relatively quickly.

As you can imagine, there was a lot of work that went into writing this script. Although most of my time was spent developing the GUI interface and the various menu options, I don't really want to get into a deep discussion of the GUI. I would rather focus my attention on the script's AI related aspects. In Part 2, I am going to talk about what's involved in connecting to a remote Ollama server and downloading the list of installed models. I plan to round out the series in Part 3 by explaining how the new query process works. At the conclusion of that article, I will provide you with the full source code for my script.

For those who are interested in how the GUI works, I would encourage you to perform a Web search for the information that you are looking for. I have written a huge number of PowerShell GUI related articles on this and other sites.

One thing that I will tell you is that because of the complexity involved in this script's GUI, I wrote the GUI first and then added in the various AI elements. I am including that code below, because it may be useful if someone wants to build their own PowerShell text editor or something like that. Again, I will provide the script's full source code at the end of Part 3. Here is the code for the GUI:

Add-Type -AssemblyName System.Windows.Forms

# Create the main form
$Form = New-Object System.Windows.Forms.Form
$Form.Text = "Poseys Large Language Model Text Editor"
$Form.Size = New-Object System.Drawing.Size(1920, 1080)

 

#############################################################
##           Input Panel (top panel)                       ##
#############################################################

 

# Create a top panel for the input box, model drop down, submit, and edit buttons
$TopPanel = New-Object System.Windows.Forms.Panel
$TopPanel.Location = New-Object System.Drawing.Point(10,10)
$TopPanel.Size = New-Object System.Drawing.Size(900,300)

# Create the input label
$InputLabel = New-Object System.Windows.Forms.Label
$InputLabel.Text = "Input and Model Selection"
$InputLabel.Location = New-Object System.Drawing.Point(0,0)
$InputLabel.Size = New-Object System.Drawing.Size(900, 50)
$InputLabel.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$InputLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$InputLabel.BackColor = [System.Drawing.Color]::LightBlue
$InputLabel.ForeColor = [System.Drawing.Color]::Black

# Create Model Combo Box
$ModelComboBox = New-Object System.Windows.Forms.ComboBox
$ModelComboBox.Location = New-Object System.Drawing.Point(0,55)
$ModelComboBox.Size = New-Object System.Drawing.Size(250,150)
$ModelComboBox.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$ModelComboBox.text = ""

# Define the array of choices and add them to the drop down
$Choices = @("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
$Choices | ForEach-Object {[void] $ModelComboBox.Items.Add($_)}

# Select the default choice
$ModelComboBox.SelectedIndex = 0

# Create the Submit Button
$SubmitButton = New-Object System.Windows.Forms.Button
$SubmitButton.Font = New-Object System.Drawing.Font("Arial", 12)
$SubmitButton.Location = New-Object System.Drawing.Point(300,55)
$SubmitButton.Size = New-Object System.Drawing.Size(75,30)
$SubmitButton.Text = "Submit"

 

# Create the Exit Button
$ExitButton = New-Object System.Windows.Forms.Button
$ExitButton.Font = New-Object System.Drawing.Font("Arial", 12)
$ExitButton.Location = New-Object System.Drawing.Point(400,55)
$ExitButton.Size = New-Object System.Drawing.Size(75,30)
$ExitButton.Text = "Exit"
$ExitButton.Add_Click({
$Form.Close()
})

 

 

# Create the User Input TextBox (Multiline for long text)
$InputBox = New-Object System.Windows.Forms.TextBox
$InputBox.Location = New-Object System.Drawing.Point(0,90)
$InputBox.Size = New-Object System.Drawing.Size(900, 160)
$InputBox.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$InputBox.Multiline = $True
$InputBox.ScrollBars = "Vertical"

# Create the output label
$OutputLabel = New-Object System.Windows.Forms.Label
$OutputLabel.Text = "AI Output"
$OutputLabel.Location = New-Object System.Drawing.Point(0,250)
$OutputLabel.Size = New-Object System.Drawing.Size(900, 50)
$OutputLabel.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$OutputLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$OutputLabel.BackColor = [System.Drawing.Color]::LightBlue
$OutputLabel.ForeColor = [System.Drawing.Color]::Black

#Add the controls to the Right panel
$TopPanel.Controls.Add($InputLabel) | Out-Null
$TopPanel.Controls.Add($ModelComboBox) | Out-Null
$TopPanel.Controls.Add($SubmitButton) | Out-Null
$TopPanel.Controls.Add($ExitButton) | Out-Null
$TopPanel.Controls.Add($InputBox) | Out-Null
$TopPanel.Controls.Add($OutputLabel) | Out-Null

 

#############################################################
##           Editor Label Panel (Blue Bar Above Editor     ##
#############################################################

 

# Create a top panel for the input box, model drop down, submit, and edit buttons
$TopEditorPanel = New-Object System.Windows.Forms.Panel
$TopEditorPanel.Location = New-Object System.Drawing.Point(910,10)
$TopEditorPanel.Size = New-Object System.Drawing.Size(1050,50)

# Create the input label
$EditorLabel = New-Object System.Windows.Forms.Label
$EditorLabel.Text = "Text Editor"
$EditorLabel.Location = New-Object System.Drawing.Point(0,0)
$EditorLabel.Size = New-Object System.Drawing.Size(1050, 50)
$EditorLabel.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$EditorLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter
$EditorLabel.BackColor = [System.Drawing.Color]::LightBlue
$EditorLabel.ForeColor = [System.Drawing.Color]::Black

 

#Add the controls to the Top Editor panel
$TopEditorPanel.Controls.Add($EditorLabel) | Out-Null

 

 

###########################################################
##                  Output Panel (Lower Left Box)        ##
###########################################################

 

# Create a panel for the left text area (menu + textbox)
$OutputPanel = New-Object System.Windows.Forms.Panel
$OutputPanel.Location = New-Object System.Drawing.Point(10, 320)
$OutputPanel.Size = New-Object System.Drawing.Size(900, 750)

# Create the output (left) RichTextBox
$OutputBox = New-Object System.Windows.Forms.RichTextBox
$OutputBox.Multiline = $true
$OutputBox.Location = New-Object System.Drawing.Point(5,30) # Positioned below menu
$OutputBox.Size = New-Object System.Drawing.Size(890, 650)
$OutputBox.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$OutputBox.BackColor = "#FFFFFFFF"
$OutputBox.ReadOnly = $True
$OutputBox.Multiline = $True
$OutputBox.ScrollBars = "Vertical"
$OutputBox.Text = "This is some generic placeholder text."

# Create the menu  for the output box

# Create MenuStrip
$OutputMenu = New-Object System.Windows.Forms.MenuStrip
$OutputMenu.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font

# File Menu
$OutputFileMenu = New-Object System.Windows.Forms.ToolStripMenuItem("File")

 

$OutputSaveMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Save Conversation")
$OutputSaveMenuItem.Add_Click({
$OutputSaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$OutputSaveFileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
If ($OutputSaveFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$FileName = $OutputSaveFileDialog.FileName
$Extension = [System.IO.Path]::GetExtension($FileName).ToLower()

If ($Extension -eq ".txt"){
$OutputBox.Text | Set-Content -Path $OutputSaveFileDialog.FileName
}
}
})

 

$OutputExitMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Exit")
$OutputExitMenuItem.Add_Click({ $Form.Close() })

$OutputFileMenu.DropDownItems.Add($OutputSaveMenuItem) | Out-Null
$OutputFileMenu.DropDownItems.Add($OutputExitMenuItem) | Out-Null

$OutputMenu.Items.Add($OutputFileMenu) | Out-Null

 

# Output Edit Menu
$OutputEditMenu = New-Object System.Windows.Forms.ToolStripMenuItem("Edit")

$OutputCopyMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Copy")
$OutputCopyMenuItem.Add_Click({ $OutputBox.Copy() })

 

# Add the "Find" functionality
$OutputFindMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Find")
$OutputFindMenuItem.Add_Click({
# Create a new dialog to ask for the search term
$FindDialogBox = New-Object System.Windows.Forms.Form
$FindDialogBox.Text = "Find"
$FindDialogBox.Width = 300
$FindDialogBox.Height = 150

    $FindDialogBox.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
$FindDialogBox.Location = $OutputBox.PointToScreen([System.Drawing.Point]::new(100, 100))

    $FindLabel = New-Object System.Windows.Forms.Label
$FindLabel.Text = "Find what:"
$FindLabel.AutoSize = $True
$FindLabel.Location = New-Object System.Drawing.Point(10, 10)
$FindDialogBox.Controls.Add($FindLabel) | Out-Null

    $FindtextBox = New-Object System.Windows.Forms.TextBox
$FindtextBox.Location = New-Object System.Drawing.Point(10, 40)
$FindtextBox.Width = 260
$FindTextBox.Text = ""
$findDialogBox.Controls.Add($FindtextBox) | Out-Null

    $FindButton = New-Object System.Windows.Forms.Button
$FindButton.Text = "Find"
$FindButton.Location = New-Object System.Drawing.Point(10, 70)
$FindButton.Add_Click({
$SearchTerm = $FindtextBox.Text
If (![String]::IsNullOrWhiteSpace($SearchTerm)) {
# Clear previous highlights by resetting the text color and background
$OutputBox.SelectAll()
$OutputBox.SelectionBackColor = [System.Drawing.Color]::White
$OutputBox.DeselectAll()

            # Search and highlight all occurrences of the search term
$Index = 0
While ($Index -ge 0) {
$Index = $OutputBox.Text.IndexOf($SearchTerm, $Index)
if ($Index -ge 0) {
$OutputBox.Select($Index, $SearchTerm.Length)
$OutputBox.SelectionBackColor = [System.Drawing.Color]::Yellow
$Index += $SearchTerm.Length
}
}
}
$FindDialogBox.Close()
})
$FindDialogBox.Controls.Add($FindButton) | Out-Null

    # Show the find dialog
$FindDialogBox.ShowDialog() | Out-Null
})

 

 

 

# Add the "Clear Highlights" functionality
$OutputClearHighlightsItem = New-Object System.Windows.Forms.ToolStripMenuItem("Clear Highlights")
$OutputClearHighlightsItem.Add_Click({
# Clear previous highlights by resetting the text color and background
$OutputBox.SelectAll()
$OutputBox.SelectionBackColor = [System.Drawing.Color]::White
$OutputBox.DeselectAll()
})

 

# Add the "Clear Conversation" functionality
$OutputClearConversationItem = New-Object System.Windows.Forms.ToolStripMenuItem("Delete Conversation")
$OutputClearConversationItem.Add_Click({
# Display a Confirmation Message Box Prior to Deleting Anything
$Confirmation = [System.Windows.Forms.MessageBox]::Show(
"Are you sure you want to delete the current conversation?",  # Message text
"Confirm Deletion",                                           # Title
[System.Windows.Forms.MessageBoxButtons]::YesNo,
[System.Windows.Forms.MessageBoxIcon]::Warning
)

    # Only Proceed with Clearing the Conversation if the User Clicks Yes
If ($Confirmation -eq [System.Windows.Forms.DialogResult]::Yes) {
# Perform the deletion logic here
$OutputBox.Clear()  # Clears the output box containing the conversation
}
})

 

 

$OutputEditMenu.DropdownItems.Add($OutputCopyMenuItem) | Out-Null
$OutputEditMenu.DropdownItems.Add($OutputFindMenuItem) | Out-Null
$OutputEditMenu.DropdownItems.Add($OutputClearHighlightsItem) | Out-Null
$OutputEditMenu.DropdownItems.Add($OutputClearConversationItem) | Out-Null

$OutputMenu.Items.Add($OutputEditMenu) | Out-Null

 

# Add MenuStrip to the Form
$Form.Controls.Add($OutputMenu) | Out-Null

 

# Add controls to left panel
$OutputPanel.Controls.Add($OutputMenu) | Out-Null
$OutputPanel.Controls.Add($OutputBox) | Out-Null

 

# Create the Context Menu (Right-Click Menu or Shortcut Menu) for the Output Box
$OutputContextMenu = New-Object System.Windows.Forms.ContextMenuStrip

# Add "Select All" Menu Item
$OutputSelectAllMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Select All")
$OutputSelectAllMenuItem.Add_Click({ $OutputBox.SelectAll() })

# Add "Copy" Menu Item
$CopyMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Copy")
$CopyMenuItem.Add_Click({ $OutputBox.Copy() })

#Add Menu Choices to the Shortcut Menu
$OutputContextMenu.Items.Add($OutputSelectAllMenuItem) | Out-Null
$OutputContextMenu.Items.Add($OutputCopyMenuItem) | Out-Null

# Assign the Shortcut Menu to the RichTextBox
$OutputBox.ContextMenuStrip = $OutputContextMenu

 

#####################################################
##              Editor (Right Panel)               ##
#####################################################

# Create a panel for the right text area (menu + textbox)
$EditorPanel = New-Object System.Windows.Forms.Panel
$EditorPanel.Location = New-Object System.Drawing.Point(910, 60)
$EditorPanel.Size = New-Object System.Drawing.Size(1000, 1000)

# Create the menu strip for the right RichTextBox
$OutputMenuRight = New-Object System.Windows.Forms.MenuStrip
$rightMenuItems = @("Copy", "Paste", "Undo")

# Create the editor's RichTextBox
$Editor = New-Object System.Windows.Forms.RichTextBox
$Editor.Location = New-Object System.Drawing.Point(5, 35) # Positioned below menu
$Editor.Size = New-Object System.Drawing.Size(970, 905)
$Editor.Text = "This is a line of sample text.`r`nHere is another line of sample text.`r`nHere is one more line of text."

$Editor.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font
$Editor.BackColor = "#FFFFFFFF"
$Editor.SelectionColor = [System.Drawing.Color]::Black
$Editor.ReadOnly = $False
$Editor.Multiline = $True
$Editor.ScrollBars = "Vertical"

 

# Populate the menu strip for the editor

 

# Create MenuStrip
$EditorMenu = New-Object System.Windows.Forms.MenuStrip
$EditorMenu.Font = New-Object System.Drawing.Font("Arial", 14)  # Default font

# File Menu
$FileMenu = New-Object System.Windows.Forms.ToolStripMenuItem("File")

 

 

 

$OpenMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Open")
$OpenMenuItem.Add_Click({
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.Filter = "Text Files (*.txt)|*.txt| RTF File (*.rtf)|*.rtf|All Files|*.*"
$OpenFileDialog.Title = "Open File"
If ($OpenFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$FileName = $OpenFileDialog.FileName
$Extension = [System.IO.Path]::GetExtension($FileName).ToLower()
If ($Extension -eq ".txt") {
$Editor.Text = Get-Content $OpenFileDialog.FileName -Raw
}
ElseIf ($Extension -eq ".rtf") {
# Open RTF file
$Editor.LoadFile($fileName, [System.Windows.Forms.RichTextBoxStreamType]::RichText)
}
}
})

 

$SaveMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Save")
$SaveMenuItem.Add_Click({
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.Filter = "RTF File (*.rtf)|*.rtf|Text Files (*.txt)|*.txt |All Files (*.*)|*.*"
If ($SaveFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$FileName = $SaveFileDialog.FileName
$Extension = [System.IO.Path]::GetExtension($fileName).ToLower()

If ($Extension -eq ".rtf") {
# Save as RTF
$Editor.SaveFile($FileName, [System.Windows.Forms.RichTextBoxStreamType]::RichText)
}
ElseIf ($Extension -eq ".txt"){
$Editor.Text | Set-Content -Path $SaveFileDialog.FileName
}
}
})

 

 

 

$ExitMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Exit")
$ExitMenuItem.Add_Click({ $Form.Close() })

 

$FileMenu.DropDownItems.Add($OpenMenuItem) | Out-Null
$FileMenu.DropDownItems.Add($SaveMenuItem) | Out-Null
$FileMenu.DropDownItems.Add($ExitMenuItem) | Out-Null

 

$EditorMenu.Items.Add($FileMenu) | Out-Null

# Edit Menu
$EditMenu = New-Object System.Windows.Forms.ToolStripMenuItem("Edit")

$UndoMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Undo")
$UndoMenuItem.Add_Click({ $Editor.Undo() })

$CutMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Cut")
$CutMenuItem.Add_Click({ $Editor.Cut() })

$CopyMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Copy")
$CopyMenuItem.Add_Click({ $Editor.Copy() })

$PasteMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Paste")
$PasteMenuItem.Add_Click({ $Editor.Paste() })

# Add the "Find" functionality
$FindMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Find")
$FindMenuItem.Add_Click({
# Create a new dialog to ask for the search term
$FindDialogBox = New-Object System.Windows.Forms.Form
$FindDialogBox.Text = "Find"
$FindDialogBox.Width = 300
$FindDialogBox.Height = 150

    $FindDialogBox.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
$FindDialogBox.Location = $EditorPanel.PointToScreen([System.Drawing.Point]::new(100, 100))

    $FindLabel = New-Object System.Windows.Forms.Label
$FindLabel.Text = "Find what:"
$FindLabel.AutoSize = $True
$FindLabel.Location = New-Object System.Drawing.Point(10, 10)
$FindDialogBox.Controls.Add($FindLabel) | Out-Null

    $FindtextBox = New-Object System.Windows.Forms.TextBox
$FindtextBox.Location = New-Object System.Drawing.Point(10, 40)
$FindtextBox.Width = 260
$FindTextBox.Text = ""
$findDialogBox.Controls.Add($FindtextBox) | Out-Null

    $FindButton = New-Object System.Windows.Forms.Button
$FindButton.Text = "Find"
$FindButton.Location = New-Object System.Drawing.Point(10, 70)
$FindButton.Add_Click({
$SearchTerm = $FindtextBox.Text
If (![String]::IsNullOrWhiteSpace($SearchTerm)) {
# Clear previous highlights by resetting the text color and background
$Editor.SelectAll()
$Editor.SelectionBackColor = [System.Drawing.Color]::White
$Editor.DeselectAll()

            # Search and highlight all occurrences of the search term
$Index = 0
While ($Index -ge 0) {
$Index = $Editor.Text.IndexOf($SearchTerm, $Index)
if ($Index -ge 0) {
$Editor.Select($Index, $SearchTerm.Length)
$Editor.SelectionBackColor = [System.Drawing.Color]::Yellow
$Index += $SearchTerm.Length
}
}
}
$FindDialogBox.Close()
})
$FindDialogBox.Controls.Add($FindButton) | Out-Null

    # Show the find dialog
$FindDialogBox.ShowDialog() | Out-Null
})

 

# Add the "Find and Replace" functionality
$FindReplaceMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Find and Replace")
$FindReplaceMenuItem.Add_Click({
# Create the Find and Replace dialog box
$FindReplaceDialogBox = New-Object System.Windows.Forms.Form
$FindReplaceDialogBox.Text = "Find and Replace"
$FindReplaceDialogBox.Width = 320
$FindReplaceDialogBox.Height = 200

    $FindReplaceDialogBox.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual
$FindReplaceDialogBox.Location = $EditorPanel.PointToScreen([System.Drawing.Point]::new(100, 100))

    # Label for Find Field
$FindReplaceLabel = New-Object System.Windows.Forms.Label
$FindReplaceLabel.Text = "Text to Find:"
$FindReplaceLabel.AutoSize = $True
$FindReplaceLabel.Location = New-Object System.Drawing.Point(10, 10)
$FindReplaceDialogBox.Controls.Add($FindReplaceLabel) | Out-Null

    # Textbox for Find Input
$FindReplaceTextBox = New-Object System.Windows.Forms.TextBox
$FindReplaceTextBox.Location = New-Object System.Drawing.Point(10, 30)
$FindReplaceTextBox.Width = 280
$FindReplaceDialogBox.Controls.Add($FindReplaceTextBox) | Out-Null

    # Label for Replace Field
$ReplaceLabel = New-Object System.Windows.Forms.Label
$ReplaceLabel.Text = "Replace with:"
$ReplaceLabel.AutoSize = $True
$ReplaceLabel.Location = New-Object System.Drawing.Point(10, 60)
$FindReplaceDialogBox.Controls.Add($ReplaceLabel) | Out-Null

    # Textbox for Replace Input
$ReplaceTextBox = New-Object System.Windows.Forms.TextBox
$ReplaceTextBox.Location = New-Object System.Drawing.Point(10, 80)
$ReplaceTextBox.Width = 280
$FindReplaceDialogBox.Controls.Add($ReplaceTextBox) | Out-Null

    # "Replace All" button
$ReplaceAllButton = New-Object System.Windows.Forms.Button
$ReplaceAllButton.Text = "Replace All"
$ReplaceAllButton.Location = New-Object System.Drawing.Point(10, 120)
$ReplaceAllButton.Add_Click({
$SearchTerm = $FindReplaceTextBox.Text
$ReplaceTerm = $ReplaceTextBox.Text

If (![String]::IsNullOrWhiteSpace($SearchTerm)) {
# Replace all occurrences in the RichTextBox
$Editor.Text = $Editor.Text -replace [Regex]::Escape($SearchTerm), $ReplaceTerm
}
$FindReplaceDialogBox.Close()
})
$FindReplaceDialogBox.Controls.Add($ReplaceAllButton) | Out-Null

    # Show the Find and Replace dialog
$FindReplaceDialogBox.ShowDialog()
})

 

# Add the "Clear Highlights" Functionality
$EditorClearHighlightsItem = New-Object System.Windows.Forms.ToolStripMenuItem("Clear Highlights")
$EditorClearHighlightsItem.Add_Click({
# Clear previous highlights by resetting the text color and background
$Editor.SelectAll()
$Editor.SelectionBackColor = [System.Drawing.Color]::White
$Editor.DeselectAll()
})

 

# Add the "Clear Notes" Functionality
$EditorDeleteEditsItem = New-Object System.Windows.Forms.ToolStripMenuItem("Delete All Text")
$EditorDeleteEditsItem.Add_Click({
# Display a Confirmation Message Box Prior to Deleting Anything
$Confirmation = [System.Windows.Forms.MessageBox]::Show(
"Are you sure you want to delete the text editor content?",  # Message text
"Confirm Deletion",                                           # Title
[System.Windows.Forms.MessageBoxButtons]::YesNo,
[System.Windows.Forms.MessageBoxIcon]::Warning
)

    # Only Proceed with Clearing the Conversation if the User Clicks Yes
If ($Confirmation -eq [System.Windows.Forms.DialogResult]::Yes) {
# Perform the deletion logic here
$Editor.Clear()  # Clears the entire contents of the text editor
}
})

$EditMenu.DropDownItems.Add($UndoMenuItem) | Out-Null
$EditMenu.DropDownItems.Add($CutMenuItem) | Out-Null
$EditMenu.DropDownItems.Add($CopyMenuItem) | Out-Null
$EditMenu.DropDownItems.Add($PasteMenuItem) | Out-Null
$EditMenu.DropDownItems.Add($FindMenuItem) | Out-Null
$EditMenu.DropDownItems.Add($FindReplaceMenuItem) | Out-Null
$EditMenu.DropDownItems.Add($EditorClearHighlightsItem) | Out-Null
$EditMenu.DropDownItems.Add($EditorDeleteEditsItem) | Out-Null

$EditorMenu.Items.Add($EditMenu) | Out-Null

# Format Menu
$FormatMenu = New-Object System.Windows.Forms.ToolStripMenuItem("Format")

$FontMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Font")
$FontMenuItem.Add_Click({
$FontDialog = New-Object System.Windows.Forms.FontDialog
$FontDialog.Font = $TextBox.Font
If ($FontDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$Editor.SelectionFont = $FontDialog.Font
}
})

# Highlight menu item
$HighlightMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Highlight")
$HighlightMenuItem.Add_Click({
If ($Editor.SelectedText.Length -gt 0) {
$Editor.SelectionBackColor = [System.Drawing.Color]::Yellow }
})

 

# Create a menu item for choosing the text color
$ColorMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Text Color")
$ColorMenuItem.Add_Click({
# Open the Color dialog box to select a color
$ColorDialog = New-Object System.Windows.Forms.ColorDialog
If ($ColorDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {

# Apply the selected color to the selected text in the RichTextBox
$Editor.SelectionColor = $ColorDialog.Color
}
})

 

 

$FormatMenu.DropDownItems.Add($FontMenuItem) | Out-Null
$FormatMenu.DropDownItems.Add($ColorMenuItem) | Out-Null
$FormatMenu.DropDownItems.Add($HighlightMenuItem) | Out-Null

 

 

 

 

 

$EditorMenu.Items.Add($FormatMenu) | Out-Null

# Add MenuStrip to the Form

$Form.Controls.Add($EditorMenu) | Out-Null

 

 

# Create the Context Menu (the Shortcut Menu or the Right-Click Menu)
$EditorContextMenu = New-Object System.Windows.Forms.ContextMenuStrip

# Add the "Undo" Menu Item
$EditorUndoMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Undo")
$EditorUndoMenuItem.Add_Click({ $Editor.Undo() })

# Add the "Cut" Menu Item
$EditorCutMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Cut")
$EditorCutMenuItem.Add_Click({ $Editor.Cut() })

# Add the "Copy" Menu Item
$EditorCopyMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Copy")
$EditorCopyMenuItem.Add_Click({ $Editor.Copy() })

# Add the "Paste" Menu Item
$EditorPasteMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Paste")
$EditorPasteMenuItem.Add_Click({ $Editor.Paste() })

# Add the "Select All" Menu Item
$EditorSelectAllMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem("Select All")
$EditorSelectAllMenuItem.Add_Click({ $Editor.SelectAll() })

# Add the Menu Options to the Shortcut Menu
$EditorContextMenu.Items.Add($EditorUndoMenuItem) | Out-Null
$EditorContextMenu.Items.Add($EditorCutMenuItem) | Out-Null
$EditorContextMenu.Items.Add($EditorCopyMenuItem) | Out-Null
$EditorContextMenu.Items.Add($EditorPasteMenuItem) | Out-Null
$EditorContextMenu.Items.Add($EditorSelectAllMenuItem) | Out-Null

# Assign the Shortcut Menu to the Editor
$Editor.ContextMenuStrip = $EditorContextMenu

 

#Add the controls to the Right panel
$EditorPanel.Controls.Add($EditorMenu) | Out-Null
$EditorPanel.Controls.Add($Editor) | Out-Null

##########################################
##       Display the GUI                ##
##########################################

# Add panels to the form
$Form.Controls.Add($TopPanel) | Out-Null
$Form.Controls.Add($OutputPanel) | Out-Null
$Form.Controls.Add($TopEditorPanel) | Out-Null
$Form.Controls.Add($EditorPanel) | Out-Null

# Show the form
[Void]$Form.ShowDialog()

 

 

About the Author

Brien Posey is a 22-time Microsoft MVP with decades of IT experience. As a freelance writer, Posey has written thousands of articles and contributed to several dozen books on a wide variety of IT topics. Prior to going freelance, Posey was a CIO for a national chain of hospitals and health care facilities. He has also served as a network administrator for some of the country's largest insurance companies and for the Department of Defense at Fort Knox. In addition to his continued work in IT, Posey has spent the last several years actively training as a commercial scientist-astronaut candidate in preparation to fly on a mission to study polar mesospheric clouds from space. You can follow his spaceflight training on his Web site.

Featured

comments powered by Disqus

Subscribe on YouTube