mirror of
https://github.com/lllyasviel/stable-diffusion-webui-forge.git
synced 2026-06-04 21:05:48 +08:00
Add smartphone screen mode
This commit is contained in:
parent
dfdcbab685
commit
ceae463c02
820
javascript/mobileMode.js
Normal file
820
javascript/mobileMode.js
Normal 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();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@ -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
386
style.css
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user