Add smartphone screen mode

This commit is contained in:
231088 2026-05-25 21:42:05 +09:00
parent dfdcbab685
commit ceae463c02
3 changed files with 1207 additions and 12 deletions

820
javascript/mobileMode.js Normal file
View File

@ -0,0 +1,820 @@
(function() {
const MOBILE_QUERY = "(max-width: 760px), (pointer: coarse) and (max-width: 900px)";
const GENERATION_TABS = ["txt2img", "img2img"];
const SCREENS = ["compose", "params", "assets", "output"];
const ASSET_STAGES = ["groups", "preview", "items"];
const SCREEN_LABELS = {
compose: "Prompt",
params: "Tune",
assets: "Assets",
output: "Output"
};
const ASSET_STAGE_LABELS = {
groups: "Groups",
preview: "Preview",
items: "Items"
};
const state = {
active: false,
tab: "txt2img",
screen: "compose",
assetStage: "groups",
drawerOpen: false,
selectedGroup: "",
allowCardCommit: false,
cart: new Set()
};
let mobileMedia = null;
let shell = null;
let cardListenerRoot = null;
let groupListenerRoot = null;
let updateTimer = null;
let lastTaggedCount = 0;
function app() {
return gradioApp();
}
function qsa(selector, root) {
return Array.from((root || app()).querySelectorAll(selector));
}
function byId(id) {
return app().getElementById(id);
}
function currentTopTabContent() {
return app().querySelector('#tabs > .tabitem[id^=tab_]:not([style*="display: none"])');
}
function activeGenerationTab() {
const content = currentTopTabContent();
if (content && content.id === "tab_img2img") {
return "img2img";
}
if (content && content.id === "tab_txt2img") {
return "txt2img";
}
return state.tab;
}
function currentTabRoot() {
return byId("tab_" + state.tab) || app();
}
function button(label, action, variant) {
const el = document.createElement("button");
el.type = "button";
el.textContent = label;
el.dataset.mobileAction = action;
if (variant) {
el.classList.add(variant);
}
return el;
}
function setButtonState(el, selected) {
el.classList.toggle("selected", selected);
el.setAttribute("aria-pressed", selected ? "true" : "false");
}
function ensureShell() {
if (shell && shell.isConnected) {
return shell;
}
shell = document.createElement("div");
shell.id = "mobile_screen_shell";
shell.setAttribute("aria-hidden", "true");
shell.innerHTML = [
'<div class="mobile-screen-drawer" hidden></div>',
'<div class="mobile-screen-context">',
' <div class="mobile-screen-title"></div>',
' <div class="mobile-screen-actions"></div>',
'</div>',
'<button class="mobile-screen-primary" type="button"></button>',
'<nav class="mobile-screen-nav" aria-label="Mobile screens"></nav>'
].join("");
const nav = shell.querySelector(".mobile-screen-nav");
nav.appendChild(button("Prompt", "screen:compose"));
nav.appendChild(button("Tune", "screen:params"));
nav.appendChild(button("Assets", "screen:assets"));
nav.appendChild(button("Output", "screen:output"));
nav.appendChild(button("Mode", "mode"));
shell.addEventListener("click", onShellClick);
document.body.appendChild(shell);
return shell;
}
function mobileEnabled() {
return mobileMedia && mobileMedia.matches;
}
function setActive(active) {
state.active = active;
document.body.classList.toggle("mobile-screen-mode", active);
if (shell) {
shell.setAttribute("aria-hidden", active ? "false" : "true");
}
if (!active) {
document.body.removeAttribute("data-mobile-screen");
document.body.removeAttribute("data-mobile-asset-stage");
clearCart();
return;
}
state.tab = activeGenerationTab();
ensureScreenTab();
syncMobileMode();
}
function scheduleSync() {
clearTimeout(updateTimer);
updateTimer = setTimeout(syncMobileMode, 80);
}
function tagPanels() {
let taggedCount = 0;
GENERATION_TABS.forEach(function(tab) {
const topRow = byId(tab + "_toprow");
const promptContainer = byId(tab + "_prompt_container");
const settings = byId(tab + "_settings");
const results = byId(tab + "_results");
const imageMode = tab === "img2img" ? byId("mode_img2img") : null;
const inpaintControls = tab === "img2img" ? byId("inpaint_controls") : null;
if (topRow) {
topRow.dataset.mobilePanel = "compose";
taggedCount += 1;
}
if (promptContainer) {
promptContainer.dataset.mobilePanel = "compose";
taggedCount += 1;
}
if (settings) {
settings.dataset.mobilePanelRoot = "settings";
Array.from(settings.children).forEach(function(child) {
if (child === promptContainer || child.querySelector("#" + tab + "_prompt_container")) {
child.dataset.mobilePanel = "compose";
} else if (child === imageMode || (imageMode && child.contains(imageMode))) {
child.dataset.mobilePanel = "params image";
} else {
child.dataset.mobilePanel = "params";
}
taggedCount += 1;
});
}
if (imageMode) {
imageMode.dataset.mobilePanel = "params image";
taggedCount += 1;
}
if (inpaintControls) {
inpaintControls.dataset.mobilePanel = "params image";
taggedCount += 1;
}
if (results) {
results.dataset.mobilePanel = "output";
taggedCount += 1;
}
qsa("#" + tab + "_extra_tabs > .tabitem.extra-page").forEach(function(page) {
page.dataset.mobilePanel = "assets";
taggedCount += 1;
});
});
lastTaggedCount = taggedCount;
}
function setPanelVisibility() {
qsa("[data-mobile-panel]").forEach(function(el) {
const panels = (el.dataset.mobilePanel || "").split(/\s+/);
let active = panels.indexOf(state.screen) !== -1;
if (active && state.screen === "assets" && el.classList.contains("extra-page")) {
active = el.style.display !== "none";
}
el.classList.toggle("mobile-panel-active", active);
});
}
function topTabButtons() {
return qsa("#tabs > .tab-nav > button");
}
function clickTopTab(tab) {
const buttonById = byId("tab_" + tab + "-button");
if (buttonById) {
buttonById.click();
return;
}
const tabs = topTabButtons();
const index = tab === "img2img" ? 1 : 0;
if (tabs[index]) {
tabs[index].click();
}
}
function extraTabButtons(tab) {
return qsa("#" + tab + "_extra_tabs > .tab-nav > button");
}
function extraSelectedIndex(tab) {
const buttons = extraTabButtons(tab);
for (let i = 0; i < buttons.length; i += 1) {
if (buttons[i].classList.contains("selected") || buttons[i].getAttribute("aria-selected") === "true") {
return i;
}
}
return 0;
}
function ensureScreenTab() {
const topTab = activeGenerationTab();
if (topTab !== state.tab) {
state.tab = topTab;
}
if (state.screen === "assets") {
const buttons = extraTabButtons(state.tab);
const selected = extraSelectedIndex(state.tab);
if (buttons.length > 1 && selected === 0) {
buttons[1].click();
}
return;
}
const generation = extraTabButtons(state.tab)[0];
if (generation && extraSelectedIndex(state.tab) !== 0) {
generation.click();
}
}
function setScreen(screen) {
if (SCREENS.indexOf(screen) === -1) {
return;
}
state.screen = screen;
if (screen === "assets" && ASSET_STAGES.indexOf(state.assetStage) === -1) {
state.assetStage = "groups";
}
ensureScreenTab();
syncMobileMode();
}
function setAssetStage(stage) {
if (ASSET_STAGES.indexOf(stage) === -1) {
return;
}
state.screen = "assets";
state.assetStage = stage;
ensureScreenTab();
syncMobileMode();
}
function goRelative(delta) {
if (state.screen === "assets") {
const stageIndex = ASSET_STAGES.indexOf(state.assetStage);
const nextStage = ASSET_STAGES[stageIndex + delta];
if (nextStage) {
setAssetStage(nextStage);
return;
}
}
const index = SCREENS.indexOf(state.screen);
const next = SCREENS[index + delta];
if (next) {
setScreen(next);
}
}
function clearCart() {
state.cart.forEach(function(card) {
card.classList.remove("mobile-cart-selected");
card.removeAttribute("aria-pressed");
});
state.cart.clear();
}
function cleanCart() {
Array.from(state.cart).forEach(function(card) {
if (!card.isConnected || card.classList.contains("hidden")) {
card.classList.remove("mobile-cart-selected");
state.cart.delete(card);
}
});
}
function toggleCard(card) {
if (state.cart.has(card)) {
state.cart.delete(card);
card.classList.remove("mobile-cart-selected");
card.setAttribute("aria-pressed", "false");
} else {
state.cart.add(card);
card.classList.add("mobile-cart-selected");
card.setAttribute("aria-pressed", "true");
}
syncMobileMode();
}
function commitCart() {
cleanCart();
if (!state.cart.size) {
return;
}
const cards = Array.from(state.cart);
state.allowCardCommit = true;
cards.forEach(function(card) {
if (card.isConnected) {
card.click();
}
});
state.allowCardCommit = false;
clearCart();
setScreen("compose");
}
function onCardClick(event) {
if (!state.active || state.screen !== "assets" || state.assetStage !== "items" || state.allowCardCommit) {
return;
}
const card = event.target.closest(".extra-network-pane .card");
if (!card) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
toggleCard(card);
}
function attachCardListener() {
const root = app();
if (cardListenerRoot === root) {
return;
}
if (cardListenerRoot) {
cardListenerRoot.removeEventListener("click", onCardClick, true);
}
cardListenerRoot = root;
cardListenerRoot.addEventListener("click", onCardClick, true);
}
function groupLabelFromTarget(target) {
const label = target.querySelector(".tree-list-item-label");
if (label) {
return label.textContent.trim();
}
return target.textContent.trim();
}
function onGroupClick(event) {
if (!state.active || state.screen !== "assets") {
return;
}
const target = event.target.closest(".extra-network-tree .tree-list-content, .extra-network-dirs button, #" + state.tab + "_extra_tabs > .tab-nav > button");
if (!target) {
return;
}
state.selectedGroup = groupLabelFromTarget(target);
if (state.assetStage === "groups") {
setTimeout(function() {
setAssetStage("preview");
}, 0);
} else {
scheduleSync();
}
}
function attachGroupListener() {
const root = app();
if (groupListenerRoot === root) {
return;
}
if (groupListenerRoot) {
groupListenerRoot.removeEventListener("click", onGroupClick, true);
}
groupListenerRoot = root;
groupListenerRoot.addEventListener("click", onGroupClick, true);
}
function visibleCard(card) {
return !card.classList.contains("hidden");
}
function updatePreviewCards() {
qsa(".extra-network-pane .card").forEach(function(card) {
card.classList.remove("mobile-preview-card");
});
if (state.screen !== "assets" || state.assetStage !== "preview") {
return;
}
const cards = qsa("#" + state.tab + "_extra_tabs .extra-network-pane .card").filter(visibleCard);
cards.slice(0, 8).forEach(function(card) {
card.classList.add("mobile-preview-card");
});
}
function clickScoped(selector) {
const root = currentTabRoot();
const target = root.querySelector(selector);
if (target) {
target.click();
return true;
}
return false;
}
function inputScoped(selector) {
return currentTabRoot().querySelector(selector);
}
function updateInputIfAvailable(input) {
if (input && typeof updateInput === "function") {
updateInput(input);
}
}
function addPromptGroup() {
const textarea = inputScoped("#" + state.tab + "_prompt textarea");
if (!textarea) {
return;
}
const start = textarea.selectionStart || textarea.value.length;
const end = textarea.selectionEnd || textarea.value.length;
const selected = textarea.value.slice(start, end);
const insert = selected ? "(" + selected + ")" : (textarea.value ? ", ()" : "()");
const cursor = selected ? start + insert.length : start + insert.length - 1;
textarea.value = textarea.value.slice(0, start) + insert + textarea.value.slice(end);
textarea.focus();
textarea.setSelectionRange(cursor, cursor);
updateInputIfAvailable(textarea);
}
function clickGenerateLike() {
const interrupt = byId(state.tab + "_interrupt");
const interrupting = byId(state.tab + "_interrupting");
const generate = byId(state.tab + "_generate");
if (interrupting && interrupting.style.display === "block") {
interrupting.click();
return;
}
if (interrupt && interrupt.style.display === "block") {
interrupt.click();
return;
}
if (generate) {
generate.click();
}
}
function clickSkip() {
const skip = byId(state.tab + "_skip");
if (skip) {
skip.click();
}
}
function cycleMode() {
const next = state.tab === "txt2img" ? "img2img" : "txt2img";
state.tab = next;
clickTopTab(next);
setTimeout(function() {
state.tab = next;
ensureScreenTab();
scheduleSync();
}, 0);
}
function toggleDrawer() {
state.drawerOpen = !state.drawerOpen;
syncShell();
}
function cycleExtraType() {
const buttons = extraTabButtons(state.tab);
if (buttons.length <= 2) {
return;
}
const selected = extraSelectedIndex(state.tab);
const next = selected + 1 >= buttons.length ? 1 : selected + 1;
buttons[next].click();
state.selectedGroup = buttons[next].textContent.trim();
setAssetStage("groups");
}
function refreshAssets() {
const selected = extraSelectedIndex(state.tab);
const buttons = extraTabButtons(state.tab);
const selectedButton = buttons[selected];
if (selectedButton && selectedButton.id) {
const pageId = selectedButton.id.replace(/-button$/, "");
const refresh = byId(pageId + "_extra_refresh");
if (refresh) {
refresh.click();
}
}
}
function focusAssetSearch() {
const selected = extraSelectedIndex(state.tab);
const buttons = extraTabButtons(state.tab);
const selectedButton = buttons[selected];
if (selectedButton && selectedButton.id) {
const pageId = selectedButton.id.replace(/-button$/, "");
const search = byId(pageId + "_extra_search");
if (search) {
search.focus();
}
}
}
function onShellClick(event) {
const action = event.target.dataset.mobileAction;
if (!action) {
return;
}
if (action.indexOf("screen:") === 0) {
setScreen(action.slice("screen:".length));
return;
}
if (action.indexOf("asset:") === 0) {
setAssetStage(action.slice("asset:".length));
return;
}
const actions = {
back: function() {
goRelative(-1);
},
next: function() {
goRelative(1);
},
generate: clickGenerateLike,
skip: clickSkip,
mode: toggleDrawer,
switchMode: cycleMode,
paste: function() {
clickScoped("#paste");
},
clear: function() {
clickScoped("#" + state.tab + "_clear_prompt");
},
styles: function() {
clickScoped("#" + state.tab + "_style_apply");
},
groupPrompt: addPromptGroup,
swapSize: function() {
clickScoped("#" + state.tab + "_res_switch_btn");
},
detectSize: function() {
clickScoped("#img2img_detect_image_size_btn");
},
type: cycleExtraType,
refresh: refreshAssets,
focusSearch: focusAssetSearch,
clearCart: function() {
clearCart();
syncMobileMode();
},
addCart: commitCart,
restore: function() {
clickScoped("#" + state.tab + "_restore_progress");
},
upscale: function() {
clickScoped("#txt2img_upscale");
},
save: function() {
clickScoped("#save_" + state.tab);
},
openFolder: function() {
clickScoped("#" + state.tab + "_open_folder");
}
};
if (actions[action]) {
actions[action]();
}
}
function addAction(container, label, action, variant) {
container.appendChild(button(label, action, variant));
}
function renderActions(container) {
container.innerHTML = "";
addAction(container, "Back", "back");
if (state.screen === "compose") {
addAction(container, "Paste", "paste");
addAction(container, "Clear", "clear");
addAction(container, "Styles", "styles");
addAction(container, "Group", "groupPrompt");
addAction(container, "Next", "next", "primary");
return;
}
if (state.screen === "params") {
addAction(container, "Swap", "swapSize");
if (state.tab === "img2img") {
addAction(container, "Detect", "detectSize");
}
addAction(container, "Next", "next", "primary");
return;
}
if (state.screen === "assets") {
addAction(container, "Type", "type");
addAction(container, "Groups", "asset:groups", state.assetStage === "groups" ? "primary" : "");
addAction(container, "Preview", "asset:preview", state.assetStage === "preview" ? "primary" : "");
addAction(container, "Items", "asset:items", state.assetStage === "items" ? "primary" : "");
if (state.assetStage === "groups") {
addAction(container, "Search", "focusSearch");
addAction(container, "Refresh", "refresh");
} else {
addAction(container, "Clear", "clearCart");
}
return;
}
if (state.screen === "output") {
addAction(container, "Restore", "restore");
addAction(container, "Save", "save");
addAction(container, "Folder", "openFolder");
if (state.tab === "txt2img") {
addAction(container, "Upscale", "upscale");
}
}
}
function updatePrimaryButton(primary) {
const interrupt = byId(state.tab + "_interrupt");
const interrupting = byId(state.tab + "_interrupting");
const generating = (interrupt && interrupt.style.display === "block") || (interrupting && interrupting.style.display === "block");
primary.dataset.mobileAction = "generate";
primary.classList.toggle("generating", generating);
if (state.screen === "assets" && state.assetStage === "items") {
cleanCart();
primary.textContent = state.cart.size ? "Add " + state.cart.size : (generating ? "Stop" : "Generate");
primary.dataset.mobileAction = state.cart.size ? "addCart" : "generate";
primary.disabled = false;
return;
}
primary.disabled = false;
primary.textContent = generating ? "Stop" : "Generate";
}
function renderDrawer(drawer) {
drawer.hidden = !state.drawerOpen;
drawer.innerHTML = "";
topTabButtons().forEach(function(tabButton) {
const item = button(tabButton.textContent.trim(), "drawer-tab", "");
item.addEventListener("click", function() {
tabButton.click();
state.drawerOpen = false;
scheduleSync();
});
drawer.appendChild(item);
});
}
function syncShell() {
const currentShell = ensureShell();
const navButtons = qsa(".mobile-screen-nav button", currentShell);
const actions = currentShell.querySelector(".mobile-screen-actions");
const title = currentShell.querySelector(".mobile-screen-title");
const primary = currentShell.querySelector(".mobile-screen-primary");
const drawer = currentShell.querySelector(".mobile-screen-drawer");
navButtons.forEach(function(navButton) {
const action = navButton.dataset.mobileAction || "";
setButtonState(navButton, action === "screen:" + state.screen || (action === "mode" && state.drawerOpen));
});
title.textContent = [
state.tab === "txt2img" ? "Txt2img" : "Img2img",
SCREEN_LABELS[state.screen],
state.screen === "assets" ? ASSET_STAGE_LABELS[state.assetStage] : "",
state.screen === "assets" && state.selectedGroup ? state.selectedGroup : ""
].filter(Boolean).join(" / ");
renderActions(actions);
updatePrimaryButton(primary);
renderDrawer(drawer);
}
function syncMobileMode() {
if (!state.active) {
return;
}
const taggedBefore = lastTaggedCount;
tagPanels();
if (taggedBefore !== lastTaggedCount) {
attachCardListener();
attachGroupListener();
}
state.tab = activeGenerationTab();
document.body.dataset.mobileScreen = state.screen;
document.body.dataset.mobileAssetStage = state.assetStage;
ensureScreenTab();
setPanelVisibility();
updatePreviewCards();
syncShell();
}
function setup() {
mobileMedia = window.matchMedia(MOBILE_QUERY);
ensureShell();
attachCardListener();
attachGroupListener();
const onChange = function() {
setActive(mobileEnabled());
};
if (mobileMedia.addEventListener) {
mobileMedia.addEventListener("change", onChange);
} else {
mobileMedia.addListener(onChange);
}
onChange();
}
onUiLoaded(setup);
onAfterUiUpdate(function() {
if (state.active) {
scheduleSync();
}
});
})();

View File

@ -25,15 +25,10 @@
if (!parent.needHideOnMoblie) {
return true;
}
if (window.innerWidth < GRADIO_MIN_WIDTH * 2 + PAD * 4) {
parent.style.display = 'flex';
parent.resizeHandle.style.display = "none";
return false;
} else {
parent.style.display = 'grid';
parent.resizeHandle.style.display = "block";
return true;
}
parent.style.display = 'grid';
parent.resizeHandle.style.display = "block";
return true;
}
function afterResize(parent) {

386
style.css
View File

@ -1700,6 +1700,386 @@ body.resizing .resize-handle {
border-color: #6f6f6f;
}
.forge_space_btn{
min-width: 0 !important;
}
.forge_space_btn{
min-width: 0 !important;
}
/* smartphone screen mode */
#mobile_screen_shell {
display: none;
}
@media (max-width: 760px), (pointer: coarse) and (max-width: 900px) {
body.mobile-screen-mode {
padding-bottom: calc(10rem + env(safe-area-inset-bottom, 0px));
overscroll-behavior-y: contain;
}
body.mobile-screen-mode .main {
padding: 0.5rem 0.5rem calc(10rem + env(safe-area-inset-bottom, 0px)) 0.5rem !important;
}
body.mobile-screen-mode div.gradio-container,
body.mobile-screen-mode .gradio-container .gradio-row,
body.mobile-screen-mode .gradio-container .gradio-column,
body.mobile-screen-mode div.gradio-group {
min-width: 0 !important;
max-width: 100% !important;
}
body.mobile-screen-mode #quicksettings,
body.mobile-screen-mode #tabs > .tab-nav,
body.mobile-screen-mode #footer,
body.mobile-screen-mode #txt2img_generate_box,
body.mobile-screen-mode #img2img_generate_box {
display: none !important;
}
body.mobile-screen-mode #mobile_screen_shell {
display: block;
position: fixed;
inset: auto 0 0 0;
z-index: 1000;
color: var(--body-text-color);
pointer-events: none;
}
body.mobile-screen-mode #mobile_screen_shell * {
box-sizing: border-box;
}
body.mobile-screen-mode .mobile-screen-context {
position: fixed;
right: 5.75rem;
bottom: calc(4.75rem + env(safe-area-inset-bottom, 0px));
left: 0.5rem;
display: flex;
min-height: 4.25rem;
flex-direction: column;
gap: 0.35rem;
padding: 0.5rem;
border: 1px solid var(--block-border-color);
border-radius: 8px;
background: var(--background-fill-primary);
box-shadow: 0 0.4rem 1.1rem rgba(0, 0, 0, 0.22);
pointer-events: auto;
}
body.mobile-screen-mode .mobile-screen-title {
overflow: hidden;
color: var(--block-title-text-color);
font-size: 0.78rem;
font-weight: 700;
line-height: 1.1rem;
text-overflow: ellipsis;
white-space: nowrap;
}
body.mobile-screen-mode .mobile-screen-actions {
display: flex;
gap: 0.35rem;
overflow-x: auto;
scrollbar-width: none;
}
body.mobile-screen-mode .mobile-screen-actions::-webkit-scrollbar,
body.mobile-screen-mode .mobile-screen-nav::-webkit-scrollbar {
display: none;
}
body.mobile-screen-mode .mobile-screen-actions button,
body.mobile-screen-mode .mobile-screen-nav button,
body.mobile-screen-mode .mobile-screen-drawer button {
min-width: 0 !important;
min-height: 2.25rem;
border: 1px solid var(--button-secondary-border-color);
border-radius: 8px;
background: var(--button-secondary-background-fill);
color: var(--button-secondary-text-color);
font-size: 0.82rem;
font-weight: 700;
line-height: 1rem;
white-space: nowrap;
}
body.mobile-screen-mode .mobile-screen-actions button {
flex: 0 0 auto;
padding: 0.4rem 0.7rem;
}
body.mobile-screen-mode .mobile-screen-actions button.primary,
body.mobile-screen-mode .mobile-screen-nav button.selected {
border-color: var(--button-primary-border-color);
background: var(--button-primary-background-fill);
color: var(--button-primary-text-color);
}
body.mobile-screen-mode .mobile-screen-primary {
position: fixed;
right: 0.75rem;
bottom: calc(4.9rem + env(safe-area-inset-bottom, 0px));
width: 4.45rem;
height: 4.45rem;
border: 1px solid var(--button-primary-border-color);
border-radius: 50%;
background: var(--button-primary-background-fill);
color: var(--button-primary-text-color);
box-shadow: 0 0.45rem 1.2rem rgba(0, 0, 0, 0.28);
font-size: 0.88rem;
font-weight: 800;
line-height: 1rem;
pointer-events: auto;
}
body.mobile-screen-mode .mobile-screen-primary.generating {
background: #a83d3d;
color: white;
}
body.mobile-screen-mode .mobile-screen-primary:disabled {
opacity: 0.55;
}
body.mobile-screen-mode .mobile-screen-nav {
position: fixed;
right: 0;
bottom: 0;
left: 0;
display: grid;
height: calc(4.25rem + env(safe-area-inset-bottom, 0px));
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 0.35rem;
padding: 0.4rem 0.5rem calc(0.45rem + env(safe-area-inset-bottom, 0px)) 0.5rem;
border-top: 1px solid var(--block-border-color);
background: var(--background-fill-primary);
box-shadow: 0 -0.35rem 1rem rgba(0, 0, 0, 0.18);
pointer-events: auto;
}
body.mobile-screen-mode .mobile-screen-nav button {
width: 100%;
padding: 0.25rem;
overflow: hidden;
text-overflow: ellipsis;
}
body.mobile-screen-mode .mobile-screen-drawer {
position: fixed;
right: 0.5rem;
bottom: calc(9.5rem + env(safe-area-inset-bottom, 0px));
left: 0.5rem;
display: grid;
max-height: 45vh;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.45rem;
overflow: auto;
padding: 0.55rem;
border: 1px solid var(--block-border-color);
border-radius: 8px;
background: var(--background-fill-primary);
box-shadow: 0 0.4rem 1.1rem rgba(0, 0, 0, 0.24);
pointer-events: auto;
}
body.mobile-screen-mode .mobile-screen-drawer[hidden] {
display: none;
}
body.mobile-screen-mode [data-mobile-panel]:not(.mobile-panel-active) {
display: none !important;
}
body.mobile-screen-mode [data-mobile-panel].mobile-panel-active {
display: flex !important;
width: 100% !important;
min-width: 0 !important;
max-width: 100% !important;
flex-direction: column;
}
body.mobile-screen-mode [data-mobile-panel-root="settings"] {
display: flex;
width: 100%;
flex-direction: column;
gap: 0.75rem;
}
body.mobile-screen-mode .resize-handle-row {
display: block !important;
grid-template-columns: none !important;
}
body.mobile-screen-mode .resize-handle {
display: none !important;
}
body.mobile-screen-mode #txt2img_settings,
body.mobile-screen-mode #img2img_settings,
body.mobile-screen-mode #txt2img_results,
body.mobile-screen-mode #img2img_results,
body.mobile-screen-mode #extras_results {
position: static !important;
top: auto !important;
width: 100%;
}
body.mobile-screen-mode .gradio-container .gradio-row {
flex-wrap: wrap !important;
}
body.mobile-screen-mode .prompt textarea {
min-height: 9.5rem !important;
font-size: 16px !important;
line-height: 1.35rem !important;
}
body.mobile-screen-mode .block.token-counter {
top: auto;
right: 0.5rem;
bottom: 0.35rem;
}
body.mobile-screen-mode:not([data-mobile-screen="assets"]) #txt2img_extra_tabs > .tab-nav,
body.mobile-screen-mode:not([data-mobile-screen="assets"]) #img2img_extra_tabs > .tab-nav {
display: none !important;
}
body.mobile-screen-mode[data-mobile-screen="assets"] #txt2img_extra_tabs > .tab-nav,
body.mobile-screen-mode[data-mobile-screen="assets"] #img2img_extra_tabs > .tab-nav {
position: sticky;
top: 0;
z-index: 50;
display: flex;
gap: 0.35rem;
overflow-x: auto;
padding: 0.35rem 0;
background: var(--background-fill-primary);
}
body.mobile-screen-mode[data-mobile-screen="assets"] #txt2img_extra_tabs > .tab-nav button,
body.mobile-screen-mode[data-mobile-screen="assets"] #img2img_extra_tabs > .tab-nav button {
flex: 0 0 auto;
border-radius: 8px;
white-space: nowrap;
}
body.mobile-screen-mode .extra-networks-controls-div {
width: 100%;
margin-left: 0;
}
body.mobile-screen-mode .extra-network-control {
flex-wrap: wrap;
gap: 0.35rem;
align-items: center;
padding: 0.35rem !important;
border: 1px solid var(--block-border-color);
border-radius: 8px;
background: var(--background-fill-secondary);
}
body.mobile-screen-mode .extra-network-control .extra-network-control--search {
flex: 1 1 100%;
min-width: 0;
}
body.mobile-screen-mode .extra-network-pane {
height: auto;
min-height: 0;
resize: none;
overflow: visible;
}
body.mobile-screen-mode .extra-network-pane .extra-network-pane-content-tree,
body.mobile-screen-mode .extra-network-pane .extra-network-pane-content-dirs {
display: block !important;
overflow: visible;
}
body.mobile-screen-mode[data-mobile-asset-stage="groups"] .extra-network-pane .extra-network-cards {
display: none !important;
}
body.mobile-screen-mode[data-mobile-asset-stage="groups"] .extra-network-pane .extra-network-tree {
display: block !important;
max-height: calc(100dvh - 14.5rem);
overflow: auto !important;
border-radius: 8px;
}
body.mobile-screen-mode[data-mobile-asset-stage="groups"] .extra-network-pane .extra-network-dirs {
display: flex !important;
flex-wrap: wrap;
gap: 0.35rem;
padding: 0.35rem 0;
}
body.mobile-screen-mode[data-mobile-asset-stage="preview"] .extra-network-pane .extra-network-tree,
body.mobile-screen-mode[data-mobile-asset-stage="preview"] .extra-network-pane .extra-network-dirs,
body.mobile-screen-mode[data-mobile-asset-stage="items"] .extra-network-pane .extra-network-tree,
body.mobile-screen-mode[data-mobile-asset-stage="items"] .extra-network-pane .extra-network-dirs {
display: none !important;
}
body.mobile-screen-mode[data-mobile-asset-stage="preview"] .extra-network-pane .extra-network-cards,
body.mobile-screen-mode[data-mobile-asset-stage="items"] .extra-network-pane .extra-network-cards {
display: grid !important;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5rem;
overflow: visible !important;
border: 0;
}
body.mobile-screen-mode[data-mobile-asset-stage="preview"] .extra-network-pane .card:not(.mobile-preview-card) {
display: none !important;
}
body.mobile-screen-mode .extra-network-pane .card,
body.mobile-screen-mode .standalone-card-preview.card {
width: 100% !important;
height: auto !important;
min-height: 12.5rem;
margin: 0 !important;
border-radius: 8px;
}
body.mobile-screen-mode .extra-network-pane .card .actions .name {
font-size: 1rem;
line-height: 1.15rem;
}
body.mobile-screen-mode .extra-network-pane .card .actions .description {
max-height: 2.4rem;
font-size: 0.78rem;
}
body.mobile-screen-mode .extra-network-pane .card.mobile-cart-selected {
box-shadow: 0 0 0 0.22rem var(--button-primary-background-fill), 0 0.25rem 0.8rem rgba(0, 0, 0, 0.25);
}
body.mobile-screen-mode .extra-network-pane .card.mobile-cart-selected::after {
content: "Selected";
position: absolute;
top: 0.45rem;
right: 0.45rem;
padding: 0.2rem 0.4rem;
border-radius: 8px;
background: var(--button-primary-background-fill);
color: var(--button-primary-text-color);
font-size: 0.72rem;
font-weight: 800;
}
body.mobile-screen-mode #txt2img_gallery,
body.mobile-screen-mode #img2img_gallery {
min-height: calc(100dvh - 15rem);
}
body.mobile-screen-mode #image_buttons_txt2img,
body.mobile-screen-mode #image_buttons_img2img {
justify-content: flex-start;
overflow-x: auto;
padding-bottom: 0.25rem;
}
}