Repository: streamlit/streamlit
Pull request: Remove `st.logo` if stale
## Describe your changes
Currently, a stale logo persists between re-runs for a SPA, MPA v1, & MPA v2. This PR attaches the `scriptRunId` to the `st.logo` call so that a stale logo is cleaned up on script finish (when `clearStaleNodes` is called).

## GitHub Issue Link (if applicable)
Closes #9336 

## Testing Plan
- Unit/Integration Tests: Added ✅ 
- Manual Testing: ✅ 

Problem statement:
Stale `st.logo` not removed
### Summary

After the MPAv2 refactor, a stale logo is not removed for single-page apps, MPA v1, & MPA v2

### Steps To Reproduce

1. Create an app with `st.logo` in the script
2. Run the script -> logo appears as expected
3. Comment out the `st.logo` call
4. Re-run script
5. Notice the logo is still displayed

### Is this a regression?
- [x] Yes, this used to work in a previous version.

Hints from issue discussion:
**If this issue affects you, please react with a 👍 (thumbs up emoji) to the initial post.**

Your feedback helps us prioritize which bugs to investigate and address first.

![Visits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fstreamlit%2Fstreamlit%2Fissues%2F9336&title=visits&edge_flat=false)

Commit message: Update comments

Patch to review:
diff --git a/frontend/app/src/App.tsx b/frontend/app/src/App.tsx
index 1cde884a75c4..ac53a8c0c015 100644
--- a/frontend/app/src/App.tsx
+++ b/frontend/app/src/App.tsx
@@ -739,9 +739,17 @@ export class App extends PureComponent<Props, State> {
   }
 
   handleLogo = (logo: Logo, metadata: ForwardMsgMetadata): void => {
+    // Pass the current page & run ID for cleanup
+    const { scriptRunId } = this.state
+    const { activeScriptHash } = metadata
+
     this.setState(
       {
-        elements: this.pendingElementsBuffer.appRootWithLogo(logo, metadata),
+        elements: this.pendingElementsBuffer.appRootWithLogo(
+          logo,
+          activeScriptHash,
+          scriptRunId
+        ),
       },
       () => {
         this.pendingElementsBuffer = this.state.elements
diff --git a/frontend/lib/src/AppNode.ts b/frontend/lib/src/AppNode.ts
index 506c631686b4..515988e1625d 100644
--- a/frontend/lib/src/AppNode.ts
+++ b/frontend/lib/src/AppNode.ts
@@ -50,6 +50,9 @@ interface AppLogo {
   logo: Logo
   // Associated scriptHash that created the logo
   activeScriptHash: string
+
+  // Associated scriptRunId that created the logo
+  scriptRunId: string
 }
 
 /**
@@ -676,11 +679,15 @@ export class AppRoot {
     return this.appLogo?.logo ?? null
   }
 
-  public appRootWithLogo(logo: Logo, metadata: ForwardMsgMetadata): AppRoot {
-    const { activeScriptHash } = metadata
+  public appRootWithLogo(
+    logo: Logo,
+    activeScriptHash: string,
+    scriptRunId: string
+  ): AppRoot {
     return new AppRoot(this.mainScriptHash, this.root, {
       logo,
       activeScriptHash,
+      scriptRunId,
     })
   }
 
@@ -791,6 +798,9 @@ export class AppRoot {
       this.bottom.clearStaleNodes(currentScriptRunId, fragmentIdsThisRun) ||
       new BlockNode(this.mainScriptHash)
 
+    const appLogo =
+      this.appLogo?.scriptRunId === currentScriptRunId ? this.appLogo : null
+
     return new AppRoot(
       this.mainScriptHash,
       new BlockNode(
@@ -799,7 +809,7 @@ export class AppRoot {
         new BlockProto({ allowEmpty: true }),
         currentScriptRunId
       ),
-      this.appLogo
+      appLogo
     )
   }
 


Code review comments:
diff_hunk: @@ -739,9 +739,17 @@ export class App extends PureComponent<Props, State> {
   }
 
   handleLogo = (logo: Logo, metadata: ForwardMsgMetadata): void => {
+    // Pass the current page & run ID for cleanup
+    const { scriptRunId } = this.state
+    const { activeScriptHash } = metadata
+
     this.setState(
       {
-        elements: this.pendingElementsBuffer.appRootWithLogo(logo, metadata),
+        elements: this.pendingElementsBuffer.appRootWithLogo(
+          logo,
+          activeScriptHash,
+          scriptRunId
path: frontend/app/src/App.tsx
text: @user1:
Not strongly held opinion, but it seems like `activeScriptHash` and `scriptRunId` should be in an object representing the `metadata` for the logo. I think it will just be cleaner. I think it goes well with the function title (appRootWithLogo takes in a logo and associated metadata) and future-proofs as we add more metadata associated with the logo.

@author:
Think thats fair - made an update let me know if it makes sense to you or not!