// ==UserScript==
// @name Muse (muse.ai) - Top Avatar Enlarger
// @namespace https://muse.ai/
// @version 4.0.0
// @description muse.ai 上部アバターのフレーム本体(role="button", 56px)を直接拡大します。サイドバー(role="img", 128px)には適用しません。非公式のユーザースクリプトです。サイズは AVATAR_SIZE で変更できます。
// @license MIT
// @match https://muse.ai/*
// @match https://*.muse.ai/*
// @run-at document-idle
// @grant none
// @noframes
// ==/UserScript==
(function () {
'use strict';
/* ================= 設定 ================= */
const AVATAR_SIZE = 300; // 拡大後の直径(px)。変えたい場合はこの数字だけ変更してください
const DEBUG = false;
/* ======================================== */
// 上部フレームの確定セレクタ(実物HTMLより):
//
// サイドバー側は role="img" / 128px なので、role="button" で明確に区別できる
const FRAME_SELECTOR = '[data-hatch-avatar-interaction="true"][role="button"]';
const STAGE_SELECTOR = '[data-hatch-avatar-stage="chat-nav"]';
const NAME_SPACE = 32; // ステージ設計: min-h 88px - 56px = 名前分 32px
function cleanupOld() {
document.querySelectorAll('[data-muse-avatar-enlarged]').forEach((el) => {
['width', 'height', 'min-width', 'min-height', 'max-width', 'max-height', 'flex']
.forEach((p) => el.style.removeProperty(p));
el.removeAttribute('data-muse-avatar-enlarged');
});
document.querySelectorAll('[data-muse-avatar-scaled]').forEach((el) => {
el.style.removeProperty('transform');
el.style.removeProperty('transform-origin');
el.style.removeProperty('z-index');
el.removeAttribute('data-muse-avatar-scaled');
});
}
function setSize(el, px) {
['width', 'height', 'min-width', 'min-height', 'max-width', 'max-height']
.forEach((p) => el.style.setProperty(p, px + 'px', 'important'));
}
// フレームと同じ親の中にある付属要素(名前の吹き出し等)は、
// top の位置が元のフレームサイズ基準で置かれているので、増えた分だけ下げる
function shiftAccessories(frame, delta) {
const root = frame.parentElement;
if (!root || delta === 0) return;
root.querySelectorAll('[data-hatch-avatar-accessory]').forEach((el) => {
if (frame.contains(el)) return;
if (!el.dataset.museOriginalTop) {
const top = parseFloat(el.style.top);
if (!Number.isFinite(top)) return;
el.dataset.museOriginalTop = String(top);
}
const newTop = parseFloat(el.dataset.museOriginalTop) + delta;
el.style.setProperty('top', newTop + 'px', 'important');
});
}
function enlargeStage() {
const stage = document.querySelector(STAGE_SELECTOR);
if (!stage) return;
const placeholder = stage.querySelector('.size-14') ||
[...stage.querySelectorAll('div')].find((el) =>
el.children.length === 0 && el.offsetWidth >= 40 && el.offsetWidth <= 72);
if (placeholder) setSize(placeholder, AVATAR_SIZE);
stage.style.setProperty('width', Math.max(112, AVATAR_SIZE + 16) + 'px', 'important');
stage.style.setProperty('min-height', (AVATAR_SIZE + NAME_SPACE) + 'px', 'important');
}
function enlargeFrame(frame) {
// 元サイズは初回だけ記録する(適用後は offsetWidth が 96 になるため)
if (!frame.dataset.museOriginalSize) {
frame.dataset.museOriginalSize = String(frame.offsetWidth || 56);
}
const original = parseFloat(frame.dataset.museOriginalSize) || 56;
const delta = AVATAR_SIZE - original;
setSize(frame, AVATAR_SIZE);
shiftAccessories(frame, delta);
if (DEBUG && !frame.hasAttribute('data-muse-v4-applied')) {
console.log('[Muse Avatar Enlarger v4] 上部フレームを拡大しました 元:', original + 'px', '→', AVATAR_SIZE + 'px', frame);
}
frame.setAttribute('data-muse-v4-applied', '1');
}
function run() {
cleanupOld();
enlargeStage();
document.querySelectorAll(FRAME_SELECTOR).forEach(enlargeFrame);
}
run();
// React が style を書き戻すことがあるため、監視+1秒ごとの再適用で維持する
let queued = false;
new MutationObserver(() => {
if (queued) return;
queued = true;
requestAnimationFrame(() => { queued = false; run(); });
}).observe(document.documentElement, { childList: true, subtree: true });
setInterval(run, 1000);
})();