Brand the webmail INBUXA: name, mark and wordmark
INBUXA is a product suite and ihasmail an independent product, so INBUXA's webmail says INBUXA: the sign-in page, header, page title, installed-app name and About page. The wordmark is drawn in the current text color. ihasmail's version and AGPL source line stay as its credit. Two new strings, in all nine catalogues; three Stalwart-only ones are no longer used.
This commit is contained in:
+1
-1
@@ -23,7 +23,7 @@
|
||||
<link rel="icon" type="image/png" sizes="64x64" href="/img/favicon-64.png" />
|
||||
<link rel="apple-touch-icon" href="/img/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<title>ihasmail</title>
|
||||
<title>INBUXA</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ihasmail",
|
||||
"short_name": "ihasmail",
|
||||
"description": "Fast, friendly JMAP webmail for Stalwart",
|
||||
"name": "INBUXA",
|
||||
"short_name": "INBUXA",
|
||||
"description": "INBUXA webmail",
|
||||
"_comment": "JSON has no comments, so: every URL below is relative on purpose. Manifest members resolve against the manifest's own address, so these follow BASE_PATH with nothing substituted into them at build time. Root-absolute values pinned the installed app, its scope and its shortcuts to the domain root whatever the mount was.",
|
||||
"_comment_id": "There is deliberately no `id`. It is the one member NOT resolved against this file's address -- the spec resolves it against the origin of start_url, so `./`, `mail` and `/mail` all mean the same thing at the domain root and none of them can name a subpath mount. Adding one would therefore break the same thing the note above describes. Worse, the default id IS start_url, which is already mount-correct: writing an id now would give every installed copy a new identity and orphan it as a second app rather than updating it. If one is ever wanted it has to be substituted at build time from BASE_PATH, and the changeover costs everybody their install.",
|
||||
"start_url": "mail",
|
||||
|
||||
@@ -10,4 +10,6 @@
|
||||
* One constant rather than the string written out at each of them, because
|
||||
* three copies of a default is how two of them end up stale.
|
||||
*/
|
||||
export const DEFAULT_APP_NAME = "ihasmail";
|
||||
// ihasmail-inbuxa: INBUXA's webmail goes by INBUXA, so it can't be taken for
|
||||
// public ihasmail. APP_NAME still names a deployment whatever it likes.
|
||||
export const DEFAULT_APP_NAME = "INBUXA";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { withBase } from "../basePath";
|
||||
|
||||
let baseTitle = "ihasmail";
|
||||
let baseTitle = "INBUXA";
|
||||
let faviconCanvas: HTMLCanvasElement | null = null;
|
||||
let baseFavicon: HTMLImageElement | null = null;
|
||||
|
||||
|
||||
@@ -1242,6 +1242,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "Filter konnten nicht gespeichert werden: {error}",
|
||||
"Could not send the receipt: {error}": "Die Lesebestätigung konnte nicht gesendet werden: {error}",
|
||||
"Could not sign in.": "Anmeldung fehlgeschlagen.",
|
||||
"About INBUXA webmail": "Über INBUXA Webmail",
|
||||
"Mail server": "Mailserver",
|
||||
"You'll enter your password on your mail server's sign-in page.": "Ihr Passwort geben Sie auf der Anmeldeseite Ihres Mailservers ein.",
|
||||
"You'll sign in on your mail server's own page.": "Sie melden sich auf der eigenen Seite Ihres Mailservers an.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Diese Anmeldung wurde nicht in diesem Browser begonnen. Bitte versuchen Sie es erneut.",
|
||||
|
||||
@@ -1215,6 +1215,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "No se pudieron guardar los filtros: {error}",
|
||||
"Could not send the receipt: {error}": "No se pudo enviar la confirmación de lectura: {error}",
|
||||
"Could not sign in.": "No se pudo iniciar sesión.",
|
||||
"About INBUXA webmail": "Acerca de INBUXA webmail",
|
||||
"Mail server": "Servidor de correo",
|
||||
"You'll enter your password on your mail server's sign-in page.": "Introducirá su contraseña en la página de inicio de sesión de su servidor de correo.",
|
||||
"You'll sign in on your mail server's own page.": "Iniciará sesión en la propia página de su servidor de correo.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Este inicio de sesión no empezó en este navegador. Inténtelo de nuevo.",
|
||||
|
||||
@@ -1220,6 +1220,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "Impossible d’enregistrer les filtres : {error}",
|
||||
"Could not send the receipt: {error}": "Impossible d’envoyer l’accusé de lecture : {error}",
|
||||
"Could not sign in.": "Connexion impossible.",
|
||||
"About INBUXA webmail": "À propos d’INBUXA webmail",
|
||||
"Mail server": "Serveur de messagerie",
|
||||
"You'll enter your password on your mail server's sign-in page.": "Vous saisirez votre mot de passe sur la page de connexion de votre serveur de messagerie.",
|
||||
"You'll sign in on your mail server's own page.": "Vous vous connecterez sur la page de votre serveur de messagerie.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Cette connexion n’a pas commencé dans ce navigateur. Réessayez.",
|
||||
|
||||
@@ -1223,6 +1223,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "フィルターを保存できませんでした: {error}",
|
||||
"Could not send the receipt: {error}": "開封確認を送信できませんでした: {error}",
|
||||
"Could not sign in.": "サインインできませんでした。",
|
||||
"About INBUXA webmail": "INBUXA ウェブメールについて",
|
||||
"Mail server": "メールサーバー",
|
||||
"You'll enter your password on your mail server's sign-in page.": "パスワードはメールサーバーのサインインページで入力します。",
|
||||
"You'll sign in on your mail server's own page.": "メールサーバー自身のページでサインインします。",
|
||||
"This sign-in didn't start in this browser. Try again.": "このサインインはこのブラウザーで開始されたものではありません。もう一度お試しください。",
|
||||
|
||||
@@ -1213,6 +1213,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "Filters opslaan mislukt: {error}",
|
||||
"Could not send the receipt: {error}": "De leesbevestiging kon niet worden verzonden: {error}",
|
||||
"Could not sign in.": "Aanmelden mislukt.",
|
||||
"About INBUXA webmail": "Over INBUXA webmail",
|
||||
"Mail server": "Mailserver",
|
||||
"You'll enter your password on your mail server's sign-in page.": "U voert uw wachtwoord in op de aanmeldpagina van uw mailserver.",
|
||||
"You'll sign in on your mail server's own page.": "U meldt zich aan op de eigen pagina van uw mailserver.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Deze aanmelding is niet in deze browser begonnen. Probeer het opnieuw.",
|
||||
|
||||
@@ -1218,6 +1218,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "Não foi possível salvar os filtros: {error}",
|
||||
"Could not send the receipt: {error}": "Não foi possível enviar a confirmação de leitura: {error}",
|
||||
"Could not sign in.": "Não foi possível entrar.",
|
||||
"About INBUXA webmail": "Sobre o INBUXA webmail",
|
||||
"Mail server": "Servidor de e-mail",
|
||||
"You'll enter your password on your mail server's sign-in page.": "Você vai digitar sua senha na página de entrada do seu servidor de e-mail.",
|
||||
"You'll sign in on your mail server's own page.": "Você vai entrar na própria página do seu servidor de e-mail.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Esta entrada não começou neste navegador. Tente de novo.",
|
||||
|
||||
@@ -1217,6 +1217,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "Не удалось сохранить фильтры: {error}",
|
||||
"Could not send the receipt: {error}": "Не удалось отправить уведомление о прочтении: {error}",
|
||||
"Could not sign in.": "Не удалось войти.",
|
||||
"About INBUXA webmail": "О веб-почте INBUXA",
|
||||
"Mail server": "Почтовый сервер",
|
||||
"You'll enter your password on your mail server's sign-in page.": "Пароль вводится на странице входа вашего почтового сервера.",
|
||||
"You'll sign in on your mail server's own page.": "Вход выполняется на странице вашего почтового сервера.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Этот вход начат не в этом браузере. Попробуйте ещё раз.",
|
||||
|
||||
@@ -1211,6 +1211,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "Не вдалося зберегти фільтри: {error}",
|
||||
"Could not send the receipt: {error}": "Не вдалося надіслати сповіщення про прочитання: {error}",
|
||||
"Could not sign in.": "Не вдалося увійти.",
|
||||
"About INBUXA webmail": "Про вебпошту INBUXA",
|
||||
"Mail server": "Поштовий сервер",
|
||||
"You'll enter your password on your mail server's sign-in page.": "Пароль вводиться на сторінці входу вашого поштового сервера.",
|
||||
"You'll sign in on your mail server's own page.": "Вхід виконується на сторінці вашого поштового сервера.",
|
||||
"This sign-in didn't start in this browser. Try again.": "Цей вхід розпочато не в цьому браузері. Спробуйте ще раз.",
|
||||
|
||||
@@ -1222,6 +1222,8 @@ export const catalog: Catalog = {
|
||||
"Could not save filters: {error}": "无法保存过滤器:{error}",
|
||||
"Could not send the receipt: {error}": "无法发送已读回执:{error}",
|
||||
"Could not sign in.": "无法登录。",
|
||||
"About INBUXA webmail": "关于 INBUXA 网页邮箱",
|
||||
"Mail server": "邮件服务器",
|
||||
"You'll enter your password on your mail server's sign-in page.": "您将在邮件服务器的登录页面上输入密码。",
|
||||
"You'll sign in on your mail server's own page.": "您将在邮件服务器自己的页面上登录。",
|
||||
"This sign-in didn't start in this browser. Try again.": "此次登录不是在这个浏览器中发起的。请重试。",
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* The INBUXA wordmark, "inbuxa" in Space Grotesk Bold, as vector paths in the
|
||||
* current text color, so it reads on every palette, light or dark. The mark
|
||||
* beside it is `/img/inbuxa-mark.png`: ihasmail's own cat and envelope, which
|
||||
* INBUXA's logo reuses unchanged. The paths are the same ones INBUXA Admin
|
||||
* draws, from the INBUXA logo bundle.
|
||||
*
|
||||
* ihasmail-inbuxa only. Public ihasmail keeps its own name.
|
||||
*/
|
||||
export function InbuxaWordmark({ className, height }: { className?: string; height: number }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="296 44 488 112"
|
||||
height={height}
|
||||
width={(height * 488) / 112}
|
||||
role="img"
|
||||
aria-label="INBUXA"
|
||||
className={`notranslate ${className ?? ""}`}
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M70 0V496H196V0ZM133 554Q99 554 75.5 576.0Q52 598 52 634Q52 670 75.5 692.0Q99 714 133 714Q168 714 191.0 692.0Q214 670 214 634Q214 598 191.0 576.0Q168 554 133 554Z" transform="translate(303.25,151.90) scale(0.150000,-0.150000)" />
|
||||
<path d="M70 0V496H194V431H212Q224 457 257.0 480.5Q290 504 357 504Q415 504 458.5 477.5Q502 451 526.0 404.5Q550 358 550 296V0H424V286Q424 342 396.5 370.0Q369 398 318 398Q260 398 228.0 359.5Q196 321 196 252V0Z" transform="translate(340.15,151.90) scale(0.150000,-0.150000)" />
|
||||
<path d="M368 -14Q301 -14 265.0 9.0Q229 32 212 60H194V0H70V700H196V439H214Q225 457 243.5 473.0Q262 489 292.5 499.5Q323 510 368 510Q428 510 479.0 480.5Q530 451 561.0 394.0Q592 337 592 256V240Q592 159 561.0 102.0Q530 45 479.0 15.5Q428 -14 368 -14ZM330 96Q388 96 427.0 133.5Q466 171 466 243V253Q466 325 427.5 362.5Q389 400 330 400Q272 400 233.0 362.5Q194 325 194 253V243Q194 171 233.0 133.5Q272 96 330 96Z" transform="translate(429.55,151.90) scale(0.150000,-0.150000)" />
|
||||
<path d="M259 -8Q201 -8 157.5 18.5Q114 45 90.0 92.0Q66 139 66 200V496H192V210Q192 154 219.5 126.0Q247 98 298 98Q356 98 388.0 136.5Q420 175 420 244V496H546V0H422V65H404Q392 40 359.0 16.0Q326 -8 259 -8Z" transform="translate(522.25,151.90) scale(0.150000,-0.150000)" />
|
||||
<path d="M26 0 206 250 28 496H174L287 331H305L418 496H564L386 250L566 0H418L305 167H287L174 0Z" transform="translate(611.65,151.90) scale(0.150000,-0.150000)" />
|
||||
<path d="M224 -14Q171 -14 129.0 4.5Q87 23 62.5 58.5Q38 94 38 145Q38 196 62.5 230.5Q87 265 130.5 282.5Q174 300 230 300H366V328Q366 363 344.0 385.5Q322 408 274 408Q227 408 204.0 386.5Q181 365 174 331L58 370Q70 408 96.5 439.5Q123 471 167.5 490.5Q212 510 276 510Q374 510 431.0 461.0Q488 412 488 319V134Q488 104 516 104H556V0H472Q435 0 411.0 18.0Q387 36 387 66V67H368Q364 55 350.0 35.5Q336 16 306.0 1.0Q276 -14 224 -14ZM246 88Q299 88 332.5 117.5Q366 147 366 196V206H239Q204 206 184.0 191.0Q164 176 164 149Q164 122 185.0 105.0Q206 88 246 88Z" transform="translate(697.45,151.90) scale(0.150000,-0.150000)" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { BookOpen, Calendar, ChevronsUpDown, FolderOpen, Globe, HelpCircle, LogO
|
||||
import { useSession } from "@/store/session";
|
||||
import { withBase } from "@/lib/basePath";
|
||||
import { DEFAULT_APP_NAME } from "@/lib/brand";
|
||||
import { InbuxaWordmark } from "@/ui/InbuxaWordmark";
|
||||
import { useEffectiveTheme, useSettings } from "@/store/settings";
|
||||
import { toggleTarget } from "@/lib/palette";
|
||||
import { useMail } from "@/store/mail";
|
||||
@@ -147,14 +148,18 @@ export function AppShell({ children }: { children: ReactNode }) {
|
||||
<MenuIcon size={22} />
|
||||
</button>
|
||||
<Link href="/mail" className="brand">
|
||||
<img src={withBase("/img/logo.png")} alt="" />
|
||||
<img src={withBase(appName === DEFAULT_APP_NAME ? "/img/inbuxa-mark.png" : "/img/logo.png")} alt="" />
|
||||
{/* A product name, not a word: translated it is a different product.
|
||||
Read from the session rather than written here, so a deployment
|
||||
that set APP_NAME is called what it calls itself -- the document
|
||||
title has taken it from there all along. */}
|
||||
<span className="brand-name notranslate" translate="no">
|
||||
{appName}
|
||||
</span>
|
||||
{appName === DEFAULT_APP_NAME ? (
|
||||
<InbuxaWordmark className="brand-name" height={22} />
|
||||
) : (
|
||||
<span className="brand-name notranslate" translate="no">
|
||||
{appName}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
<SearchBar />
|
||||
<div className="topbar-actions">
|
||||
|
||||
@@ -7,6 +7,7 @@ import { DEFAULT_SOURCE_URL } from "@/lib/source";
|
||||
import { APP_VERSION } from "@/lib/version";
|
||||
import { DEFAULT_APP_NAME } from "@/lib/brand";
|
||||
import { t } from "@/lib/i18n";
|
||||
import { InbuxaWordmark } from "@/ui/InbuxaWordmark";
|
||||
|
||||
export function LoginPage() {
|
||||
const login = useSession((s) => s.login);
|
||||
@@ -92,10 +93,12 @@ export function LoginPage() {
|
||||
<div className="login-page">
|
||||
<form className="login-card" onSubmit={submit}>
|
||||
<div className="logo">
|
||||
<img src={withBase("/img/logo.png")} alt="" width={120} height={143} />
|
||||
<img src={withBase(appName === DEFAULT_APP_NAME ? "/img/inbuxa-mark.png" : "/img/logo.png")} alt="" width={120} height={143} />
|
||||
{/* A product name, not a word: not translated, and not guessed at
|
||||
from the page it is on. */}
|
||||
<h1 className="notranslate" translate="no">{appName}</h1>
|
||||
from the page it is on. INBUXA's is its wordmark. */}
|
||||
<h1 className="notranslate" translate="no">
|
||||
{appName === DEFAULT_APP_NAME ? <InbuxaWordmark height={34} /> : appName}
|
||||
</h1>
|
||||
<p className="tagline">{t("Fast, friendly webmail. Your mailbox, your way.")}</p>
|
||||
</div>
|
||||
{error && (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { DEFAULT_SOURCE_URL } from "@/lib/source";
|
||||
import { APP_VERSION } from "@/lib/version";
|
||||
import { withBase } from "@/lib/basePath";
|
||||
import { t, tNode } from "@/lib/i18n";
|
||||
import { InbuxaWordmark } from "@/ui/InbuxaWordmark";
|
||||
|
||||
export function AboutSettings() {
|
||||
const session = useSession((s) => s.session);
|
||||
@@ -12,13 +13,16 @@ export function AboutSettings() {
|
||||
const sourceUrl = session?.ihasmail?.sourceUrl ?? DEFAULT_SOURCE_URL;
|
||||
return (
|
||||
<div>
|
||||
<h1>{t("About ihasmail")}</h1>
|
||||
<p className="lead">{tNode("A fast, friendly, open-source webmail for {server}, built on JMAP.", { server: <a href="https://stalw.art" target="_blank" rel="noreferrer">{t("Stalwart Mail Server")}</a> })}</p>
|
||||
{/* ihasmail-inbuxa: INBUXA's webmail, built on ihasmail, whose version
|
||||
and source stay here as its credit and the AGPL's offer. */}
|
||||
<h1>{t("About INBUXA webmail")}</h1>
|
||||
<p className="lead">{tNode("A fast, friendly, open-source webmail for {server}, built on JMAP.", { server: <span className="notranslate" translate="no">INBUXA</span> })}</p>
|
||||
<div className="row" style={{ gap: 16, alignItems: "center", marginBottom: 16 }}>
|
||||
<img src={withBase("/img/logo.png")} alt={t("ihasmail")} width={96} />
|
||||
<img src={withBase("/img/inbuxa-mark.png")} alt="" width={80} />
|
||||
<div>
|
||||
<InbuxaWordmark height={26} />
|
||||
{/* A product name and a version string: neither is a word to translate. */}
|
||||
<div style={{ fontWeight: 700, fontSize: "1.2em" }} className="notranslate" translate="no">ihasmail v{APP_VERSION}</div>
|
||||
<div style={{ fontWeight: 700 }} className="notranslate" translate="no">ihasmail v{APP_VERSION}</div>
|
||||
<div className="hint">{tNode("AGPL-3.0-or-later · {source}", { source: <a href={sourceUrl} target="_blank" rel="noreferrer">{sourceUrl.replace(/^https?:\/\//, "")}</a> })}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,7 +30,7 @@ export function AboutSettings() {
|
||||
<table className="sessions-table">
|
||||
<tbody>
|
||||
<tr><td>{t("Signed in as")}</td><td>{session?.username}</td></tr>
|
||||
<tr><td>{t("Stalwart")}</td><td>{describeServer(session?.ihasmail?.server)}</td></tr>
|
||||
<tr><td>{t("Mail server")}</td><td className="notranslate" translate="no">INBUXA</td></tr>
|
||||
<tr><td>{t("Accounts")}</td><td>{Object.values(session?.accounts ?? {}).map((a) => a.name).join(", ")}</td></tr>
|
||||
<tr><td>{t("Max upload")}</td><td>{t("{size} MB", { size: Math.round(client.maxSizeUpload / 1048576) })}</td></tr>
|
||||
<tr><td>{t("Image privacy proxy")}</td><td>{session?.ihasmail?.imageProxy ? t("enabled") : t("disabled")}</td></tr>
|
||||
@@ -41,14 +45,3 @@ export function AboutSettings() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stalwart deliberately withholds its version from clients (it reports a fixed
|
||||
* "1.0.0" wherever it publishes one at all), so the edition is all there is to
|
||||
* show. The generation used to be reported here too, back when ihasmail spoke
|
||||
* to both 0.15 and 0.16; it requires 0.16 now, so signing in at all is the
|
||||
* answer to that question.
|
||||
*/
|
||||
function describeServer(server: { edition?: string | null } | undefined): string {
|
||||
return server?.edition ? `0.16 or newer (${server.edition})` : "0.16 or newer";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user