api: remove old frontend files

This commit is contained in:
wukko
2024-08-02 21:23:56 +06:00
parent ec98605336
commit eede972ace
85 changed files with 0 additions and 3693 deletions

View File

@@ -1,41 +0,0 @@
import * as esbuild from "esbuild";
import * as fs from "fs";
import { loadLoc, languageList } from "../localization/manager.js";
import { cleanHTML } from "./sub/utils.js";
import page from "./pageRender/page.js";
export async function buildFront(commitHash, branch) {
try {
// preload localization files
await loadLoc();
// build html
if (!fs.existsSync('./build/')){
fs.mkdirSync('./build/');
}
// get rid of old build path
if (fs.existsSync('./min')) {
fs.rmSync('./min', { recursive: true, force: true });
}
for (let i in languageList) {
i = languageList[i];
let params = {
"hash": commitHash,
"lang": i,
"branch": branch
}
fs.writeFileSync(`./build/${i}.html`, cleanHTML(page(params)));
}
// build js & css
await esbuild.build({
entryPoints: ['src/front/cobalt.js', 'src/front/cobalt.css'],
outdir: 'build/min/',
minify: true,
loader: { '.js': 'js', '.css': 'css', },
charset: 'utf8'
})
} catch {
return;
}
}

View File

@@ -1,7 +0,0 @@
import { buildFront } from "./build.js";
import { getCurrentBranch, shortCommit } from "./sub/currentCommit.js";
const commitHash = shortCommit();
const branch = getCurrentBranch();
await buildFront(commitHash, branch);

View File

@@ -1,46 +0,0 @@
import { replaceBase } from "../../localization/manager.js";
import { loadJSON } from "../sub/loadFromFs.js";
let changelog = loadJSON('./src/modules/changelog/changelog.json')
export default function(string) {
try {
const currentChangelog = changelog.current;
switch (string) {
case "version":
return `<span class="text-backdrop changelog-tag-version">v.${currentChangelog.version}</span>${
currentChangelog.date ? `<span class="changelog-tag-date">· ${currentChangelog.date}</span>` : ''
}`
case "title":
return replaceBase(currentChangelog.title);
case "banner":
const currentBanner = changelog.current.banner;
return currentBanner ? {
...currentBanner,
url: `updateBanners/${currentBanner.file}`
} : false;
case "content":
return replaceBase(currentChangelog.content);
case "history":
return changelog.history.map((log) => {
const banner = log.banner;
return {
title: replaceBase(log.title),
version: `<span class="text-backdrop changelog-tag-version">v.${log.version}</span>${
log.date ? `<span class="changelog-tag-date">· ${log.date}</span>` : ''
}`,
content: replaceBase(log.content),
banner: banner ? {
...banner,
url: `updateBanners/${banner.file}`
} : false,
}
});
default:
return replaceBase(changelog[string])
}
} catch (e) {
return `!!CHANGELOG_${string}!!`
}
}

View File

@@ -1,66 +0,0 @@
const names = {
"🎶": "musical_notes",
"🎬": "clapper_board",
"🎉": "party_popper",
"❓": "question_mark",
"✨": "sparkles",
"🪅": "pinata",
"🪄": "magic_wand",
"🐲": "dragon_face",
"🀄": "dragon_face_wukko",
"💸": "money_with_wings",
"⚙️": "gear",
"📋": "clipboard",
"🎃": "pumpkin",
"🎄": "christmas_tree",
"🕯️": "candle",
"😺": "cat",
"🐶": "dog",
"🎂": "cake",
"🐘": "elephant",
"🐦": "bird",
"🐙": "octopus",
"🔮": "crystal_ball",
"💪": "biceps",
"💖": "sparkling_heart",
"👾": "alien_monster",
"😿": "cat_crying",
"🙀": "cat_flabbergasted",
"🐱": "cat_smile",
"❤️‍🩹": "mending_heart",
"🔒": "locked",
"🔍": "magnifying_glass",
"🔗": "link",
"⌨": "keyboard",
"📑": "boring_document",
"🧮": "abacus",
"😸": "cat_grin",
"📰": "newspaper",
"🎞️": "film_frames",
"🎧": "headphone",
"📧": "email",
"📬": "mailbox",
"📢": "loudspeaker",
"🔧": "wrench",
"🫧": "bubbles"
}
let sizing = {
18: 0.8,
22: 0.4,
30: 0.7,
32: 0.8,
48: 0.9,
64: 0.9,
78: 0.9
}
export default function(emoji, size, disablePadding, fluent) {
if (!size) size = 22;
let padding = size !== 22 ? `margin-right:${sizing[size] ? sizing[size] : "0.4"}rem;` : false;
if (disablePadding) padding = 'margin-right:0!important;';
if (!names[emoji]) emoji = "❓";
let filePath = `emoji/${names[emoji]}.svg`;
if (fluent) filePath = `emoji/3d/${names[emoji]}.png`;
return `<img class="emoji" draggable=false height="${size}" width="${size}" ${padding ? `style="${padding}" ` : ''}alt="${emoji}" src="${filePath}" loading="lazy">`
}

View File

@@ -1,270 +0,0 @@
import { authorInfo, celebrations, sponsors, env } from "../config.js";
import emoji from "../emoji.js";
import { loadFile } from "../sub/loadFromFs.js";
export const backButtonSVG = `<svg width="22" height="22" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.1905 28.5L2 16L14.1905 3.5L16.2857 5.62054L7.65986 14.4654H30V17.5346H7.65986L16.2857 26.3516L14.1905 28.5Z" fill="#E1E1E1"/>
</svg>`
export const dropdownSVG = `<svg width="18" height="18" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28 12.0533L16 24L4 12.0533L6.03571 10L14.7188 18.4104L16.25 19.9348L17.7813 18.4104L25.9375 10L28 12.0533Z" fill="#E1E1E1"/>
</svg>`
export const linkSVG = '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 256 256"><path fill="currentColor" d="M137.54 186.36a8 8 0 0 1 0 11.31l-9.94 10a56 56 0 0 1-79.22-79.27l24.12-24.12a56 56 0 0 1 76.81-2.28a8 8 0 1 1-10.64 12a40 40 0 0 0-54.85 1.63L59.7 139.72a40 40 0 0 0 56.58 56.58l9.94-9.94a8 8 0 0 1 11.32 0Zm70.08-138a56.08 56.08 0 0 0-79.22 0l-9.94 9.95a8 8 0 0 0 11.32 11.31l9.94-9.94a40 40 0 0 1 56.58 56.58l-24.12 24.14a40 40 0 0 1-54.85 1.6a8 8 0 1 0-10.64 12a56 56 0 0 0 76.81-2.26l24.12-24.12a56.08 56.08 0 0 0 0-79.24Z"/></svg>'
export function switcher(obj) {
let items = ``;
if (obj.name === "download") {
items = obj.items;
} else {
for (let i = 0; i < obj.items.length; i++) {
let classes = obj.items[i]["classes"] ? obj.items[i]["classes"] : [];
items += `<button id="${obj.name}-${obj.items[i]["action"]}" class="switch${classes.length > 0 ? ' ' + classes.join(' ') : ''}" onclick="changeSwitcher('${obj.name}', '${obj.items[i]["action"]}')">${obj.items[i]["text"] ? obj.items[i]["text"] : obj.items[i]["action"]}</button>`
}
}
if (obj.noParent) return `<div id="${obj.name}" class="switches">${items}</div>`;
return `<div id="${obj.name}-switcher" class="switch-container">
${obj.subtitle ? `<div class="subtitle">${obj.subtitle}</div>` : ``}
<div class="switches">${items}</div>
${obj.explanation ? `<div class="explanation">${obj.explanation}</div>` : ``}
</div>`
}
export function checkbox(obj) {
let paddings = ["bottom-margin", "top-margin", "no-margin", "top-margin-only"];
let checkboxes = ``;
for (let i = 0; i < obj.length; i++) {
let paddingClass = obj[i].padding && paddings.includes(obj[i].padding) ? ` ${obj[i].padding}` : '';
checkboxes += `<label id="${obj[i].action}-chkbx" class="checkbox${paddingClass}">
<input id="${obj[i].action}" type="checkbox" aria-label="${obj[i].aria ? obj[i].aria : obj[i].name}" onclick="checkbox('${obj[i].action}')">
<span>${obj[i].name}</span>
</label>`
}
return checkboxes
}
export function sep(paddingType) {
let paddingClass = ``
switch(paddingType) {
case 0:
paddingClass += ` top-margin`;
break;
}
return `<div class="separator${paddingClass}"></div>`
}
export function popup(obj) {
let classes = obj.classes ? obj.classes : [];
let body = obj.body;
if (Array.isArray(obj.body)) {
body = ``
for (let i = 0; i < obj.body.length; i++) {
if (obj.body[i]["text"].length > 0) {
classes = obj.body[i]["classes"] ?? []
if (i !== obj.body.length - 1 && !obj.body[i]["nopadding"]) {
classes.push("desc-padding")
}
body += obj.body[i]["raw"] ? obj.body[i]["text"] : `<div class="${['popup-desc', ...classes].join(' ')}">${obj.body[i]["text"]}</div>`
}
}
}
return `
${obj.standalone ? `<div id="popup-${obj.name}" class="popup center${!obj.buttonOnly ? " box" : ''}${classes.length > 0 ? ' ' + classes.join(' ') : ''}">` : ''}
${obj.buttonOnly ? obj.header.emoji : ``}
${obj.name === "error" ? `` :
`<div class="popup-header">
<div class="popup-header-contents">
${obj.header.aboveTitle ? `<a class="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.title ? `<div class="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}
</div>
${!obj.buttonOnly ? `<div class="glass-bkg alone"></div>` : ''}
</div>`
}
<div class="popup-content popup-content-inner">
${body}${obj.buttonOnly ? `<button class="close-error switch" onclick="popup('${obj.name}', 0)">${obj.buttonText}</button>` : ''}
</div>
${classes.includes("small") ? `<div class="glass-bkg small"></div>` : ''}
${obj.standalone ? `</div>` : ''}`
}
export function multiPagePopup(obj) {
let tabs = `
<button class="back-button switch tab-${obj.name}" onclick="popup('${obj.name}', 0)" ${obj.closeAria ? `aria-label="${obj.closeAria}"` : ''}>
${backButtonSVG}
</button>`;
let tabContent = ``;
for (let i = 0; i < obj.tabs.length; i++) {
tabs += `<button id="tab-button-${obj.name}-${obj.tabs[i]["name"]}" class="switch tab tab-${obj.name}" onclick="changeTab(event, 'tab-${obj.name}-${obj.tabs[i]["name"]}', '${obj.name}')">${obj.tabs[i]["title"]}</button>`
tabContent += `<div id="tab-${obj.name}-${obj.tabs[i]["name"]}" class="popup-tab-content tab-content-${obj.name}">${obj.tabs[i]["content"]}</div>`
}
return `
<div id="popup-${obj.name}" class="popup center box scrollable">
<div class="popup-content">
${obj.header ? `<div class="popup-header">
<div class="popup-header-contents">
${obj.header.aboveTitle ? `<a class="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.title ? `<div class="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}
</div>
<div class="glass-bkg alone"></div>
</div>` : ''}${tabContent}</div>
<div class="switches popup-tabs">
<div class="switches popup-tabs-child">${tabs}</div>
<div class="glass-bkg alone"></div>
</div>
</div>`
}
export function collapsibleList(arr) {
let items = ``;
for (let i = 0; i < arr.length; i++) {
let classes = arr[i]["classes"] ? arr[i]["classes"] : [];
items += `<div id="${arr[i]["name"]}-collapse" class="collapse-list${classes.length > 0 ? ' ' + classes.join(' ') : ''}">
<div class="collapse-header" onclick="expandCollapsible(event)">
<div class="collapse-title">${arr[i]["title"]}</div>
<div class="collapse-indicator">${dropdownSVG}</div>
</div>
<div id="${arr[i]["name"]}-body" class="collapse-body">${arr[i]["body"]}</div>
</div>`
}
return items;
}
export function popupWithBottomButtons(obj) {
let tabs = `
<button class="back-button switch tab-${obj.name}" onclick="popup('${obj.name}', 0)" ${obj.closeAria ? `aria-label="${obj.closeAria}"` : ''}>
${backButtonSVG}
</button>`
for (let i = 0; i < obj.buttons.length; i++) {
tabs += obj.buttons[i]
}
return `
<div id="popup-${obj.name}" class="popup center box scrollable">
<div class="popup-content">
${obj.header ? `<div class="popup-header">
<div class="popup-header-contents">
${obj.header.aboveTitle ? `<a class="popup-above-title" target="_blank" href="${obj.header.aboveTitle.url}">${obj.header.aboveTitle.text}</a>` : ''}
${obj.header.title ? `<div class="popup-title">${obj.header.title}</div>` : ''}
${obj.header.subtitle ? `<div id="popup-subtitle">${obj.header.subtitle}</div>` : ''}
${obj.header.explanation ? `<div class="explanation">${obj.header.explanation}</div>` : ''}
</div>
<div class="glass-bkg alone"></div>
</div>` : ''}${obj.content}</div>
<div class="switches popup-tabs">
<div id="picker-buttons" class="switches popup-tabs-child">${tabs}</div>
<div class="glass-bkg alone"></div>
</div>
</div>`
}
export function socialLink(emji, name, url) {
return `<div class="cobalt-support-link">${emji} <a class="text-backdrop link" href="${url}" target="_blank">${name}</a></div>`
}
export function socialLinks(lang) {
let links = authorInfo.support[lang] ? authorInfo.support[lang] : authorInfo.support.default;
let r = ``;
for (let i in links) {
r += socialLink(
emoji(links[i].emoji), links[i].name, links[i].url
)
}
return r
}
export function settingsCategory(obj) {
return `<div id="settings-${obj.name}" class="settings-category">
<div class="category-title">${obj.title ?? obj.name}</div>
<div class="category-content">${obj.body}</div>
</div>`
}
export function footerButtons(obj) {
let items = ``
for (let i = 0; i < obj.length; i++) {
let buttonName = obj[i]["context"] ? `${obj[i]["name"]}-${obj[i]["context"]}` : obj[i]["name"],
context = obj[i]["context"] ? `, '${obj[i]["context"]}'` : '',
buttonName2,
context2;
if (obj[i + 1]) {
buttonName2 = obj[i + 1]["context"] ? `${obj[i + 1]["name"]}-${obj[i + 1]["context"]}` : obj[i + 1]["name"];
context2 = obj[i + 1]["context"] ? `, '${obj[i + 1]["context"]}'` : '';
}
items +=
`<div class="footer-pair">
<button id="${buttonName}-footer" class="switch footer-button" onclick="popup('${obj[i]["name"]}', 1${context})" aria-label="${obj[i]["aria"]}">${obj[i]["text"]}</button>
${obj[i + 1] ? `<button id="${buttonName2}-footer" class="switch footer-button" onclick="popup('${obj[i + 1]["name"]}', 1${context2})" aria-label="${obj[i + 1]["aria"]}">${obj[i + 1]["text"]}</button>` : ''}
</div>`;
i++;
}
return `
<div id="footer-buttons">${items}</div>`
}
export function explanation(text) {
return `<div class="explanation">${text}</div>`
}
export function celebrationsEmoji() {
try {
let n = new Date().toISOString().split('T')[0].split('-');
let dm = `${n[1]}-${n[2]}`;
let f = Object.keys(celebrations).includes(dm) ? celebrations[dm] : "🐲";
return f != "🐲" ? emoji(f, 22) : false;
} catch (e) {
return false
}
}
export function urgentNotice(obj) {
if (obj.visible) {
return `<div id="urgent-notice" class="urgent-notice explanation">` +
`<span id="urgent-notice-child" class="urgent-text" onclick="${obj.action}">${emoji(obj.emoji, 18)} ${obj.text}</span>` +
`</div>`
}
return ``
}
export function keyboardShortcuts(arr) {
let base = `<div id="keyboard-shortcuts" class="explanation">`;
for (let i = 0; i < arr.length; i++) {
base += `<div class="shortcut-category">`;
for (let c = 0; c < arr[i].items.length; c++) {
let combo = arr[i].items[c].combo.split('+').map(
key => `<span class="text-backdrop key">${key}</span>`
).join("+")
base += `<div class="shortcut">${combo}: ${arr[i].items[c].name}</div>`
}
base += `</div>`
}
base += `</div>`;
return base;
}
export function webLoc(t, arr) {
let base = ``;
for (let i = 0; i < arr.length; i++) {
base += `${arr[i]}:` + "`" + t(arr[i]) + "`" + `,`
}
return `{${base}};`
}
export function sponsoredList() {
let base = ``;
let altText = ``
for (let i = 0; i < sponsors.length; i++) {
let s = sponsors[i];
let loadedLogo = loadFile(`./src/front/sponsors/${s.name}.svg`);
altText += `${s.fullName ? s.fullName : s.name}, `;
base +=
`<a class="sponsored-logo ${s.name}"
href="${s.url}" target="_blank"
style="width: calc(${s.logo.width}px / ${s.logo.scale}); height: calc(${s.logo.height}px / ${s.logo.scale});">
${loadedLogo}
</a>`
}
return `<div id="sponsored-logos" aria-label="${altText.slice(0, -2)}">${base}</div>`
}
export function betaTag() {
return env.isBeta ? '<span class="logo-sub">β</span>' : ''
}

View File

@@ -1,6 +0,0 @@
import { languageList } from "../../localization/manager.js";
export default function(lang) {
let language = languageList.includes(lang) ? lang : "en";
return `/build/${language}.html`;
}

View File

@@ -1,33 +0,0 @@
import changelogManager from "../changelog/changelogManager.js"
import { cleanHTML } from "../sub/utils.js";
let cache = {}
export function changelogHistory() { // blockId 0
if (cache['0']) return cache['0'];
let history = changelogManager("history");
let render = ``;
let historyLen = history.length;
for (let i in history) {
let separator = (i !== 0 && i !== historyLen) ? '<div class="separator"></div>' : '';
render += `
${separator}${history[i]["banner"] ?
`<div class="changelog-banner">
<img class="changelog-img" ` +
`src="${history[i]["banner"]["url"]}" ` +
`alt="${history[i]["banner"]["alt"].replaceAll('"', '&quot;')}" ` +
`width="${history[i]["banner"]["width"]}" ` +
`height="${history[i]["banner"]["height"]}" ` +
`onerror="this.style.opacity=0" loading="lazy">`+
`
</div>` : ''}
<div class="popup-desc changelog-tags">${history[i]["version"]}</div>
<div class="popup-desc changelog-subtitle">${history[i]["title"]}</div>
<div class="popup-desc desc-padding">${history[i]["content"]}</div>`
}
render = cleanHTML(render);
cache['0'] = render;
return render;
}

View File

@@ -1,666 +0,0 @@
import { services as s, version, repo, donations, supportedAudio, links, env } from "../config.js";
import { getCommitInfo } from "../sub/currentCommit.js";
import loc from "../../localization/manager.js";
import emoji from "../emoji.js";
import changelogManager from "../changelog/changelogManager.js";
import {
checkbox,
collapsibleList,
explanation,
footerButtons,
multiPagePopup,
popup,
popupWithBottomButtons,
sep,
settingsCategory,
switcher,
socialLink,
socialLinks,
urgentNotice,
keyboardShortcuts,
webLoc,
sponsoredList,
betaTag,
linkSVG
} from "./elements.js";
let com = getCommitInfo();
let enabledServices = Object.keys(s).filter(p => s[p].enabled).sort().map((p) => {
return `<br>&bull; ${s[p].alias ? s[p].alias : p}`
}).join('').substring(4)
let donate = ``
let donateLinks = ``
let audioFormats = supportedAudio.map((p) => {
return { "action": p }
})
audioFormats.unshift({ "action": "best" })
for (let i in donations["links"]) {
donateLinks += `<a id="don-${i}" class="switch autowidth" href="${donations["links"][i]}" target="_blank">REPLACEME ${i}</a>`
}
let extr = ''
for (let i in donations["crypto"]) {
donate += `<div class="subtitle${extr}">${i} (REPLACEME)</div><div id="don-${i}" class="text-to-copy" onClick="copy('don-${i}')">${donations["crypto"][i]}</div>`
extr = ' top-margin'
}
export default function(obj) {
const t = (str, replace) => {
return loc(obj.lang, str, replace)
}
audioFormats[0]["text"] = t('SettingsAudioFormatBest');
try {
return `
<!DOCTYPE html>
<html lang="${obj.lang}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="viewport-fit=cover, width=device-width, height=device-height, initial-scale=1, maximum-scale=1">
<title>${t("AppTitleCobalt")}</title>
<meta property="og:url" content="${env.webURL}">
<meta property="og:title" content="${t("AppTitleCobalt")}">
<meta property="og:description" content="${t('EmbedBriefDescription')}">
<meta property="og:image" content="${env.webURL}icons/generic.png">
<meta name="title" content="${t("AppTitleCobalt")}">
<meta name="description" content="${t('AboutSummary')}">
<meta name="twitter:card" content="summary">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="${t("AppTitleCobalt")}">
<link rel="icon" type="image/x-icon" href="icons/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-touch-icon.png">
<link rel="manifest" href="manifest.webmanifest">
<link rel="stylesheet" href="fonts/notosansmono.css">
<link rel="stylesheet" href="cobalt.css">
<meta name="theme-color" content="#000000">
<link rel="preload" href="fonts/notosansmono.css" as="style">
<link rel="preload" href="assets/meowbalt/error.png" as="image">
<link rel="preload" href="assets/meowbalt/question.png" as="image">
${env.plausibleHostname ?
`<script
defer
data-domain="${new URL(env.webURL).hostname}"
src="https://${env.plausibleHostname}/js/script.js"
></script>`
: ''}
</head>
<body id="cobalt-body">
<noscript>
<div style="margin: 2rem;">${t('NoScriptMessage')}</div>
</noscript>
${multiPagePopup({
name: "about",
closeAria: t('AccessibilityGoBack'),
tabs: [{
name: "about",
title: `${emoji("🐲")} ${t('AboutTab')}`,
content: popup({
name: "about",
header: {
aboveTitle: {
text: t('MadeWithLove'),
url: repo
},
closeAria: t('AccessibilityGoBack'),
title: `${emoji("🔮", 30)} ${t('TitlePopupAbout')}`
},
body: [{
text: t('AboutSummary')
}, {
text: collapsibleList([{
name: "services",
title: `${emoji("🔗")} ${t("CollapseServices")}`,
body: `${enabledServices}`
+ `<div class="explanation embedded">${t("SupportNotAffiliated")}`
+ `${obj.lang === "ru" ? `<br>${t("SupportMetaNoticeRU")}` : ''}`
+ `</div>`
+ `${t("ServicesNote")}`
}, {
name: "keyboard",
title: `${emoji("⌨")} ${t("CollapseKeyboard")}`,
body:
`${t("KeyboardShortcutsIntro")}
${keyboardShortcuts([{
items: [{
combo: "Shift+D",
name: t("PasteFromClipboard")
}, {
combo: "Shift+K",
name: t("ModeToggleAuto")
}, {
combo: "Shift+L",
name: t("ModeToggleAudio")
}]
}, {
items: [{
combo: "⌘/Ctrl+V",
name: t("KeyboardShortcutQuickPaste")
}, {
combo: "Esc",
name: t("KeyboardShortcutClear")
}, {
combo: "Esc",
name: t("KeyboardShortcutClosePopup")
}]
}, {
items: [{
combo: "Shift+B",
name: t("AboutTab")
}, {
combo: "Shift+N",
name: t("ChangelogTab")
}, {
combo: "Shift+M",
name: t("TitlePopupSettings")
}]
}])}`
}, {
name: "support",
title: `${emoji("❤️‍🩹")} ${t("CollapseSupport")}`,
body: `${t("SupportSelfTroubleshooting")}`
+ `${socialLink(emoji("📢"), t("StatusPage"), links.statusPage)}`
+ `${socialLink(emoji("🔧"), t("TroubleshootingGuide"), links.troubleshootingGuide)}`
+ `<br>`
+ `${t("FollowSupport")}`
+ `${socialLinks(obj.lang)}`
+ `<br>`
+ `${t("SourceCode")}`
+ `${socialLink(emoji("🐙"), repo.replace("https://github.com/", ''), repo)}`
}, {
name: "privacy",
title: `${emoji("🔒")} ${t("CollapsePrivacy")}`,
body: t("PrivacyPolicy") + `${
env.plausibleHostname ? `<br><br>${t("AnalyticsDescription")}` : ''
}`
}, {
name: "legal",
title: `${emoji("📑")} ${t("CollapseLegal")}`,
body: t("FairUse")
}])
},
...(env.showSponsors ?
[{
text: t("SponsoredBy"),
classes: ["sponsored-by-text"],
nopadding: true
}, {
text: sponsoredList(),
raw: true
}] : []
)]
})
}, {
name: "changelog",
title: `${emoji("🎉")} ${t('ChangelogTab')}`,
content: popup({
name: "changelog",
header: {
closeAria: t('AccessibilityGoBack'),
title: `${emoji("🪄", 30)} ${t('TitlePopupChangelog')}`
},
body: [{
text: `<div class="category-title">${t('ChangelogLastMajor')}</div>`,
raw: true
}, {
text: (() => {
const banner = changelogManager('banner');
if (!banner) return '';
return `<div class="changelog-banner">
<img class="changelog-img" ` +
`src="${banner.url}" ` +
`alt="${banner.alt.replaceAll('"', '&quot;')}" ` +
`width="${banner.width}" ` +
`height="${banner.height}" ` +
`onerror="this.style.opacity=0" loading="lazy">
</div>`;
})(),
raw: true
}, {
text: changelogManager("version"),
classes: ["changelog-tags"],
nopadding: true
}, {
text: changelogManager("title"),
classes: ["changelog-subtitle"],
nopadding: true
}, {
text: changelogManager("content")
}, {
text: sep(),
raw: true
},{
text: `<a class="text-backdrop changelog-tag-version" href="${repo}/commit/${obj.hash}">#${obj.hash}</a>`,
classes: ["changelog-tags"],
nopadding: true
}, {
text: com[0],
classes: ["changelog-subtitle"],
nopadding: true
}, {
text: com[1]
}, {
text: `<div class="category-title">${t('ChangelogOlder')}</div>`,
raw: true
}, {
text: `
<div id="changelog-history">
<button class="switch bottom-margin" onclick="loadOnDemand('changelog-history', '0')">${t("ChangelogPressToExpand")}</button>
</div>`,
raw: true
}]
})
}, {
name: "donate",
title: `${emoji("💖")} ${t('DonationsTab')}`,
content: popup({
name: "donate",
header: {
closeAria: t('AccessibilityGoBack'),
title: emoji("💸", 30) + t('TitlePopupDonate')
},
body: [{
text: `<div class="category-title">${t('DonateSub')}</div>`,
raw: true
}, {
text: `
<div class="changelog-banner">
<img class="changelog-img" ` +
`src="updateBanners/catsleep.webp" ` +
`alt="${t("DonateImageDescription")}" ` +
`width="480" ` +
`height="270" ` +
`onerror="this.style.opacity=0" loading="lazy">
</div>`,
raw: true
}, {
text: t('DonateExplanation')
}, {
text: donateLinks.replace(/REPLACEME/g, t('DonateVia')),
raw: true
}, {
text: t('DonateLinksDescription'),
classes: ["explanation"]
}, {
text: sep(),
raw: true
}, {
text: donate.replace(/REPLACEME/g, t('ClickToCopy')),
classes: ["desc-padding"]
}]
})
}],
})}
${multiPagePopup({
name: "settings",
closeAria: t('AccessibilityGoBack'),
header: {
aboveTitle: {
text: `v.${version}-${obj.hash} (${obj.branch})`,
url: `${repo}/commit/${obj.hash}`
},
title: `${emoji("⚙️", 30)} ${t('TitlePopupSettings')}`
},
tabs: [{
name: "video",
title: `${emoji("🎬")} ${t('SettingsVideoTab')}`,
content: settingsCategory({
name: "downloads",
title: t('SettingsQualitySubtitle'),
body: switcher({
name: "vQuality",
explanation: t('SettingsQualityDescription'),
items: [{
action: "max",
text: "8k+"
}, {
action: "2160",
text: "4k"
}, {
action: "1440",
text: "1440p"
}, {
action: "1080",
text: "1080p"
}, {
action: "720",
text: "720p"
}, {
action: "480",
text: "480p"
}, {
action: "360",
text: "360p"
}, {
action: "240",
text: "240p"
}, {
action: "144",
text: "144p"
}]
})
})
+ settingsCategory({
name: "codec",
title: t('SettingsCodecSubtitle'),
body: switcher({
name: "vCodec",
explanation: t('SettingsCodecDescription'),
items: [{
action: "h264",
text: "h264 (mp4)"
}, {
action: "av1",
text: "av1 (mp4)"
}, {
action: "vp9",
text: "vp9 (webm)"
}]
})
})
+ settingsCategory({
name: "twitter",
title: "twitter",
body: checkbox([{
action: "twitterGif",
name: t("SettingsTwitterGif"),
padding: "no-margin"
}])
+ explanation(t('SettingsTwitterGifDescription'))
})
+ settingsCategory({
name: "tiktok",
title: "tiktok",
body: checkbox([{
action: "tiktokH265",
name: t("SettingsTikTokH265"),
padding: "no-margin"
}])
+ explanation(t('SettingsTikTokH265Description'))
})
}, {
name: "audio",
title: `${emoji("🎶")} ${t('SettingsAudioTab')}`,
content: settingsCategory({
name: "general",
title: t('SettingsFormatSubtitle'),
body: switcher({
name: "aFormat",
explanation: t('SettingsAudioFormatDescription'),
items: audioFormats
})
+ sep(0)
+ checkbox([{
action: "muteAudio",
name: t("SettingsVideoMute"),
padding: "no-margin"
}])
+ explanation(t('SettingsVideoMuteExplanation'))
})
+ settingsCategory({
name: "youtube-dub",
title: t("SettingsAudioDub"),
body: checkbox([{
action: "ytDub",
name: t("SettingsYoutubeDub"),
padding: "no-margin"
}])
+ explanation(t('SettingsYoutubeDubDescription'))
})
+ settingsCategory({
name: "tiktok-audio",
title: "tiktok",
body: checkbox([{
action: "fullTikTokAudio",
name: t("SettingsAudioFullTikTok"),
padding: "no-margin"
}])
+ explanation(t('SettingsAudioFullTikTokDescription'))
})
}, {
name: "other",
title: `${emoji("🪅")} ${t('SettingsOtherTab')}`,
content: settingsCategory({
name: "appearance",
title: t('SettingsAppearanceSubtitle'),
body: switcher({
name: "theme",
items: [{
action: "auto",
text: t('SettingsThemeAuto')
}, {
action: "dark",
text: t('SettingsThemeDark')
}, {
action: "light",
text: t('SettingsThemeLight')
}]
})
})
+ settingsCategory({
name: "filename",
title: t('FilenameTitle'),
body: switcher({
name: "filenamePattern",
items: [{
action: "classic",
text: t('FilenamePatternClassic')
}, {
action: "basic",
text: t('FilenamePatternBasic')
}, {
action: "pretty",
text: t('FilenamePatternPretty')
}, {
action: "nerdy",
text: t('FilenamePatternNerdy')
}]
})
+ `<div id="filename-preview">
<div id="video-filename" class="filename-item line">
${emoji('🎞️', 32, 1, 1)}
<div class="filename-container">
<div class="filename-label">${t('Preview')}</div>
<div id="video-filename-text"></div>
</div>
</div>
<div id="audio-filename" class="filename-item">
${emoji('🎧', 32, 1, 1)}
<div class="filename-container">
<div class="filename-label">${t('Preview')}</div>
<div id="audio-filename-text"></div>
</div>
</div>
</div>`
+ explanation(t('FilenameDescription'))
})
+ settingsCategory({
name: "accessibility",
title: t('Accessibility'),
body: checkbox([{
action: "alwaysVisibleButton",
name: t("SettingsKeepDownloadButton"),
aria: t("AccessibilityKeepDownloadButton")
}, {
action: "reduceTransparency",
name: t("SettingsReduceTransparency")
}, {
action: "disableAnimations",
name: t("SettingsDisableAnimations"),
padding: "no-margin"
}])
})
+ (() => {
if (env.plausibleHostname) {
return settingsCategory({
name: "privacy",
title: t('PrivateAnalytics'),
body: checkbox([{
action: "plausible_ignore",
name: t("SettingsDisableAnalytics"),
padding: "no-margin"
}])
+ explanation(t('SettingsAnalyticsExplanation'))
})
}
return ''
})()
+ settingsCategory({
name: "miscellaneous",
title: t('Miscellaneous'),
body: checkbox([{
action: "downloadPopup",
name: t("SettingsEnableDownloadPopup"),
aria: t("AccessibilityEnableDownloadPopup")
}, {
action: "disableMetadata",
name: t("SettingsDisableMetadata")
}])
})
}]
})}
${popupWithBottomButtons({
name: "picker",
closeAria: t('AccessibilityGoBack'),
header: {
title: `${emoji("🧮", 30)} <div id="picker-title"></div>`,
explanation: `<div id="picker-subtitle"></div>`,
},
buttons: [`<a id="picker-download" class="switch" target="_blank" href="/">${t('ImagePickerDownloadAudio')}</a>`],
content: '<div id="picker-holder"></div>'
})}
<div id="popup-download-container" class="popup-from-bottom">
${popup({
name: "download",
standalone: true,
buttonOnly: true,
classes: ["small"],
header: {
closeAria: t('AccessibilityGoBack'),
emoji: `<img class="popout-meowbalt" `
+ `draggable="false" loading="lazy" `
+ `alt="😿" src="assets/meowbalt/question.png">`,
title: t('TitlePopupDownload')
},
body: switcher({
name: "download",
explanation: t('DownloadPopupDescription'),
items: `<a id="pd-download" class="switch full" target="_blank" href="/"><span>${t('Download')}</span></a>
<div id="pd-share" class="switch full">${t('ShareURL')}</div>
<div id="pd-copy" class="switch full">${t('CopyURL')}</div>`
}),
buttonText: t('PopupCloseDone')
})}
</div>
<div id="popup-error-container" class="popup-from-bottom">
${popup({
name: "error",
standalone: true,
buttonOnly: true,
classes: ["small"],
header: {
emoji: `<img class="popout-meowbalt" `
+ `draggable="false" loading="lazy" `
+ `alt="😿" src="assets/meowbalt/error.png">`,
},
body: `<div id="desc-error" class="desc-padding subtext desc-error"></div>`,
buttonText: t('ErrorPopupCloseButton')
})}
</div>
<div id="popup-backdrop" onclick="hideAllPopups()"></div>
<div id="home" style="visibility:hidden">
${urgentNotice({
emoji: "🎉",
text: t("UpdateOneMillion"),
visible: true,
action: "popup('about', 1, 'changelog')"
})}
<div id="cobalt-main-box" class="center">
<div id="logo">${t("AppTitleCobalt")}${betaTag()}</div>
<div id="download-area">
<div id="top">
<div id="link-icon">${linkSVG}</div>
<input id="url-input-area" class="mono" type="text" autocomplete="off" data-form-type="other" spellcheck="false" maxlength="256" autocapitalize="off" placeholder="${t('LinkInput')}" aria-label="${t('AccessibilityInputArea')}" oninput="button()">
<button id="url-clear" onclick="clearInput()" style="display:none;">x</button>
<input id="download-button" class="mono dontRead" onclick="download(document.getElementById('url-input-area').value)" type="submit" value="" disabled aria-label="${t('AccessibilityDownloadButton')}">
</div>
<div id="bottom">
<button id="paste" class="switch" onclick="pasteClipboard()" aria-label="${t('PasteFromClipboard')}">${emoji("📋", 22)} ${t('PasteFromClipboard')}</button>
${switcher({
name: "audioMode",
noParent: true,
items: [{
action: "false",
text: `${emoji("✨")} ${t("ModeToggleAuto")}`
}, {
action: "true",
text: `${emoji("🎶")} ${t("ModeToggleAudio")}`
}]
})}
</div>
</div>
</div>
<footer id="footer">
${footerButtons([{
name: "about",
type: "popup",
text: `${emoji("🐲" , 22)} ${t('AboutTab')}`,
aria: t('AccessibilityOpenAbout')
}, {
name: "about",
type: "popup",
context: "donate",
text: `${emoji("💖", 22)} ${t('Donate')}`,
aria: t('AccessibilityOpenDonate')
}, {
name: "settings",
type: "popup",
text: `${emoji("⚙️", 22)} ${t('TitlePopupSettings')}`,
aria: t('AccessibilityOpenSettings')
}])}
</footer>
</div>
<script>
let defaultApiUrl = '${env.apiURL}';
const loc = ${webLoc(t,
[
'ErrorNoInternet',
'ErrorNoUrlReturned',
'ErrorUnknownStatus',
'ChangelogPressToHide',
'MediaPickerTitle',
'MediaPickerExplanationPhone',
'MediaPickerExplanationPC',
'FeatureErrorGeneric',
'ClipboardErrorNoPermission',
'ClipboardErrorFirefox',
'DataTransferSuccess',
'DataTransferError',
'FilenamePreviewVideoTitle',
'FilenamePreviewAudioTitle',
'FilenamePreviewAudioAuthor',
'DownloadPopupDescriptionIOS'
])}
</script>
<script src="cobalt.js"></script>
</body>
</html>
`
} catch (err) {
return `${t('ErrorPageRenderFail', obj.hash)}`;
}
}