Advanced Clearing
Let's understand this. In the result data, we break down the data into its constituent symbols, then get the last symbol from the result data, and delete it from the result data, to have one less of the operation.
However, since there are some operations we added with "(", we have to remove two symbols in such.

Let's work on it now in App.vue in the clearScreen function:
const clearScreen = () => {
  if (result.value) {
    let index;
    const escaped = buttons.map(
      (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
      ).join('|')
    const allMatch = new RegExp(`(${escaped})`, 'g')
    const all = (result.value as string).match(allMatch)

    const lastSymbol = all?.slice(-1)[0]
    if (lastSymbol){
      index = result.value.lastIndexOf(lastSymbol)
      if (lastSymbol == "("){
        index = result.value.lastIndexOf(all?.slice(-2, -1)[0])
      }
      result.value = result.value.slice(0, index)
      if (result.value == "") result.value = undefined      
    }
  }
}
Sup powers
All along, we've being rendered result data on screen using Moustache syntax, but in order to render html, we will need to transform rendering to v-html in AppScreen...

From
<div class="absolute bottom-2 right-2 text-4xl tracking-tighter">
    {{ result ?? '0' }}
</div>
To
<div class="absolute bottom-2 right-2 text-4xl tracking-tighter"
v-html="result ?? '0'">
</div>
Now, on clicking xy button, we'll do two things for display:
1~ introduce and toggle superscript flag
2~ during calculation, replace the sup tags with **( )

Note: Minor change to basicMatch regex in buttonAction in AppButtons component for point to work. Also, I'm removing / from the pattern since it will match the / in </sup> hence assign it a wrong conditionality, and also since / is not a symbol in the buttons.
const basicMatch = /[+\-()÷%×πe\.\d]/g
Clicking on xy, in buttonAction, we invoke supOps emitter.
/*...*/
const emit = defineEmits([
  'basicOps', 'evalOps', 'clearOps', 'supOps'
])
/*...*/
const buttonAction = (symbol: string) => {
  const basicMatch = /[+\-()÷%×πe\.\d]/g
  /*...*/
  } else if (symbol == "x<sup>y</sup>"){
    emit("supOps")
  }
}
In App.vue, let's first initialize the superscript variable.
const superscript = ref(false)
When supOps listener is triggered, call updateScreenSup. This will open the <sup> tag, and then set superscript flag to true so that every value after that will be a power.
When xy is clicked again, close the result data with </sup> then set flag to false.
<script setup lang="ts">
// ...
const updateScreenSup = () => {
  if (result.value != undefined) {
    if (superscript.value) result.value += "</sup>"
    else result.value += "<sup>"
    superscript.value = !superscript.value
  }
}
// ...
</script>
<template>
<!-- ... -->
<app-buttons
  @basic-ops="(n: string) => updateScreen(n)"
  @eval-ops="calculateScreen"
  @clear-ops="clearScreen"
  @sup-ops="updateScreenSup"
></app-buttons>
<!-- ... -->
Then right before eval, we need to transform sup tags to ** as the power operator in our result data.
const updatePower = (_in: string): string => {
    const powerMatch = /(\d+)<sup>(\d+)<\/sup>/g
    const replace = "$1**($2)"
    _in = _in.replace(powerMatch, replace)
    return _in
}
Now, calling the function for updatePower in calculateScreen, we have
// ...
const pattern = new RegExp(Object.keys(chars).join('|'), 'ig')
let temp = addMultiplier(result.value)
temp = updatePower(temp)
temp = temp.replace(pattern, (m: string) => chars[m])    
result.value = `${eval(temp)}`
// ...
Another caveat
On setting powers without unsetting, for instance, data like 8<sup>2, or even 5log(100, without actually setting the close tags, eval would break.
We need to write a function that corrects for that, even for nested operations like,
sqrt(4<sup>2</sup>-5<sup>3</sup> ~ sqrt(42-53, missing the ending ),

To fix for this, we use the function below,
function correctSup(str: string):
    string {
    if (str.slice(-1) == "(") str += "1)"
    if (str.slice(-5) == "<sup>") str += "1</sup>"
    const pattern = /\(|<sup>|\)|<\/sup>/g
    let matches = str.match(pattern)

    if (matches !== null) {
        if (matches.length % 2 === 0) return str
        let counts_sup = matches!.filter((x: string) => ["<sup>", "</sup>"].includes(x)).length
        if (counts_sup % 2 !== 0)
            return str += "</sup>"
        else 
            return str += ")"
    } else {
        return str
    }
}
Let's explain the function above.
First of all, we fix for errors that might arise due to incomplete closing tags such as 8*5( and 5<sup>.
Next, we look for the symbols, (, ), <sup>, </sup> in the result and store in matches.
Matches not being null, we assume if there's evenness, then all tags match.
Otherwise, we look for a pair of tags that are not even, then correct for that to solve for the incomplete tags.

This solves for inputs such as:
let input1 = "sqrt(4<sup>2</sup>-5<sup>3</sup>", 
    // becomes sqrt(4<sup>2</sup>-5<sup>3</sup>)
    input2 = "8<sup>4", 
    // becomes 8<sup>4</sup>
    input3 = "4<sup>2</sup>-5<sup>3", 
    // becomes 4<sup>2</sup>-5<sup>3</sup>
    input4 = "8<sup>2",
    // becomes 8<sup>2</sup>
    input4 = "8<sup>",
    // becomes 8<sup>1</sup>
    input5 = "5log(100",
    // becomes 5log(100)
    input6 = "8<sup>2</sup>+5×(3"
    // becomes "8<sup>2</sup>+5×(3)"
Now, let's bind the function correctSup to the flow of calculateScreen in App.vue component as we compute the result of the screen data.
// ...
const pattern = new RegExp(Object.keys(chars).join('|'), 'ig')
let temp = addMultiplier(result.value)
temp = correctSup(temp)
temp = updatePower(temp)
temp = temp.replace(pattern, (m: string) => chars[m])    
result.value = `${eval(temp)}`
// ...
In the next lesson, we are going to focus on the transition animations in AppScreen.vue