Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a settings link to the footer with i18n options & pwa instructions #10254

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/orange-bugs-send.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@gradio/client": minor
"@gradio/core": minor
"gradio": minor
---

feat:Add a `settings` link to the footer with i18n options & pwa instructions
1 change: 1 addition & 0 deletions client/js/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export interface Config {
api_prefix?: string;
fill_height?: boolean;
fill_width?: boolean;
pwa?: boolean;
}

// todo: DRY up types
Expand Down
6 changes: 4 additions & 2 deletions gradio/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ def __init__(
self.renderables: list[Renderable] = []
self.state_holder: StateHolder
self.custom_mount_path: str | None = None
self.pwa = False

# For analytics_enabled and allow_flagging: (1) first check for
# parameter, (2) check for env variable, (3) default to True/"manual"
Expand Down Expand Up @@ -2171,6 +2172,7 @@ def get_config_file(self) -> BlocksConfigDict:
"fill_height": self.fill_height,
"fill_width": self.fill_width,
"theme_hash": self.theme_hash,
"pwa": self.pwa,
}
config.update(self.default_config.get_config()) # type: ignore
config["connect_heartbeat"] = utils.connect_heartbeat(
Expand Down Expand Up @@ -2450,9 +2452,10 @@ def reverse(text):
if block.key is None:
block.key = f"__{block._id}__"

self.config = self.get_config_file()
self.pwa = utils.get_space() is not None if pwa is None else pwa
self.max_threads = max_threads
self._queue.max_thread_count = max_threads
self.config = self.get_config_file()

self.ssr_mode = (
False
Expand Down Expand Up @@ -2532,7 +2535,6 @@ def reverse(text):
"http" if share_server_address is not None else "https"
)
self.has_launched = True
self.pwa = utils.get_space() is not None if pwa is None else pwa

self.protocol = (
"https"
Expand Down
1 change: 1 addition & 0 deletions gradio/data_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ class BlocksConfigDict(TypedDict):
root: NotRequired[str | None]
username: NotRequired[str | None]
api_prefix: str
pwa: NotRequired[bool]


class MediaStreamChunk(TypedDict):
Expand Down
63 changes: 60 additions & 3 deletions js/core/src/Blocks.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import type { ComponentMeta, Dependency, LayoutNode } from "./types";
import type { UpdateTransaction } from "./init";
import { setupi18n } from "./i18n";
import { ApiDocs, ApiRecorder } from "./api_docs/";
import { ApiDocs, ApiRecorder, Settings } from "./api_docs/";
import type { ThemeMode, Payload } from "./types";
import { Toast } from "@gradio/statustracker";
import type { ToastMessage } from "@gradio/statustracker";
Expand All @@ -18,6 +18,7 @@
import logo from "./images/logo.svg";
import api_logo from "./api_docs/img/api-logo.svg";
import settings_logo from "./api_docs/img/settings-logo.svg";
import { create_components, AsyncFunction } from "./init";
import type {
LogMessage,
Expand Down Expand Up @@ -85,8 +86,10 @@
export let search_params: URLSearchParams;
let api_docs_visible = search_params.get("view") === "api" && show_api;
let settings_visible = search_params.get("view") === "settings";
let api_recorder_visible =
search_params.get("view") === "api-recorder" && show_api;
function set_api_docs_visible(visible: boolean): void {
api_recorder_visible = false;
api_docs_visible = visible;
Expand All @@ -98,6 +101,18 @@
}
history.replaceState(null, "", "?" + params.toString());
}
function set_settings_visible(visible: boolean): void {
let params = new URLSearchParams(window.location.search);
if (visible) {
params.set("view", "settings");
} else {
params.delete("view");
}
history.replaceState(null, "", "?" + params.toString());
settings_visible = !settings_visible;
}
let api_calls: Payload[] = [];
export let render_complete = false;
Expand Down Expand Up @@ -770,6 +785,16 @@
{$_("common.built_with_gradio")}
<img src={logo} alt={$_("common.logo")} />
</a>
<div>·</div>
<button
on:click={() => {
set_settings_visible(!settings_visible);
}}
class="settings"
>
{$_("common.settings")}
<img src={settings_logo} alt={$_("common.settings")} />
</button>
</footer>
{/if}
</div>
Expand Down Expand Up @@ -819,6 +844,30 @@
</div>
{/if}

{#if settings_visible && $_layout && app.config}
<div class="api-docs">
<!-- TODO: fix -->
<!-- svelte-ignore a11y-click-events-have-key-events-->
<!-- svelte-ignore a11y-no-static-element-interactions-->
<div
class="backdrop"
on:click={() => {
set_settings_visible(false);
}}
/>
<div class="api-docs-wrap">
<Settings
on:close={(event) => {
set_settings_visible(false);
}}
pwa_enabled={app.config.pwa}
{root}
{space_id}
/>
</div>
</div>
{/if}

{#if messages}
<Toast {messages} on:close={handle_error_close} />
{/if}
Expand Down Expand Up @@ -849,7 +898,8 @@
margin-left: var(--size-2);
}
.show-api {
.show-api,
.settings {
display: flex;
align-items: center;
}
Expand All @@ -863,12 +913,19 @@
width: var(--size-3);
}
.settings img {
margin-right: var(--size-1);
margin-left: var(--size-1);
width: var(--size-4);
}
.built-with {
display: flex;
align-items: center;
}
.built-with:hover {
.built-with:hover,
.settings:hover {
color: var(--body-text-color);
}
Expand Down
185 changes: 185 additions & 0 deletions js/core/src/api_docs/Settings.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<script lang="ts">
/* eslint-disable */
import { onMount } from "svelte";
import SettingsBanner from "./SettingsBanner.svelte";
export let root: string;
export let space_id: string | null;
export let pwa_enabled: boolean | undefined;
import { BaseDropdown as Dropdown } from "@gradio/dropdown";
import { language_choices, changeLocale } from "../i18n";
import { locale, _ } from "svelte-i18n";
import { setupi18n } from "../i18n";
if (root === "") {
root = location.protocol + "//" + location.host + location.pathname;
}
if (!root.endsWith("/")) {
root += "/";
}
function setTheme(theme: "light" | "dark" | "system") {
const url = new URL(window.location.href);
if (theme === "system") {
url.searchParams.delete("__theme");
current_theme = "system";
} else {
url.searchParams.set("__theme", theme);
current_theme = theme;
}
window.location.href = url.toString();
}
onMount(() => {
document.body.style.overflow = "hidden";
if ("parentIFrame" in window) {
window.parentIFrame?.scrollTo(0, 0);
}
const url = new URL(window.location.href);
const theme = url.searchParams.get("__theme");
current_theme = (theme as "light" | "dark" | "system") || "system";
return () => {
document.body.style.overflow = "auto";
};
});
let current_locale: string;
let current_theme: "light" | "dark" | "system" = "system";
locale.subscribe((value) => {
if (value) {
current_locale = value;
}
});
function handleLanguageChange(e: CustomEvent): void {
const new_locale = e.detail;
changeLocale(new_locale);
}
setupi18n();
</script>

<div class="banner-wrap">
<SettingsBanner on:close {root} />
</div>
{#if space_id === null}
<!-- on Spaces, the theme is set in HF settings -->
<div class="banner-wrap">
<h2>{$_("common.display_theme")}</h2>
<p class="padded theme-buttons">
<li
class="theme-button {current_theme === 'light'
? 'current-theme'
: 'inactive-theme'}"
on:click={() => setTheme("light")}
>
<button>☀︎ &nbsp;Light</button>
</li>
<li
class="theme-button {current_theme === 'dark'
? 'current-theme'
: 'inactive-theme'}"
on:click={() => setTheme("dark")}
>
<button>⏾ &nbsp; Dark</button>
</li>
<li
class="theme-button {current_theme === 'system'
? 'current-theme'
: 'inactive-theme'}"
on:click={() => setTheme("system")}
>
<button>🖥︎ &nbsp;System</button>
</li>
</p>
</div>
{/if}
<div class="banner-wrap">
<h2>{$_("common.language")}</h2>
<p class="padded">
Gradio automatically detects the language of your browser. You can also
choose a language manually:
</p>
<Dropdown
label="Language"
choices={language_choices}
show_label={false}
{root}
value={current_locale}
on:change={handleLanguageChange}
/>
</div>
<div class="banner-wrap">
<h2>{$_("common.pwa")}</h2>
<p class="padded">
{#if pwa_enabled}
You can install this app as a Progressive Web App on your device. Visit <a
href={root}>{root}</a
> and click the install button in the URL address bar of your browser.
{:else}
Progressive Web App is not enabled for this app. To enable it, start your
Gradio app with <code>launch(pwa=True)</code>.
{/if}
</p>
</div>

<style>
.banner-wrap {
position: relative;
border-bottom: 1px solid var(--border-color-primary);
padding: var(--size-4) var(--size-6);
font-size: var(--text-md);
}
.banner-wrap h2 {
font-size: var(--text-xl);
}
a {
text-decoration: underline;
}
p.padded {
padding: 15px 0px;
}
.theme-buttons {
display: flex;
align-items: center;
}
.theme-buttons > * + * {
margin-left: var(--size-2);
}
.theme-button {
display: flex;
align-items: center;
border: 1px solid var(--border-color-primary);
border-radius: var(--radius-md);
padding: var(--size-2) var(--size-2-5);
line-height: 1;
user-select: none;
text-transform: capitalize;
cursor: pointer;
}
.current-theme {
border: 1px solid var(--body-text-color-subdued);
color: var(--body-text-color);
}
.inactive-theme {
color: var(--body-text-color-subdued);
}
.inactive-theme:hover,
.inactive-theme:focus {
box-shadow: var(--shadow-drop);
color: var(--body-text-color);
}
.theme-button button {
all: unset;
cursor: pointer;
}
</style>
Loading
Loading