Skip to content

Commit b00534a

Browse files
committed
ui(macOS): unify rounded settings controls and compact panel layout
1 parent c75abf8 commit b00534a

3 files changed

Lines changed: 76 additions & 2 deletions

File tree

‎src/App.vue‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,23 @@ function syncScrubInputs(){
443443
for(const [name,entry] of scrubbers)if(!live.has(name)){entry.controller.destroy();scrubbers.delete(name);}
444444
}
445445
watch([pageInput,zoomInput,()=>pages.value.length,navigatorVisible],()=>nextTick(syncScrubInputs),{flush:'post'});
446+
let settingsDropdownObserver;
447+
function syncSettingsDropdownWidths(){
448+
if(platform!=='darwin')return;
449+
const panel=document.querySelector('.settings:not(.paragraph-detail)');if(!panel)return;
450+
const controls=[...panel.querySelectorAll('.macvue-pop-up-button')];let width=0;
451+
for(const control of controls){
452+
const sample=control.cloneNode(true);sample.removeAttribute('id');sample.setAttribute('aria-hidden','true');
453+
Object.assign(sample.style,{position:'fixed',visibility:'hidden',pointerEvents:'none',width:'max-content',minWidth:'0',maxWidth:'none',font:getComputedStyle(control).font});
454+
document.body.append(sample);width=Math.max(width,Math.ceil(sample.getBoundingClientRect().width));sample.remove();
455+
}
456+
if(width)panel.style.setProperty('--settings-dropdown-width',width+'px');
457+
}
458+
watch(settings,async visible=>{
459+
settingsDropdownObserver?.disconnect();await nextTick();if(!visible)return;
460+
syncSettingsDropdownWidths();const panel=document.querySelector('.settings:not(.paragraph-detail)');
461+
if(panel){settingsDropdownObserver=new MutationObserver(syncSettingsDropdownWidths);settingsDropdownObserver.observe(panel,{childList:true,subtree:true,characterData:true});}
462+
});
446463
async function openSettings(target){selectedParagraph.value=null;settings.value=true;await nextTick();if(target==='language'){languageInput.value?.focus();languageMenuOpen.value=true;}else if(target==='kernel'){kernelFocusPending=engineBusy.value;kernelInput.value?.el?.querySelector('.macvue-segment[data-state="on"]')?.focus();}}
447464
function chooseKernel(id){kernelFocusPending=!!kernelInput.value?.el?.contains(document.activeElement);engine.value=id;}
448465
function kernelKeys(e){const current=kernelOptions.findIndex(k=>k.id===engine.value);const index=e.key==='ArrowRight'?Math.min(2,current+1):e.key==='ArrowLeft'?Math.max(0,current-1):e.key==='Home'?0:e.key==='End'?2:null;if(index===null)return;e.preventDefault();if(engineBusy.value)return;kernelFocusPending=true;engine.value=kernelOptions[index].id;nextTick(()=>kernelInput.value?.el?.querySelector('.macvue-segment[data-state="on"]')?.focus());}
@@ -796,7 +813,7 @@ watch(zoom,async()=>{if(restoringView.value)return;revealControllers.forEach(c=>
796813
function visibility(){updateForeground();if(foreground.value){settle();pumpPages();pump();}}
797814
watch([active,zoom,direction,columns,fitMode,sidebar,sidebarMode,showTranslations],scheduleReadingSave);
798815
onMounted(async()=>{if(window.previewActivity){stopActivity=window.previewActivity.onChange(value=>{activityActive.value=value;visibility();});activityActive.value=await window.previewActivity.current();foreground.value=activityActive.value&&!pageHidden();window.previewActivityActive=foreground.value;}if(window.previewRecents){recentDocuments.value=await window.previewRecents.list();scheduleRecentPreviews();}if(window.previewWindow){stopFullscreen=window.previewWindow.onFullscreen(value=>fullscreen.value=value);if(['win32','linux'].includes(platform)){stopMaximized=window.previewWindow.onMaximized(value=>maximized.value=value);maximized.value=await window.previewWindow.maximized();}fullscreen.value=await window.previewWindow.fullscreen();}reader.value?.addEventListener('wheel',pinchWheel,{passive:false});document.addEventListener('keydown',keyboard);document.addEventListener('pointerdown',outsidePopover);document.addEventListener('focusin',outsidePopover);window.addEventListener('blur',dismissPopovers);stopActions=window.previewActions?.onAction(readerAction);if(window.previewPreferences){const saved=await window.previewPreferences.load();applySavedSettings(saved);stopPreferences=window.previewPreferences.onChange?.(applySavedSettings);applyAppearance(saved);engine.value=saved.engine||'pdf_inspector';direction.value=saved.direction||'vertical';columns.value=saved.columns||1;fitMode.value=saved.fit;translationMode.value=saved.translationMode||'reading';if(saved.fit==='manual'){zoom.value=saved.zoom;zoomEntry.value=formatPercentValue(zoom.value);}}await nextTick();syncScrubInputs();loadingPreferences=false;window.addEventListener('resize',resizeFit);resizeObserver=new ResizeObserver(resizeFit);if(reader.value)resizeObserver.observe(reader.value,{box:'content-box'});if(window.previewAppearance){applyAppearance(await window.previewAppearance.current());stopAppearance=window.previewAppearance.onChange(applyAppearance);}const engineReady=checkEngine().then(()=>settle());const c=await api('/api/config');applyConfig(c);model.value=c.model;if(window.previewDocuments){stopDocuments=window.previewDocuments.onAvailable(receiveDocuments);await receiveDocuments();}document.addEventListener('visibilitychange',visibility);settle();scheduleRecentPreviews();if(testMode){await engineReady;window.previewReady=true;}});
799-
onBeforeUnmount(()=>{clearTimeout(recentPreviewTimer);clearInterval(sessionTimer);documentMotionController?.abort();clearTimeout(immersiveLightsTimer);clearTimeout(copyToastTimer);clearTimeout(annotationToastTimer);closeSearch();cancelResize();performanceRecorder.destroy();stopActivity?.();void releaseDocument();void saveReadingView();clearTimeout(readingSaveTimer);delete window.previewSaveReadingView;++recentPreviewGeneration;stopFullscreen?.();stopMaximized?.();reader.value?.removeEventListener('wheel',pinchWheel);cancelAnimationFrame(pinchFrame);clearTimeout(pinchTimer);document.removeEventListener('keydown',keyboard);document.removeEventListener('pointerdown',outsidePopover);document.removeEventListener('focusin',outsidePopover);window.removeEventListener('blur',dismissPopovers);stopActions?.();resizeObserver?.disconnect();window.removeEventListener('resize',resizeFit);cancelAnimationFrame(fitFrame);clearTimeout(fitResizeTimer);clearTimeout(navigatorTimer);scrollbarTimers.forEach(clearTimeout);scrollbarTimers.clear();clearTimeout(previewTimer);clearTimeout(zoomRenderTimer);scrubbers.forEach(entry=>entry.controller.destroy());scrubbers.clear();finishZoomAnimation();cancelAnimationFrame(viewportFrame);resetBitmaps();stopAppearance?.();stopPreferences?.();stopDocuments?.();cancel();clearTimeout(timer);void Promise.allSettled([pdf?.loadingTask,pendingPDFTask,...pages.value.map(p=>p.mathDocument?.loadingTask)].filter(Boolean).map(task=>task.destroy())).then(()=>pdfWorker?.destroy());document.removeEventListener('visibilitychange',visibility);});
816+
onBeforeUnmount(()=>{settingsDropdownObserver?.disconnect();clearTimeout(recentPreviewTimer);clearInterval(sessionTimer);documentMotionController?.abort();clearTimeout(immersiveLightsTimer);clearTimeout(copyToastTimer);clearTimeout(annotationToastTimer);closeSearch();cancelResize();performanceRecorder.destroy();stopActivity?.();void releaseDocument();void saveReadingView();clearTimeout(readingSaveTimer);delete window.previewSaveReadingView;++recentPreviewGeneration;stopFullscreen?.();stopMaximized?.();reader.value?.removeEventListener('wheel',pinchWheel);cancelAnimationFrame(pinchFrame);clearTimeout(pinchTimer);document.removeEventListener('keydown',keyboard);document.removeEventListener('pointerdown',outsidePopover);document.removeEventListener('focusin',outsidePopover);window.removeEventListener('blur',dismissPopovers);stopActions?.();resizeObserver?.disconnect();window.removeEventListener('resize',resizeFit);cancelAnimationFrame(fitFrame);clearTimeout(fitResizeTimer);clearTimeout(navigatorTimer);scrollbarTimers.forEach(clearTimeout);scrollbarTimers.clear();clearTimeout(previewTimer);clearTimeout(zoomRenderTimer);scrubbers.forEach(entry=>entry.controller.destroy());scrubbers.clear();finishZoomAnimation();cancelAnimationFrame(viewportFrame);resetBitmaps();stopAppearance?.();stopPreferences?.();stopDocuments?.();cancel();clearTimeout(timer);void Promise.allSettled([pdf?.loadingTask,pendingPDFTask,...pages.value.map(p=>p.mathDocument?.loadingTask)].filter(Boolean).map(task=>task.destroy())).then(()=>pdfWorker?.destroy());document.removeEventListener('visibilitychange',visibility);});
800817
</script>
801818
802819
<template>

‎src/AppearanceSettings.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function changeUILanguage(code){emit('update:uiLanguage',setUILanguage(code));}
1313
<template>
1414
<section class="settings-section appearance-section" aria-labelledby="settings-appearance"><h3 id="settings-appearance">{{t('appearance.section')}}</h3>
1515
<div class="appearance-row"><span>{{t('appearance.appearance')}}</span><div class="appearance-options" role="group" :aria-label="t('appearance.appearance')"><button v-for="[value,label] in themes" :key="value" class="appearance-choice" :aria-pressed="appearance===value" @click="emit('update:appearance',value)"><span class="appearance-preview" :data-preview="value" aria-hidden="true"><i class="preview-window"><i></i><b></b><b></b></i><i class="preview-panel"><b></b><b></b><b></b></i></span><span>{{t(label)}}</span></button></div></div>
16-
<div class="appearance-row color-row"><span>{{t('appearance.color')}}</span><div class="color-options" role="group" :aria-label="t('appearance.accentColor')"><button v-for="[value,label] in colors" :key="value" class="color-choice" :aria-label="t('appearance.accentColorOption',{name:t(label)})" :aria-pressed="accentColor===value" @click="emit('update:accentColor',value)"><span class="color-swatch" :class="{spectrum:value==='system'}" :style="value!=='system'?{background:value}:{}"></span><span>{{t(label)}}</span></button><label class="color-choice custom-color" :class="{selected:accentColor!=='system'&&!colors.some(c=>c[0]===accentColor)}"><span class="color-swatch spectrum" :style="accentColor!=='system'?{background:accentColor}:{}"><input type="color" :aria-label="t('appearance.customAccentColor')" :value="accentColor==='system'?'#3d647a':accentColor" @input="emit('update:accentColor',$event.target.value)"/></span><span>{{t('appearance.custom')}}</span></label></div></div>
16+
<div class="appearance-row color-row"><span>{{t('appearance.color')}}</span><div class="color-options" role="group" :aria-label="t('appearance.accentColor')"><button v-for="[value,label] in colors" :key="value" class="color-choice" :aria-label="t('appearance.accentColorOption',{name:t(label)})" :aria-pressed="accentColor===value" @click="emit('update:accentColor',value)"><span class="color-swatch" :class="{spectrum:value==='system'}" :style="value!=='system'?{background:value}:{}"></span><span>{{t(label)}}</span></button><label v-if="false" class="color-choice custom-color" :class="{selected:accentColor!=='system'&&!colors.some(c=>c[0]===accentColor)}"><span class="color-swatch spectrum" :style="accentColor!=='system'?{background:accentColor}:{}"><input type="color" :aria-label="t('appearance.customAccentColor')" :value="accentColor==='system'?'#3d647a':accentColor" @input="emit('update:accentColor',$event.target.value)"/></span><span>{{t('appearance.custom')}}</span></label></div></div>
1717
<div class="appearance-row"><span>{{t('appearance.interfaceLanguage')}}</span><MacPopUpButton teleport-to="body" :model-value="uiLanguage" :aria-label="t('appearance.interfaceLanguage')" @update:model-value="changeUILanguage"><MacPopUpButtonItem v-for="[value,label] in uiLanguages" :key="value" :value="value">{{t(label)}}</MacPopUpButtonItem></MacPopUpButton></div>
1818
<div class="appearance-effects"><span>{{t('appearance.effects')}}</span><div><div class="setting-row"><span id="reduce-motion-label">{{t('appearance.reduceMotion')}}</span><MacSwitch :model-value="reduceMotion" aria-labelledby="reduce-motion-label" @update:model-value="emit('update:reduceMotion',$event)"/></div><div class="setting-row"><span id="reduce-transparency-label">{{t('appearance.reduceTransparency')}}</span><MacSwitch :model-value="reduceTransparency" aria-labelledby="reduce-transparency-label" @update:model-value="emit('update:reduceTransparency',$event)"/></div><div class="setting-row"><span id="reduce-padding-label">{{t('appearance.reducePadding')}}</span><MacSwitch :model-value="reducePadding" aria-labelledby="reduce-padding-label" @update:model-value="emit('update:reducePadding',$event)"/></div></div></div>
1919
</section>

‎src/mac-controls.css‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,60 @@
369369
.app[data-platform="darwin"] :is(.recent-heading>.macvue-button,.empty>.primary,.empty>.sample-button){
370370
border-radius:12px;corner-shape:squircle;
371371
}
372+
373+
/* macOS settings: narrower panel and generously rounded controls, excluding switches. */
374+
.app[data-platform="darwin"] .settings:not(.paragraph-detail){
375+
width:min(448px,calc((100vw - 28px) * .8));border-radius:20px;corner-shape:squircle;
376+
}
377+
.app[data-platform="darwin"] .settings:not(.paragraph-detail) .settings-section{
378+
border-radius:20px;corner-shape:squircle;
379+
}
380+
.app[data-platform="darwin"] .settings :is(.macvue-button,.macvue-segmented,.macvue-segment,.macvue-field,.macvue-field-input,.macvue-pop-up-button,.appearance-preview,select,textarea,input:not([type="range"]):not([type="checkbox"]):not([type="color"]),button:not(.macvue-switch):not([role="switch"])){
381+
border-radius:12px;corner-shape:squircle;
382+
}
383+
.app[data-platform="darwin"] .settings .macvue-field{--_macvue-field-radius:12px}
384+
.app[data-platform="darwin"] .settings :is(.setting-row,.translation-service-option,.api-key-row){min-width:0}
385+
.app[data-platform="darwin"] .settings .setting-row>span{overflow-wrap:anywhere}
386+
.app[data-platform="darwin"] .settings .appearance-options{gap:6px;min-width:0}
387+
.app[data-platform="darwin"] .settings .appearance-preview{width:60px}
388+
389+
/* Equal-height option switchers throughout macOS settings. */
390+
.app[data-platform="darwin"] .settings .macvue-segmented{
391+
box-sizing:border-box;height:36px;min-height:36px;max-height:36px;
392+
padding:2px;align-items:stretch;flex-shrink:0;
393+
}
394+
.app[data-platform="darwin"] .settings .macvue-segmented>.macvue-segment{
395+
box-sizing:border-box;height:32px;min-height:32px;max-height:32px;
396+
min-width:0;flex:1 1 0%;padding:0 8px;
397+
display:flex;align-items:center;justify-content:center;
398+
font-size:13px;line-height:16px;white-space:normal;overflow-wrap:anywhere;text-align:center;
399+
}
400+
401+
.app[data-platform="darwin"] .settings:not(.paragraph-detail)>.settings-section{
402+
padding:18px 16px;
403+
}
404+
.app[data-platform="darwin"] .settings-section[aria-labelledby="settings-engine"]>.advanced-settings{
405+
border-top:0;
406+
}
407+
.app[data-platform="darwin"] .settings .macvue-pop-up-button{
408+
box-sizing:border-box;width:var(--settings-dropdown-width,max-content);
409+
min-width:0;max-width:100%;flex-shrink:0;
410+
}
411+
412+
/* Softer dividers throughout settings, including expanded advanced options. */
413+
.settings:not(.paragraph-detail){--settings-divider:rgba(0,0,0,.045);--chrome-divider:var(--settings-divider)}
414+
[data-appearance="dark"] .settings:not(.paragraph-detail){--settings-divider:rgba(255,255,255,.06)}
415+
.settings:not(.paragraph-detail) :is(.setting-row,.appearance-row,.appearance-effects,.parallel-settings,.parallel-setting,.advanced-settings,.advanced-option,.advanced-kernel-update,.translation-service-account,.translation-service-option,.kernel-setting){
416+
border-top-color:var(--settings-divider);border-bottom-color:var(--settings-divider);
417+
}
418+
419+
/* The library paints selection in a separate pill, so round that layer too. */
420+
.app[data-platform="darwin"] .settings .macvue-segmented{
421+
--_macvue-segmented-radius:14px;border-radius:16px;
422+
}
423+
.app[data-platform="darwin"] .settings .macvue-segmented-pill{
424+
inset-block:2px;border-radius:14px;corner-shape:squircle;
425+
}
426+
.app[data-platform="darwin"] .settings .macvue-segmented>.macvue-segment{
427+
border-radius:14px;corner-shape:squircle;
428+
}

0 commit comments

Comments
 (0)