Skip to content

Instantly share code, notes, and snippets.

@volkanunsal
Last active March 7, 2026 16:22
Show Gist options
  • Select an option

  • Save volkanunsal/94db50629cad816eca84c836e0232a4f to your computer and use it in GitHub Desktop.

Select an option

Save volkanunsal/94db50629cad816eca84c836e0232a4f to your computer and use it in GitHub Desktop.
Collapse Studio Buttons Container

Collapse Studio Buttons Container

Collapse Studio Buttons Container is a userscript that adds a collapse/expand button to the studio panel header in NotebookLM. This allows users to hide or show the studio buttons container, providing a cleaner and more focused view of the artifact library.

toggle

Key Features

  • Adds a collapse/expand button to the studio panel header.
  • Smooth transition when collapsing or expanding the buttons container.
  • Enhances user experience by reducing visual clutter.

Getting Started

🔧 Installation

  1. Install Tampermonkey browser extension
  2. Click on the "Raw" button of the script below.
  3. Visit notebooklm.google.com to see the magic! ✨
// @ts-nocheck
// ==UserScript==
// @name Collapse Studio Buttons Container
// @namespace https://github.com/volkanunsal
// @version 2025-09-10
// @description Collapses the studio buttons container by default
// @author Volkan Unsal
// @downloadURL https://gist.githubusercontent.com/volkanunsal/94db50629cad816eca84c836e0232a4f/raw/collapse-studio-buttons-container.user.js
// @updateURL https://gist.githubusercontent.com/volkanunsal/94db50629cad816eca84c836e0232a4f/raw/collapse-studio-buttons-container.user.js
// @match https://notebooklm.google.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
"use strict";(()=>{var k=class{constructor(e={}){this.namespace=e.namespace,this.prefix=[this.namespace,e.prefix].filter(Boolean).join(" "),this.enabled=e.enabled!==!1,this.timestamp=e.timestamp!==!1,this.timestampFormat=e.timestampFormat||"locale"}getTimestamp(){let e=new Date;return this.timestampFormat==="ISO"?e.toISOString():e.toLocaleString()}formatMessage(e,...n){let c=[this.prefix];return this.timestamp&&c.push(`[${this.getTimestamp()}]`),c.push(`[${e.toUpperCase()}]`),[c.join(" "),...n]}debug(...e){this.enabled&&console.debug(...this.formatMessage("debug",...e))}info(...e){this.enabled&&console.info(...this.formatMessage("info",...e))}warn(...e){this.enabled&&console.warn(...this.formatMessage("warn",...e))}error(...e){this.enabled&&console.error(...this.formatMessage("error",...e))}log(...e){this.enabled&&console.log(...this.formatMessage("log",...e))}custom(e,...n){this.enabled&&console.log(...this.formatMessage(e,...n))}group(e,n){if(!this.enabled)return n();console.group(`${this.prefix} ${e}`);try{n()}finally{console.groupEnd()}}groupCollapsed(e,n){if(!this.enabled)return n();console.groupCollapsed(`${this.prefix} ${e}`);try{n()}finally{console.groupEnd()}}table(e,n){this.enabled&&(this.info("Table data:"),console.table(e,n))}time(e){this.enabled&&console.time(`${this.prefix} ${e}`)}timeEnd(e){this.enabled&&console.timeEnd(`${this.prefix} ${e}`)}timeLog(e){this.enabled&&console.timeLog(`${this.prefix} ${e}`)}enable(){this.enabled=!0}disable(){this.enabled=!1}setPrefix(e){this.prefix=e}};function C(f={}){return new k(f)}function P(f,e){function n(c){return c.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"")}if(window.trustedTypes){let u=window.trustedTypes.createPolicy("myHTMLPolicy",{createHTML:n}).createHTML(e);f.innerHTML=u}else{let c=n(e);f.innerHTML=c}}async function B(f){let{tagName:e,id:n,attributes:c={},parentSelector:u="body",parentElement:b,textContent:s,innerHTML:h,innerElement:w,returnElement:S=!0,checkExisting:V=!0,replaceExisting:q=!1,insertionMethod:E="append",contextElement:p,waitTimeout:I=3e4,persistent:y=!1,autoRemove:x=!0,debounceDelay:F=500,observerConfig:g={childList:!0,subtree:!0,attributes:!1,attributeOldValue:!1,characterData:!1,characterDataOldValue:!1},namespace:A,onMount:v}=f,t=C({prefix:"[createElement]",namespace:A}),r={action:"create",tagName:e,id:n,parentMethod:u?"selector":"element",parentIdentifier:u||(b?b.tagName:"unknown"),waitTimeout:I,insertionMethod:E};if(t.info(`Starting element creation: <${e}> with ID="${n}"${p?" (conditional)":""} using ${E} insertion`),!e||typeof e!="string")return t.error("Element creation failed: Tag parameter is required and must be a string",r),null;if(!n||typeof n!="string")return t.error("Element creation failed: ID parameter is required and must be a non-empty string",r),null;if(!u&&!b)return t.error("Element creation failed: Either parentSelector or parentElement must be provided",r),null;if(E!=="append"&&E!=="insertBeforeElement"&&E!=="prepend")return t.error("Element creation failed: insertionMethod must be 'append', 'insertBeforeElement', or 'prepend'",r),null;function T(a,i,o){try{let m=new DOMParser().parseFromString(i,"text/html"),l=m.querySelector("parsererror");if(l)throw new Error(`HTML parsing error: ${l.textContent}`);let $=Array.from(m.body.childNodes);return $.length===0?(t.warn(`No valid elements found in HTML string: "${i.substring(0,50)}${i.length>50?"...":""}"`,o),!1):($.forEach(M=>{let O=a.ownerDocument.importNode(M,!0);a.appendChild(O)}),t.debug(`Created DOM elements programmatically: "${i.substring(0,50)}${i.length>50?"...":""}" (${$.length} elements)`,o),!0)}catch(d){t.warn(`DOMParser failed, attempting fallback method: ${d.message}`,o);try{return P(a,i),t.debug(`Successfully created DOM elements via fallback method: "${i.substring(0,50)}${i.length>50?"...":""}"`,o),!0}catch(m){return t.error(`DOM insertion failed: ${m.message}`,{...o,originalError:d.message,fallbackError:m.message}),!1}}}function D(){try{let a=null;if(V&&(a=document.getElementById(n),a))if(r.action="exists",q)t.info(`Found existing element with ID="${n}", replacing as requested`,r),a.remove(),r.action="replace";else return t.warn(`Element with ID="${n}" already exists, skipping creation. Use replaceExisting=true to replace.`,r),S?a:null;let i=b;if(!i&&u){let l=document.querySelector(u);if(!l)return t.error(`Element creation failed: Parent element not found with selector: ${u}`,r),null;i=l,t.debug(`Found parent element with selector: ${u}`,r)}if(!i)return t.error("Element creation failed: No valid parent element found",r),null;let o=document.createElement(e);t.debug(`Created DOM element: <${e}>`,r),o.setAttribute("id",n),t.debug(`Set required ID attribute: id="${n}"`,r);let d=Object.keys(c).length;if(d>0&&(Object.entries(c).forEach(([l,$])=>{$!=null&&(o.setAttribute(l,String($)),t.debug(`Set attribute: ${l}="${$}"`,r))}),t.debug(`Applied ${d} additional attributes to element`,r)),w&&typeof w=="function")try{let l=w();l instanceof HTMLElement?(o.appendChild(l),t.debug("Inserted child element via innerElement callback",r)):(t.warn("innerElement callback did not return a valid HTMLElement, falling back to other content methods",r),s!==void 0?(o.textContent=s,t.debug(`Set textContent: "${s.substring(0,100)}${s.length>100?"...":""}"`,r)):h!==void 0&&T(o,h,r))}catch(l){t.error("Error executing innerElement callback, falling back to other content methods:",l,r),s!==void 0?(o.textContent=s,t.debug(`Set textContent: "${s.substring(0,100)}${s.length>100?"...":""}"`,r)):h!==void 0&&T(o,h,r)}else s!==void 0?(o.textContent=s,t.debug(`Set textContent: "${s.substring(0,100)}${s.length>100?"...":""}"`,r)):h!==void 0&&T(o,h,r);if(E==="insertBeforeElement")if(i.parentElement)i.parentElement.insertBefore(o,i),t.debug("Inserted element before parent",r);else return t.error("Element insertion failed: Parent element has no parent to insert before",r),null;else E==="prepend"?(i.insertBefore(o,i.firstChild),t.debug("Prepended element as first child of parent",r)):(i.appendChild(o),t.debug("Appended element to end of parent",r));if(v&&typeof v=="function")try{v(o),t.debug(`Successfully called onMount callback for element ID="${n}"`,r)}catch(l){t.warn(`Error in onMount callback for element ID="${n}":`,l,r)}let m=`Element creation successful: <${e}> (ID="${n}") ${r.action==="replace"?"replaced and ":""}${E}ed to ${r.parentMethod==="selector"?`parent selected by "${u}"`:"provided parent element"}${p?" (after content element)":""}`;return t.info(m,{...r,success:!0,hasAttributes:d>0,hasContent:!!(s||h||w),parentTagName:i.tagName,elementPath:o.tagName+"#"+o.id+(o.className?`.${o.className.replace(/\s+/g,".")}`:"")}),S?o:null}catch(a){let i=`Element creation failed with exception: ${a.message||"Unknown error"}`;return t.error(i,{...r,success:!1,error:a.message||"Unknown error",stack:a.stack||"No stack trace available"}),null}}async function L(){try{if(typeof p=="function"){let a=await p();return a instanceof HTMLElement&&document.contains(a)}else if(typeof p=="string")return document.querySelector(p)!==null}catch(a){t.warn(`Error checking content element for element ID="${n}":`,a)}return!1}function H(){if(!y&&!x)return;t.info(`Setting up ${y?"persistent":""}${y&&x?" and ":""}${x?"auto-removal":""} monitoring for element ID="${n}"`);let a=null,i=new MutationObserver(async()=>{a&&clearTimeout(a),a=setTimeout(async()=>{t.debug(`Persistent monitoring check triggered for element ID="${n}"`);let d=await L(),m=document.getElementById(n);d?y&&!m?(t.info(`Context element found but managed element missing, recreating element ID="${n}"`),D()):t.debug(`Both context and managed elements exist for ID="${n}"`):x&&m?(t.info(`Context element no longer present, removing managed element ID="${n}"`),m.remove()):t.debug(`Context element no longer present for element ID="${n}"`)},F)}),o={childList:g.childList===!0,subtree:g.subtree===!0,attributes:g.attributes===!0,attributeOldValue:g.attributeOldValue===!0,characterData:g.characterData===!0,characterDataOldValue:g.characterDataOldValue===!0};i.observe(document.documentElement||document.body,o),t.debug(`Started persistent MutationObserver for element ID="${n}"`)}if(!p)return D();if(t.info(`Checking initial condition for element ID="${n}"`,{...r,waitCondition:typeof p=="function"?"callback":p,persistent:y}),await L()){t.info(`Initial content element already satisfied for element ID="${n}"`,r);let a=D();return(y||x)&&H(),a}return new Promise(a=>{let i=Date.now(),o=null,d=null,m=!1;function l(M){m||x&&(m=!0,o&&(clearTimeout(o),o=null),d&&(d.disconnect(),d=null,t.debug(`Disconnected MutationObserver for element ID="${n}"`,r)),a(M))}o=setTimeout(()=>{t.error(`Element creation failed: Wait condition timeout after ${I}ms for element ID="${n}"`,{...r,success:!1,timeoutReached:!0,waitCondition:typeof p=="function"?"callback":p}),l(null)},I),t.info(`Setting up MutationObserver for element ID="${n}"`,{...r,observerConfig:g,waitCondition:typeof p=="function"?"callback":p}),d=new MutationObserver(async M=>{if(!m&&(t.debug(`MutationObserver detected ${M.length} mutations for element ID="${n}"`,r),await L())){let O=Date.now()-i;t.info(`Wait condition satisfied via MutationObserver for element ID="${n}" after DOM changes`,{...r,elapsedTime:O});let _=D();(y||x)&&H(),l(_)}});let $={childList:g.childList===!0,subtree:g.subtree===!0,attributes:g.attributes===!0,attributeOldValue:g.attributeOldValue===!0,characterData:g.characterData===!0,characterDataOldValue:g.characterDataOldValue===!0};d.observe(document.documentElement||document.body,$),t.debug(`Started MutationObserver monitoring for element ID="${n}"`,{...r,target:"document.documentElement"})})}function N({selector:f,timeout:e=5e3,pollInterval:n=100}){return new Promise(c=>{let u=n,b=0,s=setInterval(()=>{let h=document.querySelector(f);h?(clearInterval(s),c(h)):b>=e&&(clearInterval(s),c(null)),b+=u},u)})}(async function(){"use strict";GM_addStyle(`
.create-artifact-buttons-container {
transition: height 0.3s ease-in-out, margin-bottom 0.3s ease-in-out, margin-top 0.3s ease-in-out, opacity 0.2s ease-in-out 0.1s, overflow 0s ease-in-out 0s;
margin-top: 16px !important;
margin-bottom: 16px !important;
height: 289px !important;
opacity: 1 !important;
}
.create-artifact-buttons-container.collapsed {
height: 0px !important;
opacity: 0 !important;
margin-top: 0 !important;
margin-bottom: 0 !important;
transition: height 0.3s ease-in-out, margin-bottom 0.3s ease-in-out, margin-top 0.3s ease-in-out, opacity 0.2s ease-in-out 0s, overflow 0s ease-in-out 0s;
overflow: hidden !important;
}
.panel-collapsed #collapse-studio-buttons-container-box, [aria-label="Close app viewer"] + #collapse-studio-buttons-container-box, [aria-label="Close report viewer"] + #collapse-studio-buttons-container-box {
display: none !important;
}
`);let f=C({prefix:"UserScript",namespace:"[CollapseStudioButtonsContainer]"}),e=GM_getValue("studioButtonsContainerCollapsed",!1);f.log(`Studio buttons container is currently ${e?"collapsed":"expanded"}`);let n=()=>document.querySelector(".create-artifact-buttons-container");(await N({selector:".create-artifact-buttons-container",timeout:3e3}))?.classList.toggle("collapsed",e),B({tagName:"span",namespace:"[CollapseStudioButtonsContainer]",id:"collapse-studio-buttons-container-box",insertionMethod:"insertBeforeElement",autoRemove:!1,attributes:{},parentSelector:".toggle-studio-panel-button",contextElement:".studio-panel",innerHTML:'<button mat-icon-button="" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger toggle-studio-panel-button mat-unthemed ng-star-inserted" mat-ripple-loader-class-name="mat-mdc-button-ripple" mat-ripple-loader-centered="" aria-label="Collapse studio panel" jslog="" style=""><span class="mat-mdc-button-persistent-ripple mdc-icon-button__ripple"></span><mat-icon role="img" aria-hidden="true" class="mat-icon notranslate material-symbols-outlined mat-icon-rtl-mirror google-symbols mat-icon-no-color" data-mat-icon-type="font">dock_to_top</mat-icon><span class="mat-focus-indicator"></span><span class="mat-mdc-button-touch-target"></span><span class="mat-ripple mat-mdc-button-ripple"></span></button>',onMount:u=>{u.addEventListener("click",async()=>{f.log("Toggle button clicked");let b=n();if(b){b.classList.toggle("collapsed");let s=b.classList.contains("collapsed");GM_setValue("studioButtonsContainerCollapsed",s),f.log(`Studio buttons container ${s?"collapsed":"expanded"}`)}else f.error("Create artifact buttons container not found")})}})})();})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment