Repository: JanDeDobbeleer/oh-my-posh
Pull request #5634 (2024-09-18 05:50:32)

refactor(theme): update multiverse-neon
### Prerequisites

- [x] I have read and understood the [contributing guide][CONTRIBUTING.md].
- [x] The commit message follows the [conventional commits][cc] guidelines.
- [ ] Tests for the changes have been added (for bug fixes / features).
- [ ] Docs have been added/updated (for bug fixes / features).

### Description

This PR includes the following changes:
- Replaces the generic yarn icon with the actual logo.
- Adds the generic pnpm icon (the actual logo is currently unavailable).
- Updates the icon colors to match the official brand colors.

The brand colors were extracted from the official SVG logos:
- [pnpm logo](https://pnpm.io/logos#standard-logo)
- [npm logo](https://github.com/npm/logos/blob/master/npm%20logo/npm-logo-red.svg)
- [yarn logo](https://github.com/yarnpkg/assets/blob/master/yarn-kitten.svg)
- [node logo](https://nodejs.org/static/logos/jsIconGreen.svg)

The colors were then adjusted to 90% brightness using the HSB color model.

Tips:

If you're not comfortable with working with Git,
we're working a guide (https://ohmyposh.dev/docs/contributing/git) to help you out.
Oh My Posh advises GitKraken (https://www.gitkraken.com/invite/nQmDPR9D) as your preferred cross platform Git GUI power tool.

-->

[CONTRIBUTING.md]: https://github.com/JanDeDobbeleer/oh-my-posh/blob/main/CONTRIBUTING.md
[cc]: https://www.conventionalcommits.org/en/v1.0.0/#summary


JanDeDobbeleer: @garysassano please adjust the commit message, it's not according to the required format.
JanDeDobbeleer: @garysassano changing the title isn't the same as the commit message, you can find a guide [here](https://ohmyposh.dev/docs/contributing/git#i-didnt-stick-to-the-conventional-commit-guidelines).

Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index 8cb93a33..9e53f4b8 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -2758,6 +2758,16 @@
       "contributions": [
         "doc"
       ]
+    },
+    {
+      "login": "oliviaBahr",
+      "name": "Olivia Bahr",
+      "avatar_url": "https://avatars.githubusercontent.com/u/98684296?v=4",
+      "profile": "https://github.com/oliviaBahr",
+      "contributions": [
+        "code",
+        "doc"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index f1cec202..26aab8b6 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -27,10 +27,10 @@ jobs:
     - name: Checkout code
       uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3
       with:
         languages: go
     - name: Autobuild
-      uses: github/codeql-action/autobuild@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d
+      uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 5f61b90c..7b1e0ef6 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -27,7 +27,7 @@ jobs:
         with:
           go-version: 1.21
           cache-dependency-path: src/go.sum
-      - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b
+      - uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6
         with:
           node-version: 20.9.0
         # Create Kind cluster to have a Kubernetes context for cloud-native-azure theme
diff --git a/src/cli/enable.go b/src/cli/enable.go
index 2b9cb30e..cdcf8e30 100644
--- a/src/cli/enable.go
+++ b/src/cli/enable.go
@@ -44,11 +44,14 @@ func init() {
 func toggleFeature(cmd *cobra.Command, feature string, enable bool) {
 	env := &runtime.Terminal{
 		CmdFlags: &runtime.Flags{
-			Shell: shellName,
+			Shell:     shellName,
+			SaveCache: true,
 		},
 	}
+
 	env.Init()
 	defer env.Close()
+
 	switch feature {
 	case "notice":
 		if enable {
diff --git a/src/cli/font.go b/src/cli/font.go
index 7c267cea..ec21ecf1 100644
--- a/src/cli/font.go
+++ b/src/cli/font.go
@@ -36,7 +36,12 @@ This command is used to install fonts and configure the font in your terminal.
 					fontName = args[1]
 				}
 
-				env := &runtime.Terminal{}
+				env := &runtime.Terminal{
+					CmdFlags: &runtime.Flags{
+						SaveCache: true,
+					},
+				}
+
 				env.Init()
 				defer env.Close()
 
diff --git a/src/cli/notice.go b/src/cli/notice.go
index ba0571eb..c2be683b 100644
--- a/src/cli/notice.go
+++ b/src/cli/notice.go
@@ -16,8 +16,11 @@ var noticeCmd = &cobra.Command{
 	Args:  cobra.NoArgs,
 	Run: func(_ *cobra.Command, _ []string) {
 		env := &runtime.Terminal{
-			CmdFlags: &runtime.Flags{},
+			CmdFlags: &runtime.Flags{
+				SaveCache: true,
+			},
 		}
+
 		env.Init()
 		defer env.Close()
 
diff --git a/src/cli/toggle.go b/src/cli/toggle.go
index 9badf5c8..f0f6b0bb 100644
--- a/src/cli/toggle.go
+++ b/src/cli/toggle.go
@@ -19,7 +19,13 @@ var toggleCmd = &cobra.Command{
 			_ = cmd.Help()
 			return
 		}
-		env := &runtime.Terminal{}
+
+		env := &runtime.Terminal{
+			CmdFlags: &runtime.Flags{
+				SaveCache: true,
+			},
+		}
+
 		env.Init()
 		defer env.Close()
 
diff --git a/src/runtime/battery/battery_windows.go b/src/runtime/battery/battery_windows.go
index 838f8c80..a5d4b294 100644
--- a/src/runtime/battery/battery_windows.go
+++ b/src/runtime/battery/battery_windows.go
@@ -185,6 +185,10 @@ func systemGet(idx int) (*battery, error) {
 		return nil, errno
 	}
 
+	if cbRequired == 0 {
+		return nil, errors.New("no buffer information returned")
+	}
+
 	// The god damn struct with ANYSIZE_ARRAY of utf16 in it is crazy.
 	// So... let's emulate it with array of uint16 ;-D.
 	// Keep in mind that the first two elements are actually cbSize.
@@ -250,7 +254,7 @@ func systemGet(idx int) (*battery, error) {
 	}
 
 	if bqi.BatteryTag == 0 {
-		return nil, errors.New("BatteryTag not returned")
+		return nil, errors.New("battery tag not returned")
 	}
 
 	b := &battery{}
@@ -274,6 +278,7 @@ func systemGet(idx int) (*battery, error) {
 	b.Full = float64(bi.FullChargedCapacity)
 
 	bws := batteryWaitStatus{BatteryTag: bqi.BatteryTag}
+
 	var bs batteryStatus
 	err = windows.DeviceIoControl(
 		handle,
diff --git a/src/template/func_map.go b/src/template/func_map.go
index e1eed05d..57cba57b 100644
--- a/src/template/func_map.go
+++ b/src/template/func_map.go
@@ -17,6 +17,7 @@ func funcMap() template.FuncMap {
 		"replaceP":     replaceP,
 		"gt":           gt,
 		"lt":           lt,
+		"random":       random,
 		"reason":       GetReasonFromStatus,
 		"hresult":      hresult,
 		"trunc":        trunc,
diff --git a/src/template/random.go b/src/template/random.go
new file mode 100644
index 00000000..9fc3cced
--- /dev/null
+++ b/src/template/random.go
@@ -0,0 +1,22 @@
+package template
+
+import (
+	"errors"
+	"fmt"
+	"math/rand"
+	"reflect"
+)
+
+func random(list interface{}) (string, error) {
+	v := reflect.ValueOf(list)
+
+	if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+		return "", errors.New("input must be a slice or array")
+	}
+
+	if v.Len() == 0 {
+		return "", errors.New("input slice or array is empty")
+	}
+
+	return fmt.Sprintf("%v", v.Index(rand.Intn(v.Len()))), nil
+}
diff --git a/src/upgrade/notice.go b/src/upgrade/notice.go
index c785303f..a95a3a22 100644
--- a/src/upgrade/notice.go
+++ b/src/upgrade/notice.go
@@ -47,6 +47,7 @@ func Latest(env runtime.Environment) (string, error) {
 	if err != nil {
 		return "", err
 	}
+
 	var release release
 	// this can't fail
 	_ = json.Unmarshal(body, &release)
diff --git a/themes/multiverse-neon.omp.json b/themes/multiverse-neon.omp.json
index dfab405c..9282e5d4 100644
--- a/themes/multiverse-neon.omp.json
+++ b/themes/multiverse-neon.omp.json
@@ -50,19 +50,19 @@
      {
       "alignment": "right",
       "segments": [
-
         {
-          "foreground": "#5fb157",
+          "type": "node",
+          "style": "plain",
+          "foreground": "#88e570",
           "properties": {
             "display_mode": "files",
             "fetch_package_manager": true,
             "fetch_version": true,
-            "npm_icon": "<#FE4A49>\ue71e npm</> ",
-            "yarn_icon":"<#44FFD2>\uf487 yarn</> "
+            "npm_icon": "<#e5272d>\ue616 npm</> ",
+            "yarn_icon": "<#37aee5>\ue6a7 yarn</> ",
+            "pnpm_icon": "<#e5a100>\udb80\udec1 pnpm</> "
           },
-          "style": "plain",
-          "template": "{{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}\ue718 {{ .Full }}",
-          "type": "node"
+          "template": "{{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}\ue718 {{ .Full }}"
         },
         {
           "foreground": "#4063D8",
diff --git a/website/docs/configuration/templates.mdx b/website/docs/configuration/templates.mdx
index 38261a4d..4e435dfb 100644
--- a/website/docs/configuration/templates.mdx
+++ b/website/docs/configuration/templates.mdx
@@ -182,6 +182,7 @@ use them.
 | `{{ replaceP "c.t" "cut code cat" "dog" }}`                        | Exposes [regexp.ReplaceAllString][regexpra] as a string template function.                                                 |
 | <code>\{\{ .Code &vert; hresult \}\}</code>                        | Transform a status code to its HRESULT value for easy troubleshooting. For example `-1978335212` becomes `0x8A150014`.     |
 | `{{ readFile ".version.json" }}`                                   | Read a file in the current directory. Returns a string.                                                                    |
+| `{{ random (list \"a\" 2 .MyThirdItem) }}`                         | Selects a random element from a list. The list can be an array or slice containing any types (use sprig's `list`).         |
 
 <!-- markdownlint-enable MD013 -->
 
diff --git a/website/docs/contributors.md b/website/docs/contributors.md
index 0a2131ea..207f25dd 100644
--- a/website/docs/contributors.md
+++ b/website/docs/contributors.md
@@ -389,6 +389,9 @@ Thanks goes to these wonderful people ([emoji key][acek]):
       <td align="center" valign="top" width="14.28%"><a href="https://kizivat.eu"><img src="https://avatars.githubusercontent.com/u/3535926?v=4?s=100" width="100px;" alt="David Kizivat"/><br /><sub><b>David Kizivat</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kizivat" title="Code">💻</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=kizivat" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/mgrubb"><img src="https://avatars.githubusercontent.com/u/351301?v=4?s=100" width="100px;" alt="Michael Grubb"/><br /><sub><b>Michael Grubb</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=mgrubb" title="Documentation">📖</a></td>
     </tr>
+    <tr>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/oliviaBahr"><img src="https://avatars.githubusercontent.com/u/98684296?v=4?s=100" width="100px;" alt="Olivia Bahr"/><br /><sub><b>Olivia Bahr</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=oliviaBahr" title="Code">💻</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=oliviaBahr" title="Documentation">📖</a></td>
+    </tr>
   </tbody>
 </table>
 
diff --git a/website/docs/installation/prompt.mdx b/website/docs/installation/prompt.mdx
index 869c3c30..a94fdc79 100644
--- a/website/docs/installation/prompt.mdx
+++ b/website/docs/installation/prompt.mdx
@@ -32,7 +32,7 @@ oh-my-posh get shell
 }>
 <TabItem value="bash">
 
-Add the following to `~/.bashrc` (could be `~/.profile` or `~/.bash_profile` depending on your environment):
+Add the following snippet as the last line to `~/.bashrc` (could be `~/.profile` or `~/.bash_profile` depending on your environment):
 
 ```bash
 eval "$(oh-my-posh init bash)"
@@ -69,7 +69,7 @@ Once added, restart cmd for the changes to take effect.
 </TabItem>
 <TabItem value="elvish">
 
-Add the following at the end of `~/.elvish/rc.elv`:
+Add the following snippet as the last line to `~/.elvish/rc.elv`:
 
 ```bash
 eval (oh-my-posh init elvish)
@@ -88,7 +88,7 @@ exec elvish
 It is recommended to use the latest version of Fish. Versions below 3.1.2 have issues displaying the prompt.
 :::
 
-Initialize Oh My Posh in `~/.config/fish/config.fish`:
+Add the following snippet as the last line to `~/.config/fish/config.fish`:
 
 ```bash
 oh-my-posh init fish | source
@@ -107,7 +107,7 @@ exec fish
 Oh My Posh requires Nushell v0.93.0 or higher.
 :::
 
-Add the following line to the Nushell env file (`$nu.env-path`):
+Add the following snippet as the last line to the Nushell env file (`$nu.env-path`):
 
 ```bash
 oh-my-posh init nu
@@ -154,7 +154,7 @@ In this scenario, it can also be that PowerShell blocks running local scripts. T
 to only require remote scripts to be signed using `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine`, or [sign the profile][sign].
 :::
 
-Then add the following line.
+Add the following snippet as the last line to your PowerShell profile script:
 
 ```powershell
 oh-my-posh init pwsh | Invoke-Expression
@@ -180,7 +180,7 @@ Once added, reload your profile for the changes to take effect.
 </TabItem>
 <TabItem value="tcsh">
 
-Add the following at the end of `~/.tcshrc`:
+Add the following snippet as the last line to `~/.tcshrc`:
 
 ```bash
 eval "`oh-my-posh init tcsh`"
@@ -195,7 +195,7 @@ exec tcsh
 </TabItem>
 <TabItem value="xonsh">
 
-Add the following line at the end of `~/.xonshrc`:
+Add the following snippet as the last line to `~/.xonshrc`:
 
 ```bash
 execx($(oh-my-posh init xonsh))
@@ -210,7 +210,7 @@ exec xonsh
 </TabItem>
 <TabItem value="zsh">
 
-Add the following to `~/.zshrc`:
+Add the following snippet as the last line to `~/.zshrc`:
 
 ```bash
 eval "$(oh-my-posh init zsh)"


Test patch:
diff --git a/src/template/random_test.go b/src/template/random_test.go
new file mode 100644
index 00000000..da70e37e
--- /dev/null
+++ b/src/template/random_test.go
@@ -0,0 +1,65 @@
+package template
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRandom(t *testing.T) {
+	cases := []struct {
+		Case        string
+		Input       interface{}
+		ShouldError bool
+	}{
+		{
+			Case:  "valid slice",
+			Input: []int{1, 2, 3, 4, 5},
+		},
+		{
+			Case:  "valid array",
+			Input: [5]int{1, 2, 3, 4, 5},
+		},
+		{
+			Case:        "empty slice",
+			Input:       []int{},
+			ShouldError: true,
+		},
+		{
+			Case:        "not a slice or array",
+			Input:       "not a slice",
+			ShouldError: true,
+		},
+		{
+			Case:  "valid string slice",
+			Input: []string{"a", "b", "c"},
+		},
+		{
+			Case:  "valid float slice",
+			Input: []float64{1.1, 2.2, 3.3},
+		},
+		{
+			Case: "interface with multiple types",
+			Input: []interface{}{
+				"a",
+				1,
+				true,
+			},
+		},
+		{
+			Case:  "valid struct slice",
+			Input: []struct{ Name string }{{Name: "Alice"}, {Name: "Bob"}},
+		},
+	}
+
+	for _, tc := range cases {
+		result, err := random(tc.Input)
+		if tc.ShouldError {
+			assert.Error(t, err, tc.Case)
+		} else {
+			assert.NoError(t, err, tc.Case)
+			assert.Contains(t, fmt.Sprintf("%v", tc.Input), result, tc.Case)
+		}
+	}
+}