/* ArozOS File Manager css file This file is used to overwrite the default css on segmentic ui (or formantic if it is later upgraded) */ /* Color definations */ :root{ --dark_theme_toggle: #333333; --dark_theme_toggle_text: rgb(218, 218, 218); --white_theme_toggle: #dadada; --white_theme_toggle_text: rgb(27, 27, 27); } body.whiteTheme { --fileopr_btnTxt: rgb(226, 226, 226); --fileopr_oprtxt: rgb(56, 52, 52); --fileopr_btnhover: #f0f0f0; --divider: #f0f0f0; --navi_bgcolor: var(--body_background_secondary, #fcfcfc); --navi_theme: var(--theme_color, #92cfe7); --navi_theme_multiselect: #6c06ba; --navi_txt: rgb(114, 114, 114); --dropdown_bg: white; --dropdown_txt: black; --vroot_active_bg: #dadada; --dir_item_color: rgb(78, 78, 78); --text_active: #23cc28; --dir_bg: #f5f5f5; --dir_txt: rgb(44, 44, 44); --dir_border: rgba(219, 226, 241, 0.7); --dir_hover_text: #a3a3a3; --dir_hover_background: #f3f3f3; --dir_hover_active: #c9e7f1; --normal_object_txt: #303030; --hex_folder_txt: #2b9447; --um_file_txt: #69aaff; --folder_view_background: #ffffff; --folder_view_share: #24cc29; --fileobject_background_hover: #f2f2f2; --fileobject_background_selected: #d2f2f7; --fileobject_text_selected: black; --address_text_border:#cfcfcf; --address_text_background: white; --popup_bg: white; --popup_border: rgb(226, 226, 226); --popup_header_bg: #ffffff; --popup_header_txt: #1f2328; --popup_btn_border: #dedede; --popup_btn_hover: rgba(238, 238, 238, 0.932); --popup_btn_primary_bg: #51aded; --popup_btn_primary_txt: white; --popup_btn_primary_bg_hover: #76c0f5; --popup_btn_negative_bg: #ed5151; --popup_btn_negative_txt: white; --popup_btn_negative_bg_hover: #f57676; --modulelist_bg: white; --modulelist_hover: rgba(35, 35, 35, 0.06); --contextmenu_background: #ffffff; --contextmenu_hover: #f9f9f9; --contextmenu_textcolor: rgb(78, 78, 78); /* Transfer panel bars. The pale legacy greens/reds washed out against the new card surface, so these are the saturated status colours. */ --upload_progressbar: var(--fs-accent); --upload_progresstext: rgb(255, 255, 255); --upload_progress_succ: #1e8e3e; --upload_progress_failed: #d93025; --fs-danger-soft: #fce8e6; --open_with_selected: #51aded; --open_with_selected_text: white; --open_with_item_hover_background:rgba(35,35,35,0.14); --popup_wrapper_background: rgba(15, 20, 30, 0.18); --code_bg: #f7f7f9; --code_txt: #bd4147; --uploadtab_bg: #f5f5f5; --uploadtab_txt: rgb(78, 78, 78); --upload_bg: #ffffff; } body.darkTheme{ /* Legacy variable set, re-sampled to match the palette in shared/fs-theme.css so the parts of the UI that still read these (popups, upload tab, context menu, file object rows) sit in the same colour family as everything already ported to the --fs-* tokens. */ /* ao.css's own dark values are a neutral grey; pull them onto the sampled palette so semantic-ui widgets inside this app match the rest of it. */ --body_background: var(--fs-bg); --body_text: var(--fs-text); --text_color: var(--fs-text); --text_color_secondary: var(--fs-text-dim); --fileopr_btnTxt: #e6e9ee; --fileopr_oprtxt: #c7ced7; --fileopr_btnhover: #2a3039; --divider: #272c34; /* Surfaces come from the sampled tokens, not ao.css's --body_background_*. Those resolve to a neutral grey that is lighter than the file area, which inverts the mockup's depth order (content darkest, chrome above it). */ --navi_bgcolor: var(--fs-bg); --navi_theme: var(--theme_color, #272c34); --navi_theme_multiselect: #4a9eff; --navi_txt: #c7ced7; --dropdown_bg: #1c2129; --dropdown_txt: #e6e9ee; --vroot_active_bg: #262b33; --dir_item_color: #c7ced7; --text_active: #4a9eff; --dir_bg: #14181d; --dir_txt: #e6e9ee; --dir_border: #272c34; --dir_hover_text: #9aa3ae; --dir_hover_background: #1f242c; --dir_hover_active: #1e2d40; --normal_object_txt: #e6e9ee; --hex_folder_txt: #5fd48b; --um_file_txt: #7db8ff; --folder_view_background: var(--fs-content-bg); --folder_view_share: #3ecf6d; --fileobject_background_hover: #1f242c; --fileobject_background_selected: #17293f; --fileobject_text_selected: #e6e9ee; --address_text_border: #272c34; --address_text_background: #1c2129; --popup_bg: #1c2129; --popup_border: #272c34; --popup_header_bg: #1c2129; --popup_header_txt: #e6e9ee; --popup_btn_border: #323945; --popup_btn_hover: #2a3039; --popup_btn_primary_bg: #4a9eff; --popup_btn_primary_txt: #0d1014; --popup_btn_primary_bg_hover: #6bb0ff; --popup_btn_negative_bg: #e5484d; --popup_btn_negative_txt: #ffffff; --popup_btn_negative_bg_hover: #f06a6e; --modulelist_bg: transparent; --modulelist_hover: rgba(122, 138, 160, 0.18); --contextmenu_background: #1c2129; --contextmenu_hover: #2a3039; --contextmenu_textcolor: #e6e9ee; --upload_progressbar: var(--fs-accent); --upload_progresstext: #0d1014; --upload_progress_succ: #3ecf6d; --upload_progress_failed: #e5484d; --fs-danger-soft: #3a2224; --open_with_selected: #1e3450; --open_with_selected_text: #e6e9ee; --open_with_item_hover_background: rgba(122, 138, 160, 0.16); --popup_wrapper_background: rgba(0, 0, 0, 0.38); --code_bg: #12161b; --code_txt: #ff8f93; --uploadtab_bg: #16191f; --uploadtab_txt: #e6e9ee; --upload_bg: #1c2129; } body{ color:var(--body_text); background-color:var(--body_background); height:100%; overflow: hidden; } h1, h2, h3, p, span, div, input, .dropdown { font-family: "Microsoft JhengHei","SimHei", "Apple LiGothic Medium", "STHeiti"} html{ height:100%; overflow:hidden; } /* force override of semantic button rule */ body.darkTheme .ui.icon.button{ color: var(--text_color) !important; } /* Navigation Bar The top bar of the interface see subparts for more detail configs */ .navibar{ width:100%; padding-left:12px; padding-right:12px; padding-top:6px; padding-bottom:3px; z-index: 99; background-color:var(--navi_bgcolor); border-bottom:2px solid var(--navi_theme); } .navibar.ctrl{ border-bottom:2px solid var(--navi_theme_multiselect); } .navibar.mobile{ padding-top: 0; } .button.standard{ box-shadow: 0 1px 1px 0px rgb(190, 190, 190); border: 0px; } .button.standard:hover{ background-color:#82c2f0 !important; color:white !important; } /* File operation bar */ .navibar .fileOprBtn{ vertical-align: top; border: 1px solid transparent; cursor: pointer; background-color: transparent; text-align: center; padding-bottom: 0.4em; padding-top: 0.5em; border-radius: 0.4em; } .navibar .fileOprBtn:hover{ border: 1px solid var(--fileopr_btnhover); opacity: 0.8; } .navibar .fileOprBtn .opricon{ width: 46px; } .navibar .fileOprBtn .oprtxt{ color:var(--fileopr_oprtxt); } .navibar .twolines.fileOprBtn{ padding-top: 0.1em; padding-bottom: 0.1em; } .navibar .twolines.fileOprBtn .opricon{ width: 40px; } .navibar .twolines.fileOprBtn .oprtxt{ font-size: 1em; } .navibar.mobile .fileOprBtn{ padding-bottom: 0.4em; padding-top: 0.2em; border-radius: 0.4em; } .navibar.mobile .fileOprBtn .opricon{ width: 36px; } .navibar.mobile .twolines.fileOprBtn .opricon{ width: 28px; } .navibar.mobile .twolines.fileOprBtn .oprtxt{ font-size: 0.7em; } .navibar .fileoprSmallBtn{ cursor: pointer; border: 1px solid transparent; color:var(--navi_txt); background-color: transparent; padding: 0.3em; width: fit-content; text-align: left; } .navibar .fileoprSmallBtn:hover{ opacity: 0.8; border: 1px solid var(--divider); border-radius: 0.4em; } .navibar.mobile .fileoprSmallBtn{ font-size: 0.9em; padding: 0.2em; } .navibar .fileoprGroupDivider{ padding-right: 0.2em; margin-right: 0.2em; border-right: 1px solid var(--divider); height: 80px; } .navibar.mobile .fileoprGroupDivider{ height: 64px; } .ui.selection.dropdown{ background-color: var(--dropdown_bg) !important; color: var(--dropdown_txt) !important; min-width: 11.5em !important; } /* special case when using in mobile sidebar */ #mobileSortDropdown .ui.selection.dropdown{ width: 100%; border-radius: 0; padding: 1em; } .ui.selection.dropdown .menu > .item{ border-top-color:var(--divider); background-color: var(--dropdown_bg) !important; color: var(--dropdown_txt) !important; } .ui.selection.active.dropdown .menu{ border: 1px solid var(--divider) !important; } .ui.selection.visible.dropdown > .text:not(.default){ color: var(--text_color_secondary) !important; } /* Mobile interface */ .fileoprBtnMobile{ position: relative; width: 100%; background-color: none; padding: 0.5em; padding-left: 1em; cursor: pointer; } #mobileNaviBar{ height: 100vh !important; overflow-y: auto !important; } #mobileNaviBar .fileoprBtnMobile{ border-bottom: 1px solid var(--divider); } .fileoprBtnMobile:hover{ background-color: var(--popup_btn_hover) !important; } .fileoprBtnMobile .opricon{ width: 30px; display: inline-block; vertical-align: middle; margin-right: 0.4em; } .fileoprBtnMobile p{ display: inline-block; vertical-align: middle; } .fileoprSmallBtnMobile{ cursor: pointer; border: 1px solid transparent; color:var(--navi_txt); background-color: transparent; padding: 0.5em; padding-left: 1em; width: 100%; text-align: left; } #mobileNaviBar .fileoprSmallBtnMobile{ border-bottom: 1px solid var(--divider); } .fileoprSmallBtnMobile:hover{ background-color: var(--popup_btn_hover) !important; } /* Address and search bar */ .navibarBtn{ cursor: pointer; border: 1px solid transparent; color:var(--navi_txt); background-color: transparent; padding: 0.3em; display: inline-block; border-radius: 0.4em; padding-right: 0.1em; padding-bottom: 0.4em; margin-left: 0.2em; } .navibarBtn:hover{ opacity: 0.8; border: 1px solid var(--divider); } .navibarBtn.disabled{ pointer-events: none; opacity: 0.5; user-select: none; } /* Special button for switching over to dark theme*/ .navibarBtn.DarkThemeToggle.inverted{ color:var(--dark_theme_toggle_text); background-color: var(--dark_theme_toggle); } .navibarBtn.DarkThemeToggle{ color: var(--white_theme_toggle_text); background-color: var(--white_theme_toggle); } .navibarBtn.DarkThemeToggle:hover{ border: 1px solid transparent; } .navibarBtn:hover{ opacity: 0.8; } /* View mode buttons */ .videmode.button{ color:var(--navi_txt) !important; background-color: transparent; } .videmode.button:hover{ background-color: transparent; opacity: 0.8; } .videmode.button:focus:not(#togglePropertiesViewBtn){ background-color: transparent !important; } .addressBar{ border-top: 1px solid var(--divider); width:100%; padding: 0.4em; padding-left: 0; padding-right: 0; padding-top: 0.6em; min-height: 35px; margin-top:0.4em; vertical-align: top; display: flex; position: relative; } .addressBar .divider{ color: var(--text_color_secondary) !important; } #pathDisplayField{ margin-left: 0.4em !important; } .addressText{ padding:5px; border-radius: 0.5em; /* border: 1px solid var(--address_text_border); */ flex-grow: 1; background-color: var(--address_text_background); } #pathInputField{ margin-left: 0.4em !important; font-size: 1rem; height: 27px; width: 100%; } #pathInputField input{ background-color: var(--address_text_background); color: var(--text_color); } .selectable{ cursor:pointer; } .viewportBtns.mobile{ order: 2; margin-left: auto !important; height: 30px; border-right: none; margin-top: -10px; } /* Mobile file operation menus */ .mobileFileOprMenu{ position: fixed; top: 0.6em; right: 0.6em; background-color: var(--navi_bgcolor); border: none; color: var(--text_color); cursor: pointer; width: 50px; height: 50px; padding-top: 4px; padding-left: 10px; border-radius: 0.4em; } .mobileFileOprMenu:hover{ opacity: 0.8; } .mobileFileOprMenuToggle{ padding: 1em; border: 0px solid transparent; color: var(--text_color); border-bottom:2px solid var(--navi_theme); text-align: right; cursor: pointer; } .mobileFileOprMenuToggle .logo{ position: absolute; top: 0.8em; left: 0.5em; width: 100px; opacity: 0.6; } /* Search bar */ .searchbar{ width:100%; padding: 0.4em; padding-left: 0; padding-right: 0; min-height: 35px; margin-top:0.4em; vertical-align: top; display: flex; align-items: center; } .searchbar .searInputBar{ flex-grow: 1; } .searchbar .navibarBtn.active{ color: var(--text_active); font-weight: bold; } /* Main Window scalling */ #mainWindow{ display: flex; align-items:flex-start; } /* Directory Sidebar */ #directorySidebar{ padding-top:1em; padding-bottom: 1em; overflow-y:auto; padding-bottom:20px; border-right:2px solid var(--dir_border); background-color:var(--dir_bg); color:var(--dir_txt); /* Starting size only. splitter.js rewrites width and min-width together as the handle is dragged - min-width is what stops flexbox from shrinking the pane, so it has to follow width down rather than pin it. */ width: 250px; min-width: 250px; flex: 0 0 auto; scrollbar-width: thin; } .rightmargin.icon{ margin-right:8px !important; } .dir{ cursor:pointer; } .dir:not(.active):hover{ color: var(--dir_hover_text) !important; } .dir.active:hover{ background-color:var(--dir_hover_active); } .dir.item{ padding-top: 0.4em !important; padding-bottom: 0.4em !important; padding-left: 1.4em !important; padding-right: 1.4em !important; color:var(--dir_item_color); } .vroot.active{ background-color: var(--vroot_active_bg); } #selectInfo{ padding-left: 1.2em; padding-right: 1.2em; color: var(--text_color_secondary); } .pathDisplay{ cursor: text; overflow:hidden; white-space: nowrap; text-align: left; } .measure { position: absolute; visibility: hidden; left: -1000px; top: -1000px; } /* Folder Viewer (Main viewer) */ /* Pane splitters A 7px grab strip that straddles the border between two panes. The negative side margins pull its own width back out of the flex line, so adding the handle does not shift the layout by a pixel - it sits on top of the border that was already there. Hidden on the touch layout: splitter.js does not bind them there, and a 7px target is not a sensible touch affordance anyway. */ .fmSplitter{ flex: 0 0 auto; align-self: stretch; width: 7px; margin-left: -3px; margin-right: -4px; position: relative; z-index: 6; cursor: col-resize; background-color: transparent; transition: background-color 0.12s ease; } .fmSplitter:hover, .fmSplitter.dragging{ background-color: var(--fs-accent); opacity: 0.55; } /* While a handle is being dragged the pointer regularly ends up over the panes either side of it. Without this the cursor flickers back to the default and stray text gets selected on the way past. */ body.fmResizing{ cursor: col-resize; user-select: none; -webkit-user-select: none; } body.fmResizing *{ cursor: col-resize !important; user-select: none; -webkit-user-select: none; } #folderView{ flex-grow:1; padding-left:2em; padding-right:2em; padding-top:1.4em; overflow-y:auto; background-color: var(--folder_view_background); scrollbar-width: thin; } #folderList{ margin-bottom:1em; } .fileviewList{ position: relative; } /* Common spacing for list view and grid view */ .fileviewList .fileObject.item,.fileviewList .fileObject.card{ display:block !important; cursor:pointer; overflow-wrap: break-word !important; user-select: none; border: 1px solid var(--divider); } /* Hidden entries, shown only when the user turns them on. Dimmed so they stay distinguishable from ordinary content at a glance; they lift to full strength on hover and while selected so they are still readable to work with. */ .fileObject[data-hidden="true"]{ opacity: 0.45; } .fileObject[data-hidden="true"]:hover, .fileObject[data-hidden="true"].selected{ opacity: 1; } .fileviewList .fileObject.item:hover,.fileviewList .fileObject.card:hover{ background-color: var(--fileobject_background_hover) !important; } .fileviewList .fileObject.item.selected, .fileviewList .fileObject.card.selected{ background-color: var(--fileobject_background_selected) !important; color: var(--fileobject_text_selected) !important; } /* Filelist in list mode */ .fileviewList .fileObject.item{ padding: 0.8em; padding-left: 1.2em; padding-right: 1em; } .fileviewList .fileObject.item:first-child{ border-top-left-radius: 0.4em; border-top-right-radius: 0.4em; } .fileviewList .fileObject.item:last-child{ border-bottom-left-radius: 0.4em; border-bottom-right-radius: 0.4em; } .fileviewList .fileObject.item.selected.renaming{ padding-top: 0.6em !important; padding-bottom: 0.6em !important; } .fileviewList .fileObject.item:not(:first-child) { border-top: 0; } /* Filelist in grid mode. Width is set by the status bar zoom slider (--fm-tile / inline style), so the old fixed 148px !important is gone - it silently defeated the slider. */ .fileviewList .fileObject.card{ border-radius: 0.4em; position: relative; margin-bottom: 1em; } /* Grid tiles are square at every width. aspect-ratio keeps the box square so the responsive breakpoint no longer has to fight fixed pixel sizes. Icons use contain so they are never distorted; a real thumbnail (marked by .hasThumbnail from thumbnail.js) uses cover so it fills the square instead of letterboxing. display:block matters as much as the sizing here: an inline sits on the text baseline, which leaves a few pixels of the container's background showing as a grey strip under every thumbnail. */ .fileviewList .fileObject.card img{ display: block; width: 100%; aspect-ratio: 1 / 1; height: auto; object-fit: contain; } .fileviewList .fileObject.card.hasThumbnail img{ object-fit: cover; object-position: center; } /* The tinted plate behind an icon is there to give a bare glyph a surface. A real thumbnail covers the whole square, so keeping the plate only risks it peeking out along an edge. */ .fileviewList .fileObject.card.hasThumbnail .image{ background: transparent; } .fileviewList .fileObject.card .content{ padding-left: 1em; padding-right: 1em; padding-top: 1em; padding-bottom: 0.4em; } /* Share icon overlay for card view */ .fileviewList .fileObject.card .image{ position: relative; } .fileviewList .fileObject.card .shareOverlay.visible{ position: absolute; bottom: 0.4em; left: 0.4em; padding-top: 4px; background-color: var(--navi_bgcolor); height: 28px; width: 28px; border-radius: 0.4em; border: 1px solid var(--divider); box-shadow: 3px 1px 5px 1px rgba(158,158,158,0.2); opacity: 0.9; } .fileviewList .fileObject.card .content .header{ font-size: 1.2em; font-weight: 500; color: var(--text_color); } .fileviewList .fileObject.card .content .meta{ font-size: 0.9em; color: var(--text_color_secondary); } .fileviewList .fileObject.card .content .description{ font-size: 1em; color: var(--text_color); position: absolute; bottom: 1em; left: 1em; } /* fileview in detail mode */ .fileviewList .fileObject.details{ cursor:pointer; user-select: none; border: 1px solid var(--divider); color: var(--text_color); } .fileviewList .fileObject.details:hover{ background-color:var(--fileobject_background_hover) !important; } .fileviewList .fileObject.details.selected{ background-color:var(--fileobject_background_selected) !important; } #folderView th{ color: var(--text_color); } .fileviewList .fileObject.details.selected.renaming td:first-child{ padding: inherit; } .fileviewList .detailTableContent tr td:first-child{ padding-left: 1em !important; } .fileviewList .fileObject.details .light-text{ color: var(--text_color_secondary); } /* Share button */ .sharebtn{ cursor: pointer; border: 1px solid transparent; background-color: transparent; color: var(--folder_view_share); margin-left: 4px; margin-top: 1px; } /* Handle specific Chrome bug in rendering the share icon */ @media screen and (-webkit-min-device-pixel-ratio:0) and (min-resolution:.001dpcm) { .sharebtn{ padding: 0 !important; } } .sharebtn:hover{ opacity: 0.5; } /* Properties View */ #propertiesView{ /* Starting size only - see the note on #directorySidebar */ width: 300px; min-width: 300px; flex: 0 0 auto; padding: 1em; overflow-y: auto; scrollbar-width: thin; color: var(--text_color); border-left: 1px solid var(--divider); background-color: var(--folder_view_background); } /* Permission grid in the properties panel. Compact enough to sit inside the properties table cell without stretching the panel. */ #propertiesView .fmPermTable{ border-collapse: collapse; width: auto; font-size: 12px; line-height: 1.3; } #propertiesView .fmPermTable th{ padding: 1px 7px 3px 7px; font-weight: 400; color: var(--fs-text-dim) !important; text-align: center; white-space: nowrap; } #propertiesView .fmPermTable td{ padding: 1px 7px; text-align: center; } #propertiesView .fmPermTable td.fmPermWho{ padding-left: 0px; padding-right: 10px; text-align: left; color: var(--fs-text-dim) !important; white-space: nowrap; } /* Granted reads as present, denied as a quiet dash rather than a red cross */ #propertiesView .fmPermTable td.fmPermOn{ color: var(--upload_progress_succ) !important; } #propertiesView .fmPermTable td.fmPermOff{ color: var(--fs-text-faint) !important; } #propertiesView td, #propertiesView span, #propertiesView div{ color: var(--text_color); } /* Context Menu This is a complete rewrite / re-implement of the ts contextmenu after migration */ /* Styled to match the .fsMenu component in shared/fs-components.css so the right click menu, the "..." menu and the sort menu all read as the same control: rounded card, inset rows that round on hover, and icons a step dimmer than the label text. */ .contextmenu{ position: fixed; display: none; width: 200px; padding: 4px; background-color: var(--contextmenu_background); border: 1px solid var(--divider); border-radius: 10px; -webkit-box-shadow: 0px 8px 28px var(--fs-shadow); -moz-box-shadow: 0px 8px 28px var(--fs-shadow); box-shadow: 0px 8px 28px var(--fs-shadow); z-index: 999; max-height: calc(100vh - 16px); overflow-y: auto; } .contextmenu.visible{ display: block; } .contextmenu .item{ display: flex; align-items: center; gap: 7px; padding: 5px 7px; border-radius: 6px; cursor: pointer; font-size: 13.5px; white-space: nowrap; color: var(--contextmenu_textcolor); } .contextmenu .item:hover{ background-color: var(--contextmenu_hover); } /* Label expands to fill the row so the trailing element is always right-aligned */ .contextmenu .item > span:not(.description){ flex-grow: 1; } /* The rows carry semantic-ui font icons. Pin to a fixed box and push to the trailing edge with margin-left: auto. Items that carry a .description (Copy / Paste / Cut) hide their icon — the hotkey text is the indicator. The colour needs !important: ao.css styles every and under a themed body with a blanket rule whose :not() chain scores higher than any reasonable selector here, so it would otherwise repaint the icons to the same colour as the label and flatten the two-tone look. */ .contextmenu .item > i.icon{ width: 16px; min-width: 16px; height: auto; margin-left: auto; font-size: 14px; text-align: center; color: var(--fs-icon) !important; opacity: 0.6; } /* Items that already show a hotkey — no icon needed */ .contextmenu .item.hasHotkey > i.icon { display: none; } /* Keyboard hint, pushed to the trailing edge */ .contextmenu .description{ margin-left: auto; color: var(--fs-text-faint) !important; font-size: 12px; } .contextmenu .divider{ width: auto; height: 1px; margin: 3px; background-color: var(--fs-line-soft); border-bottom: 0px; } /* Message box and popups */ /* Toast A dark pill centred along the bottom edge. It auto dismisses, and the trailing button closes it early. Colours are hardcoded to the toast tokens rather than the theme surfaces: the pill floats above the content instead of belonging to it, so it stays dark in the light theme too. That also means #msgbox has to win over the blanket ao.css rule that paints every span and under a themed body, which the id in the selector does without needing !important. */ #msgbox.msgbox{ position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%) translateY(10px); z-index: 999; display: flex; align-items: center; gap: 11px; max-width: min(560px, calc(100% - 32px)); padding: 11px 10px 11px 18px; border-radius: 999px; background-color: var(--fs-toast-bg); color: var(--fs-toast-fg); box-shadow: 0px 6px 22px rgba(0, 0, 0, 0.35); opacity: 0; transition: opacity 0.18s ease, transform 0.18s ease; /* Belt and braces with the display:none that hideMsgBox sets after the fade: while faded out the pill must never swallow clicks aimed at the content beneath it, and that must not depend on a timer firing. */ pointer-events: none; } #msgbox.msgbox.visible{ opacity: 1; transform: translateX(-50%) translateY(0px); pointer-events: auto; } #msgbox.msgbox.error{ background-color: var(--fs-toast-error-bg); color: var(--fs-toast-error-fg); } #msgbox .msgboxText{ flex: 1 1 auto; min-width: 0px; color: inherit; font-size: 13.5px; line-height: 1.45; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #msgbox > i.icon{ flex: 0 0 auto; width: 16px; height: auto; margin: 0px; font-size: 15px; text-align: center; color: inherit; opacity: 0.9; } #msgbox .closeMsgButton{ flex: 0 0 auto; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; border-radius: 50%; cursor: pointer; color: var(--fs-toast-dim); } #msgbox.msgbox.error .closeMsgButton{ color: var(--fs-toast-error-fg); opacity: 0.85; } #msgbox .closeMsgButton:hover{ background-color: rgba(255, 255, 255, 0.14); color: var(--fs-toast-fg); } #msgbox .closeMsgButton svg{ width: 15px; height: 15px; display: block; } /* File operation dialogs The embedded dialogs (overwrite, delete, rename, new file/folder, share, open with). Drawn as a rounded card docked to the right edge rather than a full bleed panel with a dark title bar. The progress dialog that runs in a floatWindow over a websocket is a separate page and is deliberately not restyled here. */ .popup{ position: fixed; /* Anchored to the bottom right and only as tall as its content: top is deliberately auto, because pinning both top and bottom is what used to stretch a three line confirmation over the full window height. The cap keeps a long file list inside the viewport, and .popupcontent scrolls once that cap is reached. */ top: auto; right: 10px; bottom: 10px; /* Above the transfer panel (500), below the toast and context menu (999) */ z-index: 601; width: 23em; max-width: calc(100% - 20px); max-height: calc(100% - 20px); display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--popup_border); border-radius: 12px; background-color: var(--popup_bg); box-shadow: 0px 8px 28px var(--fs-shadow); } .popup.wide{ width: 44em; } /* The header carries the card's own surface now. It used to be a dark bar with an accent underline, which read as a separate title bar sitting on top of the panel rather than part of one card. */ .popupheader{ flex: 0 0 auto; display: flex; align-items: center; gap: 9px; width: 100%; height: auto; padding: 12px 10px 12px 16px; border-bottom: 1px solid var(--divider); background-color: var(--popup_header_bg); color: var(--popup_header_txt); font-size: 14px; } /* The title is a bare text node between the icon and the closer, so it cannot be targeted directly - pushing the closer instead is what right aligns it. */ .popupheader > span{ flex: 1 1 auto; min-width: 0px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .popupheader > i.icon{ flex: 0 0 auto; width: 16px; margin: 0px; font-size: 15px; text-align: center; opacity: 0.85; } .popupcontent{ /* Grows to its content and only shrinks when the card hits its cap. min-height:0 is what lets it actually scroll then - a flex item's default min-height:auto refuses to shrink below its content. */ flex: 0 1 auto; min-height: 0; height: auto; padding: 14px 16px 16px 16px; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; color: var(--normal_object_txt); } .popupcontent p, .popupcontent small{ color: var(--fs-text-dim) !important; line-height: 1.5; } .popupcloser{ position: static; flex: 0 0 auto; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; border-radius: 6px; font-size: 100%; cursor: pointer; opacity: 0.75; } .popupcloser:hover{ background-color: var(--popup_btn_hover); opacity: 1; } .popupcloser i.icon{ margin: 0px; } /* ---------- Option rows ---------- */ .popupbuttons{ display: flex; align-items: center; gap: 10px; cursor: pointer; padding: 10px 13px; margin-bottom: 7px; border: 1px solid var(--popup_btn_border); border-radius: 9px; font-size: 13.5px; line-height: 1.35; color: var(--normal_object_txt); } .popupbuttons > i.icon{ flex: 0 0 auto; width: 16px; height: auto; margin: 0px; font-size: 14px; text-align: center; opacity: 0.75; } .popupbuttons.primary{ background-color: var(--popup_btn_primary_bg); border: 1px solid transparent; color: var(--popup_btn_primary_txt) !important; } .popupbuttons.primary > i.icon{ color: var(--popup_btn_primary_txt) !important; opacity: 1; } .popupbuttons.negative{ background-color: var(--popup_btn_negative_bg); border: 1px solid transparent; color: var(--popup_btn_negative_txt) !important; } .popupbuttons.negative > i.icon{ color: var(--popup_btn_negative_txt) !important; opacity: 1; } .popupbuttons.disabled{ opacity: 0.4; pointer-events: none; } .popupbuttons.primary.disabled{ opacity: 0.4; pointer-events: none; } /* Rename dialog */ .renameinput input{ box-shadow: none; border: 1px solid var(--divider); padding-left: 0.4em; padding-right: 0.4em; padding-top: 0.2em; padding-bottom: 0.2em; width: 100%; } .renameinput input:focus { outline: none !important; border: 1px solid var(--navi_theme); } .allowHover:hover{ background-color:var(--popup_btn_hover); } .allowHover.primary:hover{ background-color:var(--popup_btn_primary_bg_hover); } .allowHover.negative:hover{ background-color:var(--popup_btn_negative_bg_hover); } .newFileFormat{ cursor:pointer; } .popupWrapper{ position:fixed; top:0px; left:0px; width:100%; height:100%; /* Directly under .popup, so the scrim covers the transfer panel too */ z-index:600; background-color: var(--popup_wrapper_background); display:none; } /* The scrim is much lighter than it used to be, and no longer blurs: the mockup shows the file list plainly behind the card. Some dimming is kept rather than none, because the wrapper still swallows clicks - an invisible overlay that silently eats them would be worse than a faint one that explains itself. */ /* The Open With picker. Laid out like the standalone opener dialog (SystemAO/file_system/defaultOpener.html): the WebApps that registered the selected file's extension are listed first and everything else stays folded behind the toggle row, so a long module list no longer buries the two apps that can actually open the file. The box grows with its content up to a cap rather than always standing 50vh tall - a two app suggestion list used to leave most of the card empty. */ #openWithModuleList{ max-height:42vh; overflow-y:auto; overflow-x:hidden; scrollbar-width: thin; padding: 4px; background-color: var(--modulelist_bg); border: 1px solid var(--divider); border-radius: 8px; } #openWithModuleList .ui.avatar.image, #openWithModuleList .ui.avatar.image img, #openWithModuleList .ui.avatar.image svg, #openWithModuleList .ui.avatar.images .image, #openWithModuleList .ui.avatar.images img, #openWithModuleList .ui.avatar.images svg{ flex: 0 0 auto; width: 28px; height: 28px; margin: 0px; border-radius: 0; object-fit: contain; } .openWithSectionLabel{ padding: 6px 8px 4px 8px; color: var(--fs-text-dim); font-size: 12.5px; } #openWithModuleList .item{ padding: 8px 10px; border-radius: 6px; display: flex; align-items: center; gap: 10px; } #openWithModuleList .item .content{ flex: 1 1 auto; min-width: 0px; } #openWithModuleList .item .header{ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* Hidden until the row is picked, so the list does not look pre-ticked */ #openWithModuleList .item .tick{ flex: 0 0 auto; width: 20px; text-align: center; color: inherit; visibility: hidden; } #openWithModuleList .item.selected .tick{ visibility: visible; } #openWithModuleList .item:hover{ background-color: var(--modulelist_hover) !important; } #openWithModuleList .item.selected{ background-color: var(--open_with_selected) !important; color:var(--open_with_selected_text); } #openWithModuleList .item.selectable:hover{ background-color:var(--open_with_item_hover_background); } /* The one row that unfolds the WebApps which did not register this file type */ .openWithToggleRow{ display: flex; align-items: center; gap: 10px; padding: 8px 10px; margin-top: 4px; border-top: 1px solid var(--divider); border-radius: 6px; color: var(--fs-text-dim); cursor: pointer; user-select: none; } .openWithToggleRow:hover{ background-color: var(--open_with_item_hover_background); } .openWithToggleRow i.icon{ flex: 0 0 auto; width: 20px; margin: 0px; text-align: center; } .openWithEmptyNote{ padding: 14px 10px; text-align: center; color: var(--fs-text-dim); font-size: 13px; } /* New Item List */ .newfilelist{ border: 1px solid var(--divider); border-radius: 0.4em; margin-bottom: 0.6em; } .newfilelist .item{ padding: 0.6em; } .newfilelist .item:not(:last-child){ border-bottom: 1px solid var(--divider); } .newfilelist .item.selected{ background-color: var(--open_with_selected) !important; color:var(--open_with_selected_text); } /* Transfer panel The floating upload / zip task card at the bottom right. Markup shell lives in file_explorer.html, rows are drawn by js/explorer/uploadui.js. */ #uploadTab{ position: fixed; right: 16px; bottom: 16px; width: 360px; max-width: calc(100% - 32px); background-color: var(--upload_bg); border: 1px solid var(--divider); border-radius: 12px; box-shadow: 0px 8px 28px var(--fs-shadow); overflow: hidden; z-index: 500; } /* ---------- Header ---------- */ #uploadTab .uploadHeader{ display: flex; align-items: center; gap: 4px; padding: 12px 10px 12px 16px; background-color: var(--uploadtab_bg); } #uploadTab .uploadHeaderTitle{ flex: 1 1 auto; min-width: 0px; font-size: 15px; color: var(--uploadtab_txt); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #uploadTab .uploadHeaderBtn{ flex: 0 0 auto; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 6px; cursor: pointer; color: var(--fs-icon); } #uploadTab .uploadHeaderBtn:hover{ background-color: var(--fs-fill-hover); } #uploadTab .uploadHeaderBtn svg{ width: 17px; height: 17px; display: block; } /* ---------- Task list ---------- */ #uploadTab .uploadList{ padding: 0px 16px; max-height: 300px; overflow-y: auto; overflow-x: hidden; scrollbar-width: thin; } /* Three rows: name, byte counter + status, progress bar. The action button spans the first two so it centres against the text block while the bar still runs the full width of the card. */ #uploadTab .uploadTask{ display: grid; grid-template-columns: 1fr auto; column-gap: 10px; padding: 14px 0px 2px 0px; } #uploadTab .uploadTaskName{ grid-column: 1; grid-row: 1; font-size: 13.5px; line-height: 1.45; color: var(--normal_object_txt); word-break: break-word; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } #uploadTab .uploadTaskMeta{ grid-column: 1; grid-row: 2; display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 7px; font-size: 12px; color: var(--fs-text-dim); } #uploadTab .uploadTaskSize, #uploadTab .uploadTaskStatus{ color: var(--fs-text-dim); } /* A row with no byte counter has an empty size cell, and the flex gap after it would indent whatever comes next - which is what the download link on a finished download would otherwise be, sitting a notch right of the task name */ #uploadTab .uploadTaskSize:empty{ display: none; } /* Byte counters update several times a second, so every glyph has to keep its x position or the text is unreadable. Two things are needed: tabular figures so "1" occupies the same advance as "8", and a fixed cell per part so a changing digit count cannot push the neighbouring parts around. Widths are in ch (the width of a digit at the current size), so they track the font rather than assuming one. */ #uploadTab .uploadTaskSize, #uploadTab .uploadSummaryText{ font-variant-numeric: tabular-nums; -moz-font-feature-settings: "tnum"; -webkit-font-feature-settings: "tnum"; font-feature-settings: "tnum"; } /* Right aligned: the ones column stays put as the value grows into 4 digits */ #uploadTab .uploadNum{ display: inline-block; text-align: right; } /* Left aligned and padded off the number, so the unit starts at a fixed x. This one is a hard width, not a min-width: unit letters are not digit width and differ from each other ("MB" is wider than "KB", which is wider than "B"), so a min-width still lets the cell grow and shove everything after it when a transfer crosses a unit boundary. The value is sized for the widest unit rather than measured per string. */ #uploadTab .uploadUnit{ display: inline-block; width: 4.4ch; text-align: left; padding-left: 4px; } #uploadTab .uploadSep{ display: inline-block; width: 16px; text-align: center; } /* Always the right hand end of the status line, whatever is to its left: a byte counter, the download link of a finished download, or nothing at all. An auto margin rather than the row's space-between, which only reaches the edge when there happen to be two items on the line. */ #uploadTab .uploadTaskStatus{ flex: 0 0 auto; margin-left: auto; white-space: nowrap; } /* The action column holds up to two controls: cancel, which only appears once the transfer is paused, and the main pause/resume/retry button. Cancel is deliberately hidden while uploading - the pause button is the one to reach for, and putting a destructive control next to it invites misclicks. */ #uploadTab .uploadTaskActions{ grid-column: 2; grid-row: 1 / span 2; /* Bottom rather than centre: the column spans the name and the status line, and centring it hung the buttons between the two with a gap under them. Ending the column lines their base up with the status text. */ align-self: end; display: flex; align-items: center; gap: 2px; } #uploadTab .uploadTaskCancel{ display: none; width: 28px; height: 28px; align-items: center; justify-content: center; border-radius: 50%; cursor: pointer; color: var(--fs-text-faint); } #uploadTab .uploadTask.paused .uploadTaskCancel{ /* Shown by state class alone, so nothing has to rewrite this button on a progress tick - which is what used to swallow clicks on its neighbour. */ display: flex; } #uploadTab .uploadTaskCancel:hover{ background-color: var(--fs-fill-hover); color: var(--upload_progress_failed); } #uploadTab .uploadTaskCancel svg{ width: 22px; height: 22px; display: block; pointer-events: none; } /* The "Download again" link on a finished download. A completed download row shows this instead of the tick: the browser may have blocked the download that started on its own, and a tick would then be the only thing on screen while nothing had actually been saved. It lives in the status line rather than the button column, so it reads as one row with the label opposite it - the link at the left of the line, and "Completed" pinned to its right hand end. */ #uploadTab .uploadTaskDoneLink{ display: none; white-space: nowrap; color: var(--upload_progressbar); cursor: pointer; } #uploadTab .uploadTask.withDoneLink .uploadTaskDoneLink{ display: inline-block; } #uploadTab .uploadTaskDoneLink:hover{ text-decoration: underline; } /* No control on a finished row - see renderUploadTask() */ #uploadTab .uploadTask.done .uploadTaskAction{ display: none; } #uploadTab .uploadTaskAction{ width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; border-radius: 50%; cursor: pointer; color: var(--upload_progressbar); } #uploadTab .uploadTaskAction svg{ width: 26px; height: 26px; display: block; /* The glyph must not be the event target. It is swapped out whenever the task changes state, and a press that spans the swap would leave mousedown and mouseup on two different elements - which produces no click at all. Letting the pointer fall through to the button itself makes the whole 30px control clickable regardless of what the icon does underneath. */ pointer-events: none; } #uploadTab .uploadTask.failed .uploadTaskAction{ color: var(--upload_progress_failed); } #uploadTab .uploadTaskAction:hover{ background-color: var(--fs-fill-hover); } /* ---------- Progress bar ---------- */ #uploadTab .uploadTaskBar{ grid-column: 1 / -1; grid-row: 3; height: 4px; margin-top: 10px; border-radius: 999px; background-color: var(--fs-line); overflow: hidden; } #uploadTab .uploadTaskBarFill{ width: 0%; height: 100%; border-radius: 999px; background-color: var(--upload_progressbar); transition: width 0.2s linear; } #uploadTab .uploadTask.done .uploadTaskBarFill{ background-color: var(--upload_progress_succ); } #uploadTab .uploadTask.failed .uploadTaskBarFill{ background-color: var(--upload_progress_failed); } #uploadTab .uploadTask.paused .uploadTaskBarFill{ background-color: var(--fs-text-faint); } /* A task with no announced total sweeps instead of filling */ #uploadTab .uploadTask.indeterminate .uploadTaskBarFill{ width: 40%; transition: none; animation: uploadSweep 1.4s ease-in-out infinite; } @keyframes uploadSweep { 0% { transform: translateX(-105%); } 100% { transform: translateX(255%); } } /* ---------- Summary row ---------- */ #uploadTab .uploadSummary{ display: flex; align-items: center; gap: 10px; padding: 12px 16px; margin-top: 6px; border-top: 1px solid var(--divider); } #uploadTab .uploadSummaryIcon{ flex: 0 0 auto; color: var(--fs-icon); } #uploadTab .uploadSummaryIcon svg{ width: 19px; height: 19px; display: block; } /* The filled part of the ring, sized by setUploadSummaryRing() */ #uploadTab .uploadSummaryIcon .taskProgressArc{ color: var(--upload_progressbar); transition: stroke-dasharray 0.25s linear; } #uploadTab .uploadSummaryText{ flex: 1 1 auto; min-width: 0px; font-size: 12.5px; color: var(--fs-text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* ---------- Footer buttons ---------- */ #uploadTab .uploadActions{ display: flex; align-items: center; gap: 10px; padding: 4px 16px 16px 16px; } #uploadTab .uploadActionBtn{ display: flex; align-items: center; gap: 7px; padding: 8px 14px; border: 1px solid var(--popup_btn_border); border-radius: 8px; cursor: pointer; font-size: 12.5px; white-space: nowrap; color: var(--normal_object_txt); } #uploadTab .uploadActionBtn:hover{ background-color: var(--fs-fill-hover); } #uploadTab .uploadActionBtn svg{ width: 15px; height: 15px; display: block; color: var(--fs-icon); } #uploadTab .uploadActionBtn.primary{ margin-left: auto; border-color: var(--upload_progressbar); color: var(--upload_progressbar); } #uploadTab .uploadActionBtn.primary:hover{ background-color: var(--fs-accent-soft); } @media screen and (max-width: 500px) { #uploadTab{ right: 12px; left: 12px; bottom: 12px; width: auto; max-width: none; } .uploadList{ max-height: 40vh; } } /* Special message or exceptions */ .ui.themed.header{ color: var(--text_color); } .ui.themed.header .sub.header{ color: var(--text_color_secondary); } code{ background-color: var(--code_bg); color: var(--code_txt); line-height: 2.5; padding: 0.1em; padding-left: 0.4em; padding-right: 0.4em; border-radius: 0.2em; } /* Legacy supports (Deprecated) */ .normal.object{ color:var(--normal_object_txt); } .hex.foldername{ color: var(--hex_folder_txt) !important; } .um.filename{ color: var(--um_file_txt) !important; } /* Hot Search Related */ i.blue{ color: #345eeb; } .fileObject.hotSearchHighlight{ background-color: #d2f2f7 !important; } /* Responsive */ @media (max-width:1035px) { .viewportBtns{ display: block; float: right; } } @media (max-width:840px) { .viewportBtns{ float: none; } .tabletAndDesktopOnly{ display:none !important; } .mobileFileOprMenu{ display:block !important; } .navibar.mobile .fileOprBtn{ padding-top: 0.1em; padding-bottom: 0em; } .navibar.mobile .twolines.fileOprBtn{ padding-top: 0.1em; padding-bottom: 0em; } .navibar.mobile .twolines.fileOprBtn .opricon{ width: 36px; } .navibar.mobile .twolines.fileOprBtn .oprtxt{ display:none !important; } .navibar.mobile .wideScreenOnly{ display:none; } .navibar.mobile .fileoprGroupDivider{ height: 42px; } .mobileFileOprMenu{ top: 0; right: 0.4em; margin-top: -0.2em; } } @media (max-width:620px) { #pathInputField{ width: 100%; } #pathInputField input{ margin-left: 0; } .ui.selection.dropdown{ min-width: 8em; } /*Directory sidebar, full width */ #directorySidebar{ width: 100%; min-width: 100%; } /* grid mode preview */ .fileviewList .fileObject.card{ border-radius: 0.4em; position: relative; margin-bottom:1em; width: calc(33% - 4px) !important; min-width: 100px; max-width: 148px; } /* Dialogs centre in the viewport instead of hugging a corner. Centred with inset:0 + margin:auto rather than a translate(-50%,-50%), because these are shown with semantic's transition("slide left in") - its keyframes set transform themselves, so a centring transform would be dropped for the length of the animation and the card would visibly jump into place at the end. Auto margins are untouched by the animation. The auto margins need a definite height to resolve against, which is what height:fit-content provides - plain auto height would stretch to fill the insets instead. */ .popup{ right: 0; bottom: 2em; left: 0; margin: auto; width: calc(100% - 28px); max-width: 420px; height: -webkit-fit-content; height: fit-content; max-height: calc(100% - 40px); } /* Properties view, disable function in small display size*/ #propertiesView{ display:none !important; } #togglePropertiesViewBtn{ display:none !important; } /* ...and with no pane to resize, neither does its handle */ #fmPropsSplitter{ display:none !important; } } @media (max-width:450px) { .viewportBtns{ float: right; } } /* ====================================================================== Redesigned navigation row and sidebar (Phase 2.3 / 2.4) Colours come from shared/fs-theme.css, components from shared/fs-components.css. Only layout lives here. ====================================================================== */ .navibar { padding: 0 !important; height: auto !important; } .fmNav { display: flex; align-items: center; gap: 8px; padding: 8px 12px; position: relative; background: var(--fs-bg); border-bottom: 1px solid var(--fs-line); } .fmNavBtns { display: flex; align-items: center; gap: 4px; flex-shrink: 0; } .fmNav .fsIconBtn.disabled, .fmNav .fsIconBtn:disabled { opacity: 0.35; pointer-events: none; } /* ---------- Breadcrumb pill ---------- */ .fmPathbar { flex-grow: 1; min-width: 0; cursor: text; padding-left: 4px; padding-right: 0px; } .fmPathHome { width: 26px; height: 26px; min-width: 26px; border: none; background: transparent; border-radius: 6px; padding: 4px; color: var(--fs-icon); cursor: pointer; flex-shrink: 0; } .fmPathHome:hover { background: var(--fs-fill); color: var(--fs-text); } .fmPathbar .pathDisplay { flex-grow: 1; min-width: 0; overflow: hidden; white-space: nowrap; font-size: 14px; margin: 0 !important; color: var(--fs-text); } .fmPathbar .pathDisplay .section { padding: 3px 7px; border-radius: 6px; cursor: pointer; } .fmPathbar .pathDisplay .section:hover { background: var(--fs-fill); } .fmPathbar .pathDisplay .divider { color: var(--fs-text-faint); } .fmPathInput { display: flex; align-items: center; gap: 6px; flex-grow: 1; min-width: 0; } .fmPathInput input { flex-grow: 1; min-width: 0; border: none; outline: none; background: transparent; font-size: 14px; font-family: inherit; color: var(--fs-text); padding: 4px 2px; } /* ---------- Search pill ---------- */ .fmSearch { width: 240px; flex-shrink: 0; padding-right: 8px; } .fmSearch input { flex-grow: 1; min-width: 0; border: none; outline: none; background: transparent; font-size: 14px; font-family: inherit; color: var(--fs-text); } .fmSearchIcon { width: 17px; height: 17px; flex-shrink: 0; color: var(--fs-text-dim); } .fmSearchIcon svg { display: block; width: 100%; height: 100%; } .fmCaseBtn { border: none; background: transparent; color: var(--fs-text-dim); font-size: 12px; font-weight: 600; border-radius: 5px; padding: 3px 5px; cursor: pointer; flex-shrink: 0; } .fmCaseBtn:hover { background: var(--fs-fill); color: var(--fs-text); } .fmCaseBtn.active { background: var(--fs-accent-soft); color: var(--fs-accent-strong); } /* ---------- View toggle ---------- */ .fsViewToggle { display: flex; background: var(--fs-fill); border-radius: 8px; padding: 2px; gap: 2px; flex-shrink: 0; } .fsViewToggle button { width: 30px; height: 26px; border: none; background: transparent; border-radius: 6px; padding: 5px; color: var(--fs-icon); cursor: pointer; } .fsViewToggle button:hover { background: var(--fs-fill-hover); } .fsViewToggle button.disabled, .fsViewToggle button:disabled { background: var(--fs-surface); color: var(--fs-accent); box-shadow: 0 1px 2px rgba(0,0,0,0.12); pointer-events: none; opacity: 1; } .fsViewToggle button svg { display: block; width: 100%; height: 100%; } /* ---------- Overflow menu button ---------- */ .fsMoreBtn { width: 32px; height: 32px; min-width: 32px; border: 1px solid var(--fs-line); background: var(--fs-surface); border-radius: 8px; padding: 7px; color: var(--fs-text); cursor: pointer; flex-shrink: 0; } .fsMoreBtn:hover { background: var(--fs-fill); } .fsMoreBtn.active { background: var(--fs-accent-soft); color: var(--fs-accent); border-color: transparent; } .fsMoreBtn svg { display: block; width: 100%; height: 100%; } .fmMoreMenu { right: 12px; top: 46px; max-height: 70vh; overflow-y: auto; overflow-x: hidden; } .fmSortRow select { flex-grow: 1; border: 1px solid var(--fs-line); border-radius: 6px; background: var(--fs-surface); color: var(--fs-text); font-family: inherit; font-size: 13px; padding: 3px 6px; outline: none; } .fmSortRow:hover { background: transparent; } /* ---------- Sidebar ---------- */ #directorySidebar { padding: 10px 8px 10px 10px !important; background: var(--fs-bg); border-right: 1px solid var(--fs-line); } .fsSideTitle { font-size: 12px; color: var(--fs-text-faint); padding: 4px 8px; margin-top: 4px; } .fsSideTitle:first-child { margin-top: 0; } .fmSideGroup { margin-bottom: 6px; } #directorySidebar .fsSideItem { display: flex !important; align-items: center; gap: 10px; padding: 5px 8px !important; border-radius: 7px; cursor: pointer; white-space: nowrap; overflow: hidden; color: var(--fs-text); margin: 0 !important; } #directorySidebar .fsSideItem:hover { background: var(--fs-fill); } #directorySidebar .fsSideItem.active { background: var(--fs-accent-soft); color: var(--fs-accent-strong); } #directorySidebar .fsSideItem .fsSideIcon { width: 20px; height: 20px; min-width: 20px; } #directorySidebar .fsSideItem .fsSideIcon svg { display: block; width: 100%; height: 100%; } #directorySidebar .fsSideItem .fsSideLabel { overflow: hidden; text-overflow: ellipsis; } /* Separates the mounted storage devices above from the special views below them - the trash bin and anything that joins it later. Without it the two kinds run together and the bin reads as another drive. */ #directorySidebar .fsSideDivider { height: 1px; background: var(--fs-line); margin: 7px 8px; } #selectInfo { font-size: 12px; color: var(--fs-text-faint); padding: 8px; margin: 0; } /* ---------- Narrow windows ---------- */ @media only screen and (max-width: 900px) { .fmSearch { width: 170px; } } @media only screen and (max-width: 720px) { .fmSearch { display: none; } } /* ====================================================================== Redesigned file list, grid and status bar (Phase 2.5 - 2.8) ====================================================================== */ /* ---------- Shared row icon ---------- */ .fmRowIcon { width: 20px; height: 20px; min-width: 20px; display: inline-block; vertical-align: middle; flex-shrink: 0; } .fmRowIcon .fsSmallIcon { width: 100%; height: 100%; } /* ---------- List view (single column) ---------- */ .fileviewList .fileObject.item { display: flex !important; align-items: center; gap: 9px; height: 38px; padding: 0 14px 0 16px !important; cursor: pointer; border-bottom: 1px solid var(--fs-line-soft); } .fileviewList .fileObject.item .filename { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ---------- Details view: the mockup's columned list ---------- */ #folderView.fmDetailsView { padding-top: 0px; border-top: 10px solid var(--folder_view_background); } #fmListHeader { position: sticky; top: 0; z-index: 6; background: var(--fs-bg); border-bottom: 1px solid var(--fs-line); } .detailstable { table-layout: fixed !important; width: 100% !important; margin: 0 !important; border-collapse: collapse !important; } #fmListHeader th { font-size: 13px !important; font-weight: 400 !important; color: var(--fs-text-dim) !important; padding: 6px 8px !important; text-align: left; background: transparent !important; border: none !important; white-space: nowrap; } #fmListHeader th.fmSortable { cursor: pointer; } #fmListHeader th.fmSortable:hover { background: var(--fs-fill) !important; } #fmListHeader th.sorted { color: var(--fs-text) !important; } .fmSortMark { margin-left: 5px; font-size: 11px; color: var(--fs-text-dim); } .detailstable tr.fileObject.details { cursor: pointer; } .detailstable tr.fileObject.details td { padding: 6px 8px !important; border: none !important; border-bottom: 1px solid var(--fs-line-soft) !important; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: middle; } /* The name cell must stay a real table-cell: display:flex on a drops it out of the table formatting context and collapses the column to zero width. The flex row goes on an inner wrapper instead. */ .detailstable td.fmColName { color: var(--fs-text); } .detailstable td.fmColName .fmNameCell { display: flex; align-items: center; gap: 9px; min-width: 0; } .detailstable td.fmColName .filename { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .detailstable td.fmColDate, .detailstable td.fmColType, .detailstable td.fmColSize { color: var(--fs-text-dim); } /* ---------- Selection highlight, all three modes ---------- Selection is shown by the row/tile fill alone - there is no check badge in any view mode. #folderView still gains .fmMultiSelect while ctrl/shift is held or mobile multi-select is on, but nothing keys off it visually. */ .fileviewList .fileObject.item:hover, .detailstable tr.fileObject.details:hover { background: var(--fs-row-hover); } .fileviewList .fileObject.item.selected, .detailstable tr.fileObject.details.selected { background: var(--fs-selected) !important; } .fileviewList .fileObject.item.selected .filename, .detailstable tr.fileObject.details.selected td { color: var(--fs-text) !important; } /* ---------- Grid view ---------- */ #folderView { --fm-tile: 170px; } #folderList.fileviewList, #fileList.fileviewList { padding: 0 !important; } .fileviewList .fileObject.card { width: var(--fm-tile); display: inline-block !important; vertical-align: top; margin: 0 !important; padding: 8px; border: 1px solid transparent; border-radius: 10px; background: transparent; box-shadow: none !important; overflow: hidden; cursor: pointer; } .fileviewList .fileObject.card:hover { background: var(--fs-row-hover); } .fileviewList .fileObject.card.selected { background: var(--fs-selected) !important; border-color: var(--fs-selected-line); } .fileviewList .fileObject.card .image { position: relative; text-align: center; border-radius: 8px; overflow: hidden; background: var(--fs-thumb-bg); } .fileviewList .fileObject.card .content { padding: 7px 2px 2px 2px !important; border: none !important; } .fileviewList .fileObject.card .content .header { font-size: 13px !important; font-weight: 400 !important; color: var(--fs-text) !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .fileviewList .fileObject.card .content .description { font-size: 11px; color: var(--fs-text-faint); margin-top: 2px; } /* Date group headers */ .fmGroupHeader { font-size: 13px; font-weight: 600; color: var(--fs-text); padding: 14px 6px 6px 6px; } .fmGroup { display: block; } /* ====================================================================== Trash bin view Rendered into #folderList by js/explorer/trash.js when the path is the %trashbin% sentinel. It replaces the file listing entirely, so it owns the full width of the file area rather than sitting alongside file objects. ====================================================================== */ .fmTrashLoading{ padding: 28px 4px; color: var(--fs-text-dim); } .fmTrashHeader{ position: relative; display: flex; align-items: center; gap: 16px; padding: 4px 4px 20px 4px; } .fmTrashIcon{ flex: 0 0 auto; width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--fs-fill); color: var(--fs-text-dim); } .fmTrashIcon svg{ width: 28px; height: 28px; display: block; } .fmTrashHeadText{ flex: 1 1 auto; min-width: 0px; } .fmTrashTitle{ font-size: 20px; color: var(--fs-text); margin-bottom: 3px; } .fmTrashDesc{ font-size: 12.5px; color: var(--fs-text-dim); line-height: 1.5; } /* Usage readout. Its numbers are placeholders until retention lands. */ .fmTrashUsage{ flex: 0 0 auto; width: 190px; } .fmTrashUsageLabel{ font-size: 12px; color: var(--fs-text-dim); } .fmTrashUsageValue{ font-size: 19px; color: var(--fs-accent); margin: 2px 0px 6px 0px; } .fmTrashUsageBar{ height: 6px; border-radius: 999px; background: var(--fs-line); overflow: hidden; } .fmTrashUsageFill{ height: 100%; border-radius: 999px; background: var(--fs-accent); } .fmTrashUsageTotal{ margin-top: 5px; font-size: 12px; color: var(--fs-text-dim); } .fmTrashEmptyBtn{ flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 10px 16px; border: none; border-radius: 9px; cursor: pointer; font-size: 13.5px; white-space: nowrap; background: var(--upload_progress_failed); color: #ffffff; } .fmTrashEmptyBtn:hover{ opacity: 0.9; } .fmTrashBtnIcon svg{ width: 16px; height: 16px; display: block; } .fmTrashMoreBtn{ flex: 0 0 auto; width: 30px; height: 30px; padding: 6px; border: none; background: transparent; border-radius: 6px; cursor: pointer; color: var(--fs-icon); } .fmTrashMoreBtn:hover{ background: var(--fs-fill); } .fmTrashMoreBtn svg{ width: 100%; height: 100%; display: block; } .fmTrashBulkMenu{ position: absolute; top: 44px; right: 0px; min-width: 220px; } /* ---------- Listing ---------- */ .fmTrashTable{ width: 100%; border-collapse: collapse; font-size: 13px; } .fmTrashTable th{ padding: 10px 10px; text-align: left; font-weight: 400; color: var(--fs-text-dim) !important; border-top: 1px solid var(--fs-line); border-bottom: 1px solid var(--fs-line); white-space: nowrap; } .fmTrashTable td{ padding: 9px 10px; border-bottom: 1px solid var(--fs-line-soft); color: var(--fs-text) !important; vertical-align: middle; } .fmTrashRow:hover{ background: var(--fs-row-hover); } .fmTrashRow.selected{ background: var(--fs-selected); } .fmTrashDim{ color: var(--fs-text-dim) !important; white-space: nowrap; } /* The flex row lives on a span inside the cell, never on the itself. display:flex on a table cell drops it out of the table formatting context, which collapses the column and leaves its bottom border out of line with every other cell in the row. */ .fmTrashName{ display: flex; align-items: center; gap: 9px; min-width: 0px; } .fmTrashNameText{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* ---------- Sortable headers ---------- */ .fmTrashSortable{ cursor: pointer; } .fmTrashSortable:hover{ background: var(--fs-fill); } .fmTrashTable th.sorted{ color: var(--fs-text) !important; } .fmTrashSortMark{ display: inline-block; width: 12px; margin-left: 3px; font-size: 11px; } /* ---------- Details dialog ---------- */ .fmTrashDetailTable{ width: 100%; border-collapse: collapse; font-size: 13px; margin-bottom: 14px; } .fmTrashDetailTable td{ padding: 7px 0px; border-bottom: 1px solid var(--fs-line-soft); word-break: break-all; color: var(--normal_object_txt) !important; } .fmTrashDetailKey{ width: 38%; color: var(--fs-text-dim) !important; white-space: nowrap; } /* ---------- Special view root chip in the address bar ---------- */ .fmSpecialRoot{ display: inline-flex !important; align-items: center; gap: 7px; } .fmSpecialRootIcon{ width: 16px; height: 16px; color: var(--fs-icon); } .fmSpecialRootIcon svg{ width: 100%; height: 100%; display: block; } .fmTrashRowIcon{ flex: 0 0 auto; width: 20px; height: 20px; color: var(--fs-icon); } .fmTrashRowIcon svg{ width: 100%; height: 100%; display: block; } .fmTrashColCheck{ width: 34px; } /* Drawn rather than an , to match the check styling used elsewhere */ .fmTrashCheck{ display: inline-block; width: 16px; height: 16px; border: 1.5px solid var(--fs-line); border-radius: 4px; background: var(--fs-surface); cursor: pointer; position: relative; vertical-align: middle; } .fmTrashCheck.checked{ background: var(--fs-accent); border-color: var(--fs-accent); } .fmTrashCheck.checked::after{ content: ""; position: absolute; left: 4.5px; top: 1px; width: 4px; height: 8px; border: solid var(--fs-primary-fg); border-width: 0px 2px 2px 0px; transform: rotate(45deg); } .fmTrashActionCell{ width: 1%; white-space: nowrap; text-align: right; } .fmTrashRestoreBtn{ padding: 6px 14px; border: 1px solid var(--popup_btn_border); border-radius: 7px; background: transparent; cursor: pointer; font-size: 12.5px; color: var(--fs-text); } .fmTrashRestoreBtn:hover{ background: var(--fs-fill-hover); } .fmTrashRowMore{ width: 26px; height: 26px; padding: 4px; border: none; background: transparent; border-radius: 6px; cursor: pointer; color: var(--fs-icon); } .fmTrashRowMore:hover{ background: var(--fs-fill); } .fmTrashRowMore svg{ width: 100%; height: 100%; display: block; } .fmTrashRowMenu{ position: fixed; min-width: 210px; z-index: 610; } .fmTrashEmpty{ padding: 40px 10px !important; text-align: center; color: var(--fs-text-dim) !important; } /* Sits at the end of the table while the server is still walking the file system, with rows appearing above it as they are found. */ .fmTrashScanning{ padding: 14px 10px !important; color: var(--fs-text-dim) !important; } /* The flex box is an inner element rather than the cell itself: display:flex on a takes it out of the table's formatting context, which drops the colspan and squeezes the row into the width of the first column. */ .fmTrashScanInner{ display: flex; align-items: center; gap: 9px; } .fmTrashSpinner{ width: 13px; height: 13px; min-width: 13px; border: 2px solid var(--fs-line); border-top-color: var(--fs-accent); border-radius: 50%; display: inline-block; animation: fmTrashSpin 0.7s linear infinite; } @keyframes fmTrashSpin { to { transform: rotate(360deg); } } /* A spinning ring is exactly what this setting asks to be spared */ @media (prefers-reduced-motion: reduce) { .fmTrashSpinner{ animation: none; } } .fmTrashFootnote{ display: flex; align-items: center; gap: 9px; margin-top: 16px; padding: 12px 14px; border-radius: 9px; background: var(--fs-fill); font-size: 12.5px; color: var(--fs-text-dim); } .fmTrashFootIcon{ flex: 0 0 auto; width: 17px; height: 17px; color: var(--fs-accent); } .fmTrashFootIcon svg{ width: 100%; height: 100%; display: block; } /* The original path is the widest column in the table and the one the layout can most afford to lose. Driven by a class from updateListDensity() rather than a media query, because the file area is bounded by two draggable panes and the viewport width says nothing useful about it. */ #folderView.fmTrashHideOrigin .fmTrashColOrigin{ display: none !important; } @media only screen and (max-width: 720px) { /* The usage readout is the first thing to go when space is tight */ .fmTrashHeader{ flex-wrap: wrap; } .fmTrashUsage{ display: none; } .fmTrashTable th:nth-child(4), .fmTrashTable td:nth-child(4){ display: none; } } /* ---------- Trash bin: card layout (phones and very narrow panes) ---------- */ /* Drawn instead of the table below FM_TRASH_CARD_MAX_WIDTH - see useTrashCardLayout() in js/explorer/trash.js. The same rows, restacked: one line per item, with restore and delete moved to a bar along the bottom where they act on the selection. */ .fmTrashCardHead{ background: var(--fs-surface); border: 1px solid var(--fs-line); border-radius: 14px; padding: 16px; margin: 10px 10px 12px 10px; } .fmTrashCardTop{ display: flex; align-items: center; gap: 13px; } .fmTrashCardHead .fmTrashIcon{ flex: 0 0 auto; width: 46px; height: 46px; border-radius: 50%; background: var(--fs-fill); display: flex; align-items: center; justify-content: center; color: var(--fs-icon); } .fmTrashCardHead .fmTrashIcon svg{ width: 24px; height: 24px; display: block; } .fmTrashCardHead .fmTrashTitle{ font-size: 17px; font-weight: 600; color: var(--fs-text) !important; } .fmTrashCardHead .fmTrashDesc{ font-size: 12px; line-height: 1.45; color: var(--fs-text-dim) !important; margin-top: 2px; } .fmTrashCardUsage{ margin-top: 15px; } .fmTrashCardUsage .fmTrashUsageLabel{ font-size: 12px; color: var(--fs-text-dim) !important; } .fmTrashUsageRow{ display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-top: 2px; } .fmTrashCardUsage .fmTrashUsageValue{ font-size: 21px; font-weight: 600; color: var(--fs-accent) !important; } .fmTrashCardUsage .fmTrashUsageTotal{ font-size: 12px; color: var(--fs-text-dim) !important; } .fmTrashCardUsage .fmTrashUsageBar{ margin-top: 8px; width: 100%; height: 6px; border-radius: 3px; background: var(--fs-fill); overflow: hidden; } .fmTrashCardUsage .fmTrashUsageFill{ height: 100%; border-radius: 3px; background: var(--fs-accent); } .fmTrashCardEmptyBtn{ margin-top: 15px; display: flex; align-items: center; gap: 8px; width: 100%; padding: 0; border: none; background: none; cursor: pointer; font-size: 14px; font-family: inherit; color: var(--upload_progress_failed); } .fmTrashCardEmptyBtn .fmTrashBtnIcon{ width: 16px; height: 16px; } .fmTrashCardEmptyBtn .fmTrashBtnIcon svg{ width: 100%; height: 100%; display: block; } /* Kind filter chips, standing in for the table's sortable headers */ .fmTrashChips{ position: relative; display: flex; align-items: center; gap: 8px; padding: 0 10px 10px 10px; overflow-x: auto; scrollbar-width: none; } .fmTrashChips::-webkit-scrollbar{ display: none; } .fmTrashChip{ flex: 0 0 auto; padding: 6px 13px; border-radius: 999px; border: 1px solid var(--fs-line); background: var(--fs-bg); color: var(--fs-text-dim); font-size: 12.5px; font-family: inherit; cursor: pointer; white-space: nowrap; } .fmTrashChip.active{ background: var(--fs-accent-soft); border-color: transparent; color: var(--fs-accent-strong); font-weight: 500; } .fmTrashChipSpacer{ flex: 1 1 auto; } .fmTrashChipMore{ flex: 0 0 auto; width: 30px; height: 30px; padding: 6px; border: none; border-radius: 8px; background: none; cursor: pointer; color: var(--fs-icon); } .fmTrashChipMore svg{ width: 100%; height: 100%; display: block; } .fmTrashChips .fmTrashBulkMenu{ right: 10px; top: 38px; } /* The rows themselves */ .fmTrashCards{ padding: 0px; } .fmTrashCardRow{ display: flex; align-items: center; gap: 11px; padding: 10px 10px; border-bottom: 1px solid var(--fs-line-soft); } .fmTrashCardRow .fmTrashCheck{ flex: 0 0 auto; } .fmTrashCardIcon{ flex: 0 0 auto; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; } .fmTrashCardIcon svg, .fmTrashCardIcon img{ width: 100%; height: 100%; display: block; object-fit: contain; } .fmTrashCardText{ flex: 1 1 auto; min-width: 0; /* lets the name ellipsis instead of stretching the row */ } .fmTrashCardName{ font-size: 13.5px; color: var(--fs-text) !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .fmTrashCardMeta{ font-size: 11.5px; color: var(--fs-text-dim) !important; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .fmTrashCardDays{ flex: 0 0 auto; font-size: 11.5px; color: var(--fs-text-dim) !important; white-space: nowrap; } .fmTrashCardRow .fmTrashRowMore{ flex: 0 0 auto; width: 26px; height: 26px; padding: 5px; } .fmTrashCards .fmTrashEmpty{ padding: 40px 10px !important; } .fmTrashCards .fmTrashScanRow{ padding: 14px 4px; } /* Action bar. In normal flow rather than fixed: the file area is the thing that scrolls, and a fixed bar would sit over the desktop chrome outside it. */ .fmTrashActionBar{ /* Sticky to the foot of the file area: the list can run well past one screen, and the two buttons are the whole point of selecting anything, so they follow the scroll instead of sitting at the very bottom. */ position: sticky; bottom: 8px; z-index: 2; display: flex; align-items: center; gap: 10px; margin: 12px 10px 14px 10px; padding: 11px 12px; border: 1px solid var(--fs-line); border-radius: 12px; background: var(--fs-surface); } .fmTrashBarCount{ flex: 1 1 auto; font-size: 12px; color: var(--fs-text-dim) !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .fmTrashBarBtn{ flex: 0 0 auto; display: flex; align-items: center; gap: 6px; padding: 7px 12px; border: 1px solid var(--fs-line); border-radius: 9px; background: var(--fs-bg); color: var(--fs-text); font-size: 12.5px; font-family: inherit; cursor: pointer; white-space: nowrap; } .fmTrashBarBtn.danger{ border-color: transparent; background: var(--fs-danger-soft); color: var(--upload_progress_failed); font-weight: 500; } .fmTrashBarBtn .fmTrashBtnIcon{ width: 14px; height: 14px; } .fmTrashBarBtn .fmTrashBtnIcon svg{ width: 100%; height: 100%; display: block; } /* Nothing selected yet: the buttons are still there, just visibly inert */ .fmTrashActionBar:not(.hasSelection) .fmTrashBarBtn{ opacity: 0.5; } /* ---------- Status bar ---------- */ #fmStatusBar { display: flex; align-items: center; gap: 10px; padding: 6px 12px; border-top: 1px solid var(--fs-line); background: var(--fs-bg); font-size: 12px; color: var(--fs-text-dim); flex-shrink: 0; } .fmStatusSpacer { flex-grow: 1; } #fmSelectionSize { color: var(--fs-text); } #fmZoom { display: flex; align-items: center; gap: 6px; } .fmZoomIcon { /* inline-block, because width and height do not apply to a non-replaced inline element. As a bare this box silently loses its size the moment anything stops #fmZoom being a flex container, and the SVG inside then renders at its 300px intrinsic size. */ display: inline-block; flex-shrink: 0; width: 14px; height: 14px; color: var(--fs-text-faint); } .fmZoomIcon svg { display: block; width: 100%; height: 100%; } #fmZoomSlider { width: 96px; accent-color: var(--fs-accent); cursor: pointer; } .fmStatusToggle button { width: 26px; height: 22px; padding: 4px; } .fmStatusBtn { width: 26px; height: 26px; border: none; background: transparent; border-radius: 6px; padding: 5px; color: var(--fs-icon); cursor: pointer; } .fmStatusBtn:hover { background: var(--fs-fill); } .fmStatusBtn svg { display: block; width: 100%; height: 100%; } /* Collapsed transfer panel Scoped to the id rather than .fmStatusBtn.active so the properties toggle, which also carries an .active class, keeps its current look. */ #fmUploadListBtn.active{ background: var(--fs-fill-active); color: var(--fs-accent); } /* Slow pulse while something is still transferring, so background progress is noticeable without being a distraction. Animating the colour rather than the whole button keeps the hit area and the hover fill steady. */ #fmUploadListBtn.transferring{ animation: uploadBtnPulse 2.2s ease-in-out infinite; } @keyframes uploadBtnPulse { 0% { color: var(--fs-icon); } 50% { color: var(--fs-accent); } 100% { color: var(--fs-icon); } } /* A blinking control is exactly what this setting asks to be spared */ @media (prefers-reduced-motion: reduce) { #fmUploadListBtn.transferring{ animation: none; color: var(--fs-accent); } } @media only screen and (max-width: 720px) { /* updateZoomControlVisibility() shows this with an inline style, which outranks a plain media query - hence !important. Without it the zoom control rendered at full size on phones and pushed the status bar over. */ /* Only the zoom slider is too wide for a phone. The view toggle and the properties button are small enough to keep, and hiding them left the footer with no controls at all. */ #fmZoom { display: none !important; } .fmStatusToggle button { width: 30px; height: 26px; } } /* ---------- Details column widths + responsive column dropping ---------- With table-layout:fixed the first row defines the columns, so these widths apply to the header table and both row tables identically. Hiding a cell collapses its column, which a could not do. */ .detailstable .fmColDate { width: 170px; } .detailstable .fmColType { width: 120px; } .detailstable .fmColSize { width: 90px; } .detailstable .fmColName { width: auto; } /* Force identical box geometry between the header and the row tables */ #fmListHeader table.detailstable, .fileviewList table.detailstable { border: none !important; box-shadow: none !important; background: transparent !important; } #folderView.fmNarrow .fmColType { display: none !important; } #folderView.fmVeryNarrow .fmColDate { display: none !important; } /* The legacy .fileObject.details rule put a 1px border on the , which inset every row cell by 1px and left the columns misaligned with the header table. */ .detailstable tr.fileObject.details { border: none !important; } /* ---------- Classic file operation toolbar ---------- */ /* Sits above the nav row; toggled from the overflow menu and remembered server side under file_explorer/oprbar. It must never wrap to a second row - that was the old behaviour and it ate vertical space the file list needs. Instead the bar collapses in stages as the window narrows: first the stacked text-button groups drop out (rightmost first, since those actions are the least used), then the big buttons lose their labels and become icons. Anything dropped reappears in the overflow menu automatically, because syncOprMenuDuplicates() checks what is actually visible rather than assuming. */ .fileOprBar { display: flex; align-items: flex-start; flex-wrap: nowrap; gap: 2px; padding: 6px 12px 4px 12px; border-bottom: 1px solid var(--fs-line); background: var(--fs-bg); overflow: hidden; white-space: nowrap; } .fileOprBar .fileOprBtn { flex-shrink: 0; min-width: 0; } /* One line per split. The legacy .navibar .fileoprGroupDivider rule already draws a border-right; adding a border-left here rendered two lines side by side wherever two groups met. */ .fileOprBar .fileoprGroupDivider { display: inline-block; vertical-align: top; border-left: none; border-right: 1px solid var(--fs-line-soft); margin: 0 6px 0 2px; padding-right: 8px; flex-shrink: 0; } .fileOprBar .fileOprBtn .oprtxt { font-size: 12px; margin: 2px 0 0 0; white-space: nowrap; } /* Stage 1: drop the stacked groups, rightmost (least used) first */ @media only screen and (max-width: 960px) { .fileOprBar .fileoprGroupDivider:nth-of-type(4) { display: none; } } @media only screen and (max-width: 840px) { .fileOprBar .fileoprGroupDivider:nth-of-type(3) { display: none; } } @media only screen and (max-width: 600px) { .fileOprBar .fileoprGroupDivider { display: none; } } /* Stage 2: big buttons become icon-only */ @media only screen and (max-width: 720px) { .fileOprBar .fileOprBtn .oprtxt { display: none; } .fileOprBar .fileOprBtn .opricon { width: 32px; } .fileOprBar { padding-bottom: 6px; } } /* Stage 3: below this the nav row alone is the interface */ @media only screen and (max-width: 720px) { .fileOprBar { display: none !important; } } /* ---------- Grid tile corrections ---------- */ /* The legacy .card .content .description rule positions the size absolutely against the card, which only worked while cards had a fixed 20em height. Cards are content-sized now, so it has to sit in the normal flow or it lands on top of the filename. */ .fileviewList .fileObject.card .content .description { position: static !important; bottom: auto !important; left: auto !important; font-size: 11px; color: var(--fs-text-faint); margin-top: 2px; } /* Folders are drawn glyphs, not photos - the thumbnail plate behind them just boxes in the icon. Files keep it so real thumbnails have something to sit on. */ .fileviewList .fileObject.card[type="folder"] .image { background: transparent !important; } /* ---------- Sort menu ---------- */ /* Anchored to the sort button on the right of the nav row, like the "..." menu */ .fmSortMenu { right: 52px; top: 46px; min-width: 210px; } /* Greyed section label above the options */ .fsMenuHeader { padding: 3px 6px 4px 6px; font-size: 12px; color: var(--fs-text-faint); cursor: default; user-select: none; } /* The tick sits in the icon column, so the labels stay aligned whether or not a mode is active */ .fmSortOption .fmSortCheck { visibility: hidden; color: var(--fs-accent); } .fmSortOption.active .fmSortCheck { visibility: visible; } .fmSortOption.active { color: var(--fs-text); } /* ====================================================================== Mobile layout ====================================================================== */ @media only screen and (max-width: 720px) { /* The nav row becomes two rows: controls on top, the path on its own row underneath. A single row cannot hold the breadcrumb and the buttons at phone widths without the path collapsing to nothing. */ .fmNav { flex-wrap: wrap; row-gap: 6px; padding: 6px 8px; } .fmNavBtns { flex-shrink: 0; } .fmPathbar { order: 10; flex-basis: 100%; min-width: 0; } /* Push the sort and overflow buttons to the right edge. margin-left:auto on the first of them absorbs the free space, so both end up flush right regardless of how many nav buttons precede them. */ .fmNav #fmSortBtn { margin-left: auto; } /* Menus anchor to the row above the path */ .fmMoreMenu, .fmSortMenu { top: 44px; max-width: calc(100vw - 24px); } /* Status bar: text only, no controls to overflow */ #fmStatusBar { padding: 5px 10px; gap: 8px; min-height: 0; } #fmStatusBar .fmStatusBtn { flex-shrink: 0; } #selectInfo, #fmSelectionSize { white-space: nowrap; } /* Guard: every icon in the bar stays at its intended box even if some future rule forgets to size the button around it */ #fmStatusBar svg { width: 100%; height: 100%; } #fmStatusBar .fmStatusBtn { width: 26px; height: 26px; } } /* Menu entries that cannot act on the current selection are hidden outright rather than greyed - the menu is long enough already. */ .fsMenuItem.fmOprUnusable { display: none !important; } /* An operation the current special view does not support. Dimmed rather than removed from the toolbar: the bar keeps its shape as the user moves in and out of the trash bin, and a button that has visibly gone grey explains itself better than one that has vanished. */ .fileOprBar .fmOprBlocked, .fileOprBar .fmOprBlocked:hover { opacity: 0.35; pointer-events: none; cursor: default; background: none; } /* Menu entries that only make sense on a touch device */ .fmMobileOnly { display: none; }