diff --git a/src/main.tsx b/src/main.tsx
index fdb3239..91597e6 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -29,7 +29,7 @@ function main() {
const container = getMenuContainer()
injectionMap.set(target, container)
- target.before(container)
+ getNavMenuInsertionTarget(target).before(container)
}
const selector = '[data-testid="accounts-profile-button"]'
@@ -99,3 +99,10 @@ function getMenuContainer() {
render(
, container)
return container
}
+
+function getNavMenuInsertionTarget(target: Element) {
+ const wrapper = target.parentElement
+ if (!wrapper || wrapper.children.length !== 1) return target
+
+ return wrapper
+}
diff --git a/src/style.css b/src/style.css
index cafad35..4a4f45f 100644
--- a/src/style.css
+++ b/src/style.css
@@ -54,6 +54,37 @@ html {
filter: brightness(0.5);
}
+.ce-nav-trigger {
+ min-width: 0;
+ border: 0;
+ color: var(--ce-text-primary);
+}
+
+.ce-nav-trigger .ce-menu-item-text {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.ce-nav-trigger-collapsed {
+ width: 32px;
+ height: 32px;
+ margin: 0 auto 0.5rem;
+ padding: 0;
+ justify-content: center;
+ gap: 0;
+ border-radius: 8px;
+ color: var(--text-secondary, var(--ce-text-primary));
+}
+
+.ce-nav-trigger-collapsed:hover {
+ background-color: var(--sidebar-surface-secondary, rgba(255, 255, 255, 0.1));
+}
+
+.ce-nav-trigger-collapsed .ce-menu-item-text {
+ display: none;
+}
+
.ce-card {
border-radius: 1rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);
diff --git a/src/ui/Menu.tsx b/src/ui/Menu.tsx
index 117e86a..9c259d7 100644
--- a/src/ui/Menu.tsx
+++ b/src/ui/Menu.tsx
@@ -18,6 +18,61 @@ import { SettingDialog } from './SettingDialog'
import '../style.css'
import './Dialog.css'
+function useCollapsedSidebar(container: HTMLDivElement, isMobile: boolean) {
+ const [isCollapsed, setIsCollapsed] = useState(false)
+
+ useEffect(() => {
+ if (isMobile) {
+ setIsCollapsed(false)
+ return
+ }
+
+ let frame = 0
+ const observed = new Set()
+ const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(update)
+
+ function sidebarElement() {
+ return container.closest('nav, aside, [aria-label="Sidebar"], [data-testid="sidebar"]')
+ ?? container.parentElement
+ }
+
+ function observe(element: Element | null | undefined) {
+ if (!observer || !element || observed.has(element)) return
+ observer.observe(element)
+ observed.add(element)
+ }
+
+ function update() {
+ cancelAnimationFrame(frame)
+ frame = requestAnimationFrame(() => {
+ const parentWidth = container.parentElement?.getBoundingClientRect().width ?? 0
+ const sidebarWidth = sidebarElement()?.getBoundingClientRect().width ?? parentWidth
+ const nextCollapsed = (parentWidth > 0 && parentWidth < 96)
+ || (sidebarWidth > 0 && sidebarWidth < 96)
+
+ setIsCollapsed(nextCollapsed)
+ container.toggleAttribute('data-ce-sidebar-collapsed', nextCollapsed)
+ observe(container.parentElement)
+ observe(sidebarElement())
+ })
+ }
+
+ observe(container.parentElement)
+ observe(sidebarElement())
+ update()
+
+ window.addEventListener('resize', update)
+ return () => {
+ cancelAnimationFrame(frame)
+ window.removeEventListener('resize', update)
+ observer?.disconnect()
+ container.removeAttribute('data-ce-sidebar-collapsed')
+ }
+ }, [container, isMobile])
+
+ return isCollapsed
+}
+
function MenuInner({ container }: { container: HTMLDivElement }) {
const { t } = useTranslation()
@@ -59,6 +114,7 @@ function MenuInner({ container }: { container: HTMLDivElement }) {
const width = useWindowResize(() => window.innerWidth)
const isMobile = width < 768
+ const isCollapsedSidebar = useCollapsedSidebar(container, isMobile)
const Portal = isMobile ? 'div' : HoverCard.Portal
return (
@@ -78,8 +134,11 @@ function MenuInner({ container }: { container: HTMLDivElement }) {
>