Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save lunamoth/27717e92ded97d0f5e820d8897ade053 to your computer and use it in GitHub Desktop.
Save lunamoth/27717e92ded97d0f5e820d8897ade053 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Google AI Studio - 사이드바 자동 숨김 (v2.3 - 사용자 설정)
// @namespace http://tampermonkey.net/
// @version 2.3
// @description Google AI Studio 자동 숨김. 사용자가 직접 열었을 때 유지 시간 설정 가능.
// @author Gemini
// @match https://aistudio.google.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
// ========================================================================
// ★★★ 사용자 설정 영역 시작 ★★★
// ========================================================================
// 오른쪽 패널을 직접 열었을 때 자동으로 닫히지 않고 유지되는 시간 (밀리초 단위)
// 예: 5초 = 5000, 10초 = 10000, 30초 = 30000, 1분 = 60000
// 스크립트 편집기에서 이 숫자 값을 원하는 시간으로 변경하세요.
const USER_ACTION_GRACE_PERIOD_MS = 10000; // <-- 여기 숫자를 수정하세요 (현재 10초로 설정됨)
// (고급 설정) 핵심 확인 주기 (밀리초). 낮을수록 반응이 빠르지만 CPU 사용량이 늘 수 있습니다. (기본값: 250)
const CHECK_INTERVAL_MS = 250;
// (고급 설정) 초기 로딩 시 확인 최대 지속 시간 (밀리초). (기본값: 5000)
const INITIAL_CHECK_MAX_DURATION_MS = 5000;
// (고급 설정) 페이지 이동(네비게이션) 후 닫기 시도까지 지연 시간 (밀리초). (기본값: 50)
const NAV_CLOSE_DELAY_MS = 50;
// ========================================================================
// ★★★ 사용자 설정 영역 끝 ★★★
// ========================================================================
// --- 상태 변수 ---
let lastUserOpenTimestamp = 0; // 사용자가 마지막으로 열기 버튼 누른 시간 (Timestamp)
let checkIntervalId = null;
let isInitialCheckPhase = true; // 초기 확인 단계인지 여부
let initialCheckStartTime = Date.now();
let currentHref = document.location.href;
let isClosingRightPanel = false; // 스크립트가 닫기 진행 중인지 플래그
// --- 선택자 정의 ---
const LEFT_NAVBAR_SELECTOR = 'ms-navbar .layout-navbar';
const LEFT_COLLAPSE_BUTTON_SELECTOR = 'ms-navbar button[aria-label="Expand or collapse navigation menu"], ms-navbar button[aria-label*="탐색 메뉴"]';
const RIGHT_PANEL_SELECTOR = 'ms-right-side-panel';
const RIGHT_PANEL_CONTENT_SELECTOR = '.content-container'; // RIGHT_PANEL_SELECTOR 하위에서 검색
const RIGHT_PANEL_CLOSE_BUTTON_SELECTOR = 'ms-run-settings button[aria-label="Close run settings panel"], ms-run-settings button[aria-label*="닫기"]'; // RIGHT_PANEL_SELECTOR 하위에서 검색
const RIGHT_PANEL_OPEN_BUTTON_SELECTOR = 'button[aria-label="Run settings"], button[aria-label*="실행 설정"]'; // RIGHT_PANEL_SELECTOR 하위에서 검색
console.log(`AI Studio 자동 숨김 (v2.3 - 사용자 설정): 스크립트 시작됨. 유지 시간: ${USER_ACTION_GRACE_PERIOD_MS / 1000}초`);
// --- 디바운스 함수 ---
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func.apply(this, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// --- 오른쪽 패널 열기 버튼 리스너 ---
function attachOpenButtonListener() {
try {
const rightPanelElement = document.querySelector(RIGHT_PANEL_SELECTOR);
if (!rightPanelElement) return false;
const openButton = rightPanelElement.querySelector(RIGHT_PANEL_OPEN_BUTTON_SELECTOR);
if (openButton && openButton.getAttribute('data-listener-attached-v2.3') !== 'true') {
openButton.removeEventListener('click', handleOpenButtonClick); // 이전 리스너 제거
openButton.addEventListener('click', handleOpenButtonClick); // 새 리스너 추가
openButton.setAttribute('data-listener-attached-v2.3', 'true');
return true;
}
} catch (error) {
console.error('AI Studio 자동 숨김: attachOpenButtonListener 오류:', error);
}
return false;
}
// 열기 버튼 클릭 핸들러
function handleOpenButtonClick(event) {
if (event && event.isTrusted) {
console.log('%cAI Studio 자동 숨김: [사용자 액션] 오른쪽 패널 열기 버튼 클릭 감지 (타임스탬프 설정).', 'color: blue; font-weight: bold;');
lastUserOpenTimestamp = Date.now(); // 타임스탬프 기록
}
}
// --- 왼쪽 패널 닫기 ---
function closeLeftPanel() {
try {
const leftNavbar = document.querySelector(LEFT_NAVBAR_SELECTOR);
const leftCollapseButton = document.querySelector(LEFT_COLLAPSE_BUTTON_SELECTOR);
if (leftNavbar && leftCollapseButton && leftNavbar.classList.contains('expanded')) {
leftCollapseButton.click();
return true;
}
} catch (error) {
console.error('AI Studio 자동 숨김: closeLeftPanel 오류:', error);
}
return false;
}
// --- 오른쪽 패널 닫기 (타임스탬프 확인 후) ---
function closeRightPanelIfNeeded(reason = "Check") {
if (isClosingRightPanel) return false; // 닫기 진행 중이면 건너뜀
const timeSinceLastUserOpen = Date.now() - lastUserOpenTimestamp;
// 사용자가 직접 열었고, 설정된 유예 시간 이내라면 닫지 않음
if (lastUserOpenTimestamp > 0 && timeSinceLastUserOpen < USER_ACTION_GRACE_PERIOD_MS) {
// 유예 시간이 0이 아니고, 마지막 사용자 액션이 있었고, 그 시간이 유예 시간 내일 때만 로그 출력
if (USER_ACTION_GRACE_PERIOD_MS > 0) {
// console.log(`AI Studio 자동 숨김: 오른쪽 닫기 건너뜀 (사용자 유예 기간 ${Math.round((USER_ACTION_GRACE_PERIOD_MS - timeSinceLastUserOpen)/1000)}초 남음).`);
}
return false;
}
try {
const rightPanelElement = document.querySelector(RIGHT_PANEL_SELECTOR);
if (!rightPanelElement) return false;
const content = rightPanelElement.querySelector(RIGHT_PANEL_CONTENT_SELECTOR);
const closeButton = rightPanelElement.querySelector(RIGHT_PANEL_CLOSE_BUTTON_SELECTOR);
if (content && closeButton) { // 열려 있는 경우
isClosingRightPanel = true;
// console.log(`%cAI Studio 자동 숨김: 오른쪽 패널 자동 닫기 실행됨. (사유: ${reason})`, 'color: red;'); // 로그 간소화
try {
if (document.body.contains(closeButton)) {
closeButton.click();
} else {
console.warn('AI Studio 자동 숨김: 닫기 버튼 클릭 시점에 버튼이 사라짐.');
}
} catch (clickError) {
console.error('AI Studio 자동 숨김: 닫기 버튼 클릭 오류:', clickError);
} finally {
setTimeout(() => { isClosingRightPanel = false; }, 100); // 닫기 시도 후 플래그 해제
}
return true;
}
} catch (error) {
console.error('AI Studio 자동 숨김: closeRightPanelIfNeeded 오류:', error);
isClosingRightPanel = false;
}
return false;
}
// --- 핵심 주기적 확인 함수 ---
function persistentCheck() {
try {
closeLeftPanel();
closeRightPanelIfNeeded("Persistent Check");
attachOpenButtonListener();
if (isInitialCheckPhase) {
const leftOk = !document.querySelector(LEFT_NAVBAR_SELECTOR + '.expanded');
const rightOk = !document.querySelector(RIGHT_PANEL_SELECTOR + ' ' + RIGHT_PANEL_CONTENT_SELECTOR);
const elapsedTime = Date.now() - initialCheckStartTime;
if ((leftOk && rightOk && elapsedTime > 500) || (elapsedTime > INITIAL_CHECK_MAX_DURATION_MS)) {
isInitialCheckPhase = false;
console.log(`%cAI Studio 자동 숨김: === 초기 확인 단계 종료 === (경과: ${elapsedTime}ms). 상태: Left ${leftOk}, Right ${rightOk}`, 'color: green; font-weight: bold;');
}
}
} catch (error) {
console.error('AI Studio 자동 숨김: persistentCheck 오류:', error);
}
}
// --- URL 변경 처리 함수 ---
function handleUrlChange() {
console.log('%cAI Studio 자동 숨김: [URL 변경 감지] 즉시 닫기 시도.', 'background-color: yellow;');
currentHref = document.location.href;
lastUserOpenTimestamp = 0; // 타임스탬프 초기화
isClosingRightPanel = false; // 닫기 플래그 초기화
setTimeout(() => {
// console.log('AI Studio 자동 숨김: [Nav] 왼쪽 즉시 닫기 시도.'); // 로그 간소화
closeLeftPanel();
// console.log('AI Studio 자동 숨김: [Nav] 오른쪽 즉시 닫기 시도 (강제).'); // 로그 간소화
try {
const rightPanelElement = document.querySelector(RIGHT_PANEL_SELECTOR);
const content = rightPanelElement?.querySelector(RIGHT_PANEL_CONTENT_SELECTOR);
const closeButton = rightPanelElement?.querySelector(RIGHT_PANEL_CLOSE_BUTTON_SELECTOR);
if (content && closeButton) {
// console.log('%cAI Studio 자동 숨김: [Nav] 오른쪽 강제 닫기 실행.', 'color: magenta;'); // 로그 간소화
closeButton.click();
}
} catch (error) {
console.error('AI Studio 자동 숨김: [Nav] 오른쪽 강제 닫기 오류:', error);
}
attachOpenButtonListener(); // 리스너 재부착
isInitialCheckPhase = true; // 초기화 단계 재시작
initialCheckStartTime = Date.now();
}, NAV_CLOSE_DELAY_MS);
}
const debouncedHandleUrlChange = debounce(handleUrlChange, 150);
// --- URL 변경 리스너 설정 ---
window.addEventListener('popstate', () => {
if (document.location.href !== currentHref) debouncedHandleUrlChange();
});
const originalPushState = history.pushState;
history.pushState = function(...args) {
const previousHref = currentHref;
originalPushState.apply(history, args);
if (document.location.href !== previousHref) debouncedHandleUrlChange();
};
const originalReplaceState = history.replaceState;
history.replaceState = function(...args) {
const previousHref = currentHref;
originalReplaceState.apply(history, args);
if (document.location.href !== previousHref) debouncedHandleUrlChange();
};
// --- 스크립트 실행 시작 ---
function initialize() {
console.log('AI Studio 자동 숨김 (v2.3 - 사용자 설정): 초기화 시작!');
initialCheckStartTime = Date.now();
isInitialCheckPhase = true;
lastUserOpenTimestamp = 0;
isClosingRightPanel = false;
if (checkIntervalId) clearInterval(checkIntervalId); // 기존 인터벌 정리
checkIntervalId = setInterval(persistentCheck, CHECK_INTERVAL_MS);
console.log(`AI Studio 자동 숨김: 핵심 확인 인터벌 (간격: ${CHECK_INTERVAL_MS}ms) 시작됨.`);
setTimeout(persistentCheck, 50); // 첫 확인은 더 빠르게
}
// DOM 로드 상태 확인 후 초기화 시작
if (document.readyState === 'complete') {
setTimeout(initialize, 200);
} else {
document.addEventListener('readystatechange', () => {
if (document.readyState === 'complete' && !checkIntervalId) {
setTimeout(initialize, 200);
}
});
setTimeout(() => { // 안전 장치
if (!checkIntervalId && document.readyState === 'complete') {
console.warn('AI Studio 자동 숨김: 강제 초기화.');
initialize();
}
}, 1500);
}
// 페이지 언로드 시 인터벌 정리
window.addEventListener('beforeunload', () => {
// console.log('AI Studio 자동 숨김: 페이지 언로드. 인터벌 정리.'); // 로그 간소화
if (checkIntervalId) clearInterval(checkIntervalId);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment