Repository: JanDeDobbeleer/oh-my-posh
Pull request #4230 (2023-09-04 20:51:56)

refactor: M365Princess extract colors to palette
### 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

<!--
copilot:summary
-->
### <samp>🤖 Generated by Copilot at 2c42d54</samp>

Refactor `M365Princess.omp.json` theme to use custom palette object for colors. This simplifies the theme file and enables more flexibility for users to adjust the colors.

### How

<!--
copilot:walkthrough
-->
### <samp>🤖 Generated by Copilot at 2c42d54</samp>

*  Add a palette object with eight custom colors to the theme file `themes/M365Princess.omp.json` ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276R3-R12))
*  Replace hard-coded hex values for foreground and background colors of prompt segments with named colors from the palette, prefixed with `p:` ([link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L11-R22), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L18-R29), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L27-R38), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L38-R49), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L46-R57), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L57-R68), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L71-R82), [link](https://github.com/JanDeDobbeleer/oh-my-posh/pull/4230/files?diff=unified&w=0#diff-7bfaeb2b43b34cb08f271631485540c2c047b46ffadad21ccac06151558de276L79-R90))

<!---

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



Patch:
diff --git a/.all-contributorsrc b/.all-contributorsrc
index ed581c32..913559db 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1883,6 +1883,24 @@
         "design",
         "doc"
       ]
+    },
+    {
+      "login": "0Ky",
+      "name": "ｃｒｙｐｔｉｘ",
+      "avatar_url": "https://avatars.githubusercontent.com/u/16103757?v=4",
+      "profile": "https://github.com/0Ky",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "ehawman",
+      "name": "Evan Hawman",
+      "avatar_url": "https://avatars.githubusercontent.com/u/52979227?v=4",
+      "profile": "https://github.com/ehawman",
+      "contributions": [
+        "design"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 5000a635..fc6555d9 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -9,7 +9,7 @@ jobs:
     container: ghcr.io/jandedobbeleer/golang-android-container:latest
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Build
       run: |
         VERSION=$(echo "${{ github.event.release.name }}" | cut -c2-)
diff --git a/.github/workflows/build_code.yml b/.github/workflows/build_code.yml
index e090e295..5f8c76b2 100644
--- a/.github/workflows/build_code.yml
+++ b/.github/workflows/build_code.yml
@@ -17,18 +17,18 @@ jobs:
         shell: pwsh
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Install Go 🗳
       uses: ./.github/workflows/composite/bootstrap-go
     - name: Run GoReleaser 🚀
-      uses: goreleaser/goreleaser-action@3fa32b8bb5620a2c1afe798654bbad59f9da4906
+      uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8
       with:
         distribution: goreleaser
         version: latest
         args: build --rm-dist --snapshot --skip-post-hooks --skip-before
         workdir: src
     - name: Archive production artifacts
-      uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce
+      uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
       with:
         name: builds
         retention-days: 1
diff --git a/.github/workflows/close_themes_pr.yml b/.github/workflows/close_themes_pr.yml
index dc733fd3..77963f6d 100644
--- a/.github/workflows/close_themes_pr.yml
+++ b/.github/workflows/close_themes_pr.yml
@@ -9,7 +9,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Check and close 🔐
         uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410
         with:
diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml
index 4a2eb63c..0d1bdeb1 100644
--- a/.github/workflows/code.yml
+++ b/.github/workflows/code.yml
@@ -25,7 +25,7 @@ jobs:
         go-version: 1.21
         cache-dependency-path: src/go.sum
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Golang CI
       uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc
       with:
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 349d7be4..af5524a8 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -25,7 +25,7 @@ jobs:
         go-version: 1.21
         cache-dependency-path: src/go.sum
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Initialize CodeQL
       uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a
       with:
diff --git a/.github/workflows/commits.yml b/.github/workflows/commits.yml
index 5130d2c4..aa802b05 100644
--- a/.github/workflows/commits.yml
+++ b/.github/workflows/commits.yml
@@ -6,7 +6,7 @@ jobs:
   commitlint:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
         with:
           fetch-depth: 0
       - uses: wagoid/commitlint-github-action@6319f54d83768b60acd6fd60e61007ccc583e62f
diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml
index 5cb14a0f..108d7841 100644
--- a/.github/workflows/contributors.yml
+++ b/.github/workflows/contributors.yml
@@ -10,7 +10,7 @@ jobs:
     if: github.event.pull_request.merged == true
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Check and comment 👩🏾‍💻
         uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410
         with:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 0d778270..ae18d524 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -18,7 +18,7 @@ jobs:
     runs-on: ubuntu-latest
     name: Build and Deploy
     steps:
-      - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
         with:
           submodules: true
           persist-credentials: false
diff --git a/.github/workflows/gomod.yml b/.github/workflows/gomod.yml
index 1c126067..06b20845 100644
--- a/.github/workflows/gomod.yml
+++ b/.github/workflows/gomod.yml
@@ -15,7 +15,7 @@ jobs:
         go-version: 1.21
         cache-dependency-path: src/go.sum
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Check for unused dependencies
       run: |
         go mod tidy
diff --git a/.github/workflows/inno.yml b/.github/workflows/inno.yml
index c0104170..6ed7da6a 100644
--- a/.github/workflows/inno.yml
+++ b/.github/workflows/inno.yml
@@ -15,7 +15,7 @@ jobs:
         working-directory: ${{ github.workspace }}/packages/inno
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Build installer 📦
       id: build
       env:
diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml
index 3d9daeb9..c93398b0 100644
--- a/.github/workflows/markdown.yml
+++ b/.github/workflows/markdown.yml
@@ -7,7 +7,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout code
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Lint files
       uses: articulate/actions-markdownlint@17b8abe7407cd17590c006ecc837c35e1ac3ed83
       with:
diff --git a/.github/workflows/merge_contributions_pr.yml b/.github/workflows/merge_contributions_pr.yml
index 8e552ba4..4c83cf7b 100644
--- a/.github/workflows/merge_contributions_pr.yml
+++ b/.github/workflows/merge_contributions_pr.yml
@@ -10,7 +10,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Check and merge ⛙
         uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410
         with:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 247340fd..09e0240e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ jobs:
       skipped: ${{ steps.changelog.outputs.skipped }}
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Create changelog ✍️
         id: changelog
         uses: TriPSs/conventional-changelog-action@5f865e4ca373f189640721c0cca04ea3e0e7bfcd
@@ -41,7 +41,7 @@ jobs:
         working-directory: ${{ github.workspace }}/src
     steps:
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Install Go 🗳
         uses: ./.github/workflows/composite/bootstrap-go
       - name: Tag HEAD 😸
@@ -60,7 +60,7 @@ jobs:
         env:
           COSIGN_KEY: ${{secrets.COSIGN_KEY}}
       - name: Run GoReleaser 🚀
-        uses: goreleaser/goreleaser-action@3fa32b8bb5620a2c1afe798654bbad59f9da4906
+        uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8
         with:
           distribution: goreleaser
           version: latest
diff --git a/.github/workflows/scoop.yml b/.github/workflows/scoop.yml
index f5c4a714..fc5db0bd 100644
--- a/.github/workflows/scoop.yml
+++ b/.github/workflows/scoop.yml
@@ -18,7 +18,7 @@ jobs:
         working-directory: ${{ github.workspace }}/packages/scoop
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Update Template ✍️
       run: |
         ./build.ps1 -Version ${{ github.event.inputs.version }}
diff --git a/.github/workflows/social.yml b/.github/workflows/social.yml
index 26bd412b..616c87b4 100644
--- a/.github/workflows/social.yml
+++ b/.github/workflows/social.yml
@@ -13,7 +13,7 @@ jobs:
         with:
           node-version: 18
       - name: Checkout code 👋
-        uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+        uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
       - name: Skyt 🌤️
         working-directory: scripts/bluesky
         env:
diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml
index 79613a91..90e7f6d6 100644
--- a/.github/workflows/winget.yml
+++ b/.github/workflows/winget.yml
@@ -17,7 +17,7 @@ jobs:
       WINGETCREATE_TOKEN: ${{ secrets.WINGETCREATE_TOKEN }}
     steps:
     - name: Checkout code 👋
-      uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744
+      uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
     - name: Create manifest and submit PR 📦
       run: |
         ./build.ps1 -Version "${{ github.event.inputs.version }}" -Token $env:WINGETCREATE_TOKEN
diff --git a/src/ansi/ansi_writer.go b/src/ansi/ansi_writer.go
index 7a8290f7..820ad1a2 100644
--- a/src/ansi/ansi_writer.go
+++ b/src/ansi/ansi_writer.go
@@ -86,13 +86,12 @@ type Writer struct {
 	builder strings.Builder
 	length  int
 
-	foreground        Color
-	background        Color
-	currentForeground Color
-	currentBackground Color
-	runes             []rune
-	transparent       bool
-	invisible         bool
+	foreground  Color
+	background  Color
+	current     ColorHistory
+	runes       []rune
+	transparent bool
+	invisible   bool
 
 	shell                 string
 	format                string
@@ -303,7 +302,8 @@ func (w *Writer) Write(background, foreground, text string) {
 			colorOverride = false
 		}
 		if colorOverride {
-			w.currentBackground, w.currentForeground = w.asAnsiColors(match[BG], match[FG])
+			w.current.Add(w.asAnsiColors(match[BG], match[FG]))
+			// w.current.Background(), w.current.Foreground() = w.asAnsiColors(match[BG], match[FG])
 		}
 	}
 	w.writeSegmentColors()
@@ -343,8 +343,7 @@ func (w *Writer) Write(background, foreground, text string) {
 	w.writeEscapedAnsiString(resetStyle.End)
 
 	// reset current
-	w.currentBackground = ""
-	w.currentForeground = ""
+	w.current.Pop()
 }
 
 func (w *Writer) String() (string, int) {
@@ -378,11 +377,11 @@ func (w *Writer) writeSegmentColors() {
 	// use correct starting colors
 	bg := w.background
 	fg := w.foreground
-	if !w.currentBackground.IsEmpty() {
-		bg = w.currentBackground
+	if !w.current.Background().IsEmpty() {
+		bg = w.current.Background()
 	}
-	if !w.currentForeground.IsEmpty() {
-		fg = w.currentForeground
+	if !w.current.Foreground().IsEmpty() {
+		fg = w.current.Foreground()
 	}
 
 	// ignore processing fully tranparent colors
@@ -408,45 +407,14 @@ func (w *Writer) writeSegmentColors() {
 	}
 
 	// set current colors
-	w.currentBackground = bg
-	w.currentForeground = fg
+	w.current.Add(bg, fg)
 }
 
 func (w *Writer) writeColorOverrides(match map[string]string, background string, i int) int {
 	position := i
 	// check color reset first
 	if match[ANCHOR] == resetStyle.AnchorEnd {
-		// make sure to reset the colors if needed
-		position += len([]rune(resetStyle.AnchorEnd)) - 1
-
-		// do not reset when colors are identical
-		if w.currentBackground == w.background && w.currentForeground == w.foreground {
-			return position
-		}
-
-		// do not restore colors at the end of the string, we print it anyways
-		if position == len(w.runes)-1 {
-			return position
-		}
-
-		if w.transparent {
-			w.writeEscapedAnsiString(transparentEnd)
-		}
-
-		if w.background.IsClear() {
-			w.writeEscapedAnsiString(backgroundStyle.End)
-		}
-
-		if w.currentBackground != w.background && !w.background.IsClear() {
-			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
-		}
-
-		if (w.currentForeground != w.foreground || w.transparent) && !w.foreground.IsClear() {
-			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
-		}
-
-		w.transparent = false
-		return position
+		return w.endColorOverride(position)
 	}
 
 	position += len([]rune(match[ANCHOR])) - 1
@@ -465,48 +433,110 @@ func (w *Writer) writeColorOverrides(match map[string]string, background string,
 	if match[FG] == Transparent && len(match[BG]) == 0 {
 		match[BG] = background
 	}
-	w.currentBackground, w.currentForeground = w.asAnsiColors(match[BG], match[FG])
+
+	bg, fg := w.asAnsiColors(match[BG], match[FG])
 
 	// ignore processing fully tranparent colors
-	w.invisible = w.currentForeground.IsTransparent() && w.currentBackground.IsTransparent()
+	w.invisible = fg.IsTransparent() && bg.IsTransparent()
 	if w.invisible {
 		return position
 	}
 
 	// make sure we have colors
-	if w.currentForeground.IsEmpty() {
-		w.currentForeground = w.foreground
+	if fg.IsEmpty() {
+		fg = w.foreground
 	}
-	if w.currentBackground.IsEmpty() {
-		w.currentBackground = w.background
+	if bg.IsEmpty() {
+		bg = w.background
 	}
 
-	if w.currentForeground.IsTransparent() && len(w.TerminalBackground) != 0 {
+	w.current.Add(bg, fg)
+
+	if w.current.Foreground().IsTransparent() && len(w.TerminalBackground) != 0 {
 		background := w.getAnsiFromColorString(w.TerminalBackground, false)
 		w.writeEscapedAnsiString(fmt.Sprintf(colorise, background))
-		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground.ToForeground()))
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background().ToForeground()))
 		return position
 	}
 
-	if w.currentForeground.IsTransparent() && !w.currentBackground.IsTransparent() {
+	if w.current.Foreground().IsTransparent() && !w.current.Background().IsTransparent() {
 		w.transparent = true
-		w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.currentBackground))
+		w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.current.Background()))
 		return position
 	}
 
-	if w.currentBackground != w.background {
+	if w.current.Background() != w.background {
 		// end the colors in case we have a transparent background
-		if w.currentBackground.IsTransparent() {
+		if w.current.Background().IsTransparent() {
 			w.writeEscapedAnsiString(backgroundEnd)
 		} else {
-			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground))
+			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
 		}
 	}
 
-	if w.currentForeground != w.foreground {
-		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentForeground))
+	if w.current.Foreground() != w.foreground {
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
+	}
+
+	return position
+}
+
+func (w *Writer) endColorOverride(position int) int {
+	// pop the last colors from the stack
+	defer w.current.Pop()
+
+	// make sure to reset the colors if needed
+	position += len([]rune(resetStyle.AnchorEnd)) - 1
+
+	// reset colors to previous when we have > 2 in stack
+	// - first is always the starting colors used in Write()
+	// - second is the first override
+	// as soon as we have  more than 2, we can pop the last one
+	// and print the previous override as it wasn't ended yet
+	if w.current.Len() >= 3 {
+		fg := w.current.Foreground()
+		bg := w.current.Background()
+
+		w.current.Pop()
+
+		if w.current.Background() != bg {
+			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
+		}
+
+		if w.current.Foreground() != fg {
+			w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
+		}
+
+		return position
+	}
+
+	// do not reset when colors are identical
+	if w.current.Background() == w.background && w.current.Foreground() == w.foreground {
+		return position
+	}
+
+	// do not restore colors at the end of the string, we print it anyways
+	if position == len(w.runes)-1 {
+		return position
+	}
+
+	if w.transparent {
+		w.writeEscapedAnsiString(transparentEnd)
+	}
+
+	if w.background.IsClear() {
+		w.writeEscapedAnsiString(backgroundStyle.End)
+	}
+
+	if w.current.Background() != w.background && !w.background.IsClear() {
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
+	}
+
+	if (w.current.Foreground() != w.foreground || w.transparent) && !w.foreground.IsClear() {
+		w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
 	}
 
+	w.transparent = false
 	return position
 }
 
diff --git a/src/ansi/ansi_writer_hyperlink.go b/src/ansi/ansi_writer_hyperlink.go
index 3846678b..7a27c166 100644
--- a/src/ansi/ansi_writer_hyperlink.go
+++ b/src/ansi/ansi_writer_hyperlink.go
@@ -76,8 +76,9 @@ func (w *Writer) replaceHyperlink(text string) string {
 		return strings.Replace(text, results["ALL"], linkText, 1)
 	}
 
-	// we only care about the length of the text part
-	w.length += runewidth.StringWidth(linkText)
+	// we only care about the length of the actual text part
+	characters := w.trimAnsi(linkText)
+	w.length += runewidth.StringWidth(characters)
 
 	if w.Plain {
 		return linkText
diff --git a/src/ansi/colors.go b/src/ansi/colors.go
index fa95b45a..6c5590ce 100644
--- a/src/ansi/colors.go
+++ b/src/ansi/colors.go
@@ -19,6 +19,61 @@ type ColorString interface {
 	ToColor(colorString string, isBackground bool, trueColor bool) Color
 }
 
+type ColorSet struct {
+	Foreground Color
+	Background Color
+}
+
+type ColorHistory []*ColorSet
+
+func (c *ColorHistory) Len() int {
+	return len(*c)
+}
+
+func (c *ColorHistory) Add(background, foreground Color) {
+	colors := &ColorSet{
+		Foreground: foreground,
+		Background: background,
+	}
+
+	if c.Len() == 0 {
+		*c = append(*c, colors)
+		return
+	}
+
+	last := (*c)[c.Len()-1]
+	// never add the same colors twice
+	if last.Foreground == colors.Foreground && last.Background == colors.Background {
+		return
+	}
+
+	*c = append(*c, colors)
+}
+
+func (c *ColorHistory) Pop() {
+	if c.Len() == 0 {
+		return
+	}
+
+	*c = (*c)[:c.Len()-1]
+}
+
+func (c *ColorHistory) Background() Color {
+	if c.Len() == 0 {
+		return emptyColor
+	}
+
+	return (*c)[c.Len()-1].Background
+}
+
+func (c *ColorHistory) Foreground() Color {
+	if c.Len() == 0 {
+		return emptyColor
+	}
+
+	return (*c)[c.Len()-1].Foreground
+}
+
 // Color is an ANSI color code ready to be printed to the console.
 // Example: "38;2;255;255;255", "48;2;255;255;255", "31", "95".
 type Color string
diff --git a/src/template/compare.go b/src/template/compare.go
index 5a69a112..52462f01 100644
--- a/src/template/compare.go
+++ b/src/template/compare.go
@@ -30,6 +30,9 @@ func gt(e1, e2 interface{}) bool {
 	if val, OK := e1.(int); OK {
 		return val > interFaceToInt(e2)
 	}
+	if val, OK := e1.(int64); OK {
+		return val > int64(interFaceToInt(e2))
+	}
 	if val, OK := e1.(float64); OK {
 		return val > interfaceToFloat64(e2)
 	}
diff --git a/themes/M365Princess.omp.json b/themes/M365Princess.omp.json
index 4d1368e4..12bb380c 100644
--- a/themes/M365Princess.omp.json
+++ b/themes/M365Princess.omp.json
@@ -1,5 +1,15 @@
 {
   "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
+  "palette": {
+    "white": "#FFFFFF",
+    "tan": "#CC3802",
+    "teal": "#047E84",
+    "plum": "#9A348E",
+    "blush": "#DA627D",
+    "salmon": "#FCA17D",
+    "sky": "#86BBD8",
+    "teal_blue": "#33658A"
+  },
   "blocks": [
     {
       "alignment": "left",
@@ -8,15 +18,15 @@
           "type": "text",
           "style": "diamond",
           "leading_diamond": "\ue0b6",
-          "foreground": "#ffffff",
-          "background": "#cc3802",
+          "foreground": "p:white",
+          "background": "p:tan",
           "template": "{{ if .Env.PNPPSHOST }} \uf8c5 {{ .Env.PNPPSHOST }} {{ end }}"
         },
         {
           "type": "text",
           "style": "powerline",
-          "foreground": "#ffffff",
-          "background": "#047e84",
+          "foreground": "p:white",
+          "background": "p:teal",
           "powerline_symbol": "\ue0b0",
           "template": "{{ if .Env.PNPPSSITE }} \uf2dd {{ .Env.PNPPSSITE }}{{ end }}"
         },
@@ -24,8 +34,8 @@
           "type": "text",
           "style": "diamond",
           "trailing_diamond": "\ue0b4",
-          "foreground": "#ffffff",
-          "background": "#047e84",
+          "foreground": "p:white",
+          "background": "p:teal",
           "template": "{{ if .Env.PNPPSSITE }}\u00A0{{ end }}"
         }
       ],
@@ -35,16 +45,16 @@
       "alignment": "left",
       "segments": [
         {
-          "background": "#9A348E",
-          "foreground": "#ffffff",
+          "background": "p:plum",
+          "foreground": "p:white",
           "leading_diamond": "\ue0b6",
           "style": "diamond",
           "template": "{{ .UserName }} ",
           "type": "session"
         },
         {
-          "background": "#DA627D",
-          "foreground": "#ffffff",
+          "background": "p:blush",
+          "foreground": "p:white",
           "powerline_symbol": "\ue0b0",
           "properties": {
             "style": "folder"
@@ -54,8 +64,8 @@
           "type": "path"
         },
         {
-          "background": "#FCA17D",
-          "foreground": "#ffffff",
+          "background": "p:salmon",
+          "foreground": "p:white",
           "powerline_symbol": "\ue0b0",
           "properties": {
             "branch_icon": "",
@@ -68,16 +78,16 @@
           "type": "git"
         },
         {
-          "background": "#86BBD8",
-          "foreground": "#ffffff",
+          "background": "p:sky",
+          "foreground": "p:white",
           "powerline_symbol": "\ue0b0",
           "style": "powerline",
           "template": " \ue718 {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} ",
           "type": "node"
         },
         {
-          "background": "#33658A",
-          "foreground": "#ffffff",
+          "background": "p:teal_blue",
+          "foreground": "p:white",
           "properties": {
             "time_format": "15:04"
           },
diff --git a/themes/devious-diamonds.omp.yaml b/themes/devious-diamonds.omp.yaml
index c3b8268f..ffda11a8 100644
--- a/themes/devious-diamonds.omp.yaml
+++ b/themes/devious-diamonds.omp.yaml
@@ -11,7 +11,6 @@ blocks:
         template: "{{ .PromptMark }}"
       - type: os
         style: diamond
-        trailing_diamond: <transparent,></>
         foreground: cyan
         properties:
           alpine: 
@@ -67,7 +66,7 @@ blocks:
         foreground: black
         properties:
           display_default: false
-        template: " ﴃ Subscription {{ .Name }} ({{ if .EnvironmentName | contains 'AzureCloud' }}{{ 'Global' }}{{ else }}{{ .EnvironmentName }}{{ end }}) "
+        template: " ﴃ Subscription {{ .Name }} ({{ if .EnvironmentName | contains \"AzureCloud\" }}{{ \"Global\" }}{{ else }}{{ .EnvironmentName }}{{ end }}) "
       - type: azfunc
         style: powerline
         powerline_symbol: 
@@ -84,13 +83,13 @@ blocks:
         foreground: black
         properties:
           fetch_version: true
-        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
+        template: " ﳑ {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
       - type: java
         style: powerline
         powerline_symbol: 
         background: lightCyan
         foreground: black
-        template: "  {{ .Full }}"
+        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
       - type: julia
         style: powerline
         powerline_symbol: 
@@ -118,7 +117,7 @@ blocks:
         powerline_symbol: 
         background: lightCyan
         foreground: black
-        template: "  {{ .Full }} "
+        template: "  {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }} "
       - type: project
         style: powerline
         powerline_symbol: 
@@ -143,6 +142,24 @@ blocks:
           display_mode: files
           fetch_version: true
         template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
+      - type: rust
+        style: powerline
+        powerline_symbol: 
+        background: lightRed
+        foreground: black
+        properties:
+          display_mode: files
+          fetch_version: true
+        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
+      - type: swift
+        style: powerline
+        powerline_symbol: 
+        background: blue
+        foreground: black
+        properties:
+          display_mode: files
+          fetch_version: true
+        template: "  {{ if .Error }}{{ .Error }}{{ else }}{{ .Full }}{{ end }} "
       - type: time
         style: powerline
         powerline_symbol: 
@@ -184,7 +201,7 @@ blocks:
         style: plain
         foreground: cyan
         template: " ╚"
-      - type: exit
+      - type: status
         style: diamond
         leading_diamond: 
         background: blue
diff --git a/website/docs/contributors.md b/website/docs/contributors.md
index 3bfe6abc..3ecb5494 100644
--- a/website/docs/contributors.md
+++ b/website/docs/contributors.md
@@ -272,6 +272,10 @@ Thanks goes to these wonderful people ([emoji key][acek]):
       <td align="center" valign="top" width="14.28%"><a href="https://carlwa.com"><img src="https://avatars.githubusercontent.com/u/2829438?v=4?s=100" width="100px;" alt="Carl Walsh"/><br /><sub><b>Carl Walsh</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=darthwalsh" title="Documentation">📖</a></td>
       <td align="center" valign="top" width="14.28%"><a href="https://github.com/ercpereda"><img src="https://avatars.githubusercontent.com/u/13546685?v=4?s=100" width="100px;" alt="Ernesto R. C. Pereda"/><br /><sub><b>Ernesto R. C. Pereda</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ercpereda" title="Code">💻</a> <a href="#design-ercpereda" title="Design">🎨</a> <a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=ercpereda" title="Documentation">📖</a></td>
     </tr>
+    <tr>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/0Ky"><img src="https://avatars.githubusercontent.com/u/16103757?v=4?s=100" width="100px;" alt="ｃｒｙｐｔｉｘ"/><br /><sub><b>ｃｒｙｐｔｉｘ</b></sub></a><br /><a href="https://github.com/JanDeDobbeleer/oh-my-posh/commits?author=0Ky" title="Documentation">📖</a></td>
+      <td align="center" valign="top" width="14.28%"><a href="https://github.com/ehawman"><img src="https://avatars.githubusercontent.com/u/52979227?v=4?s=100" width="100px;" alt="Evan Hawman"/><br /><sub><b>Evan Hawman</b></sub></a><br /><a href="#design-ehawman" title="Design">🎨</a></td>
+    </tr>
   </tbody>
 </table>
 
diff --git a/website/docs/faq.mdx b/website/docs/faq.mdx
index bd7b502b..c12ecbf1 100644
--- a/website/docs/faq.mdx
+++ b/website/docs/faq.mdx
@@ -26,7 +26,19 @@ oh-my-posh enable notice
 ### The prompt is slow (delay in showing the prompt between commands)
 
 :::tip Windows
-Make sure Windows Defender, or your AntiVirus, has an exclusion [configured][exclusion].
+The delay you're experiencing may be linked to the real-time protection features of antivirus software.
+
+To potentially alleviate this delay, ensure that Windows Defender or your primary antivirus software has an [exclusion][exclusion] set for the full path of the executable. To identify the full path of the executable in question, you can run the following command in a PowerShell prompt: 
+
+```powershell
+(Get-Command oh-my-posh).Source
+```
+
+Additionally, check your antivirus settings for features that actively scan scripts during execution. Even with exclusions set, some antivirus programs might still actively scan scripts, which can introduce a noticeable delay.
+:::
+
+:::caution
+Always proceed with caution to maintain a balance between system performance and security. Modifying security settings or adding exclusions can expose your system to potential threats.
 :::
 
 <Tabs


Test patch:
diff --git a/src/ansi/ansi_writer_test.go b/src/ansi/ansi_writer_test.go
index 24011b6a..c23ad58c 100644
--- a/src/ansi/ansi_writer_test.go
+++ b/src/ansi/ansi_writer_test.go
@@ -195,6 +195,12 @@ func TestWriteANSIColors(t *testing.T) {
 			Expected: "\x1b[40m\x1b[30mtest\x1b[0m",
 			Colors:   &Colors{Foreground: "black", Background: "white"},
 		},
+		{
+			Case:     "Nested override",
+			Input:    "hello, <red>world, <white>rabbit</> hello</>",
+			Expected: "\x1b[47m\x1b[30mhello, \x1b[31mworld, \x1b[37mrabbit\x1b[31m hello\x1b[0m",
+			Colors:   &Colors{Foreground: "black", Background: "white"},
+		},
 	}
 
 	for _, tc := range cases {