Favicon templates
This commit is contained in:
parent
5f2e7d7bfa
commit
b82da219ce
@ -1864,20 +1864,24 @@ const AutoSettingsMixin = (SuperClass) => {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this._auto_settings_setup();
|
this._auto_settings_setup();
|
||||||
|
this.addEventListener("browser-mod-config-update", () => this._auto_settings_setup());
|
||||||
}
|
}
|
||||||
async _auto_settings_setup() {
|
async _auto_settings_setup() {
|
||||||
await this.connectionPromise;
|
await this.connectionPromise;
|
||||||
const settings = this.settings;
|
const settings = this.settings;
|
||||||
|
// Sidebar panel order and hiding
|
||||||
if (settings.sidebarPanelOrder) {
|
if (settings.sidebarPanelOrder) {
|
||||||
localStorage.setItem("sidebarPanelOrder", settings.sidebarPanelOrder);
|
localStorage.setItem("sidebarPanelOrder", settings.sidebarPanelOrder);
|
||||||
}
|
}
|
||||||
if (settings.sidebarHiddenPanels) {
|
if (settings.sidebarHiddenPanels) {
|
||||||
localStorage.setItem("sidebarHiddenPanels", settings.sidebarHiddenPanels);
|
localStorage.setItem("sidebarHiddenPanels", settings.sidebarHiddenPanels);
|
||||||
}
|
}
|
||||||
|
// Hide sidebar
|
||||||
if (settings.hideSidebar === true) {
|
if (settings.hideSidebar === true) {
|
||||||
selectTree(document.body, "home-assistant$home-assistant-main$app-drawer-layout").then((el) => el.style.setProperty("--app-drawer-width", "0px"));
|
selectTree(document.body, "home-assistant$home-assistant-main$app-drawer-layout").then((el) => el.style.setProperty("--app-drawer-width", "0px"));
|
||||||
selectTree(document.body, "home-assistant$home-assistant-main$app-drawer-layout app-drawer").then((el) => el.remove());
|
selectTree(document.body, "home-assistant$home-assistant-main$app-drawer-layout app-drawer").then((el) => el.remove());
|
||||||
}
|
}
|
||||||
|
// Hide header
|
||||||
if (settings.hideHeader === true) {
|
if (settings.hideHeader === true) {
|
||||||
customElements.whenDefined("app-header-layout").then(() => {
|
customElements.whenDefined("app-header-layout").then(() => {
|
||||||
const appHeader = customElements.get("app-header").prototype;
|
const appHeader = customElements.get("app-header").prototype;
|
||||||
@ -1888,6 +1892,40 @@ const AutoSettingsMixin = (SuperClass) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Favicon template
|
||||||
|
if (settings.faviconTemplate !== undefined) {
|
||||||
|
(async () => {
|
||||||
|
if (this._faviconTemplateSubscription) {
|
||||||
|
this._faviconTemplateSubscription();
|
||||||
|
}
|
||||||
|
this._faviconTemplateSubscription = undefined;
|
||||||
|
this._faviconTemplateSubscription =
|
||||||
|
await this.connection.subscribeMessage(this._updateFavicon, {
|
||||||
|
type: "render_template",
|
||||||
|
template: settings.faviconTemplate,
|
||||||
|
variables: {},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
// Title template
|
||||||
|
if (settings.titleTemplate !== undefined) ;
|
||||||
|
}
|
||||||
|
get _currentFavicon() {
|
||||||
|
const link = document.head.querySelector("link[rel~='icon']");
|
||||||
|
return link === null || link === void 0 ? void 0 : link.href;
|
||||||
|
}
|
||||||
|
_updateFavicon({ result }) {
|
||||||
|
// TEMP: Template for testing
|
||||||
|
/*
|
||||||
|
{% if is_state("light.bed_light", "on") %}
|
||||||
|
/local/workspace/test/icons/green.png
|
||||||
|
{% else %}
|
||||||
|
/local/workspace/test/icons/red.png
|
||||||
|
{% endif %}
|
||||||
|
*/
|
||||||
|
const link = document.head.querySelector("link[rel~='icon']");
|
||||||
|
link.href = result;
|
||||||
|
window.browser_mod.fireEvent("browser-mod-favicon-update");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -1931,9 +1969,9 @@ const AutoSettingsMixin = (SuperClass) => {
|
|||||||
x Kiosk mode
|
x Kiosk mode
|
||||||
- Default panel?
|
- Default panel?
|
||||||
- Screensaver?
|
- Screensaver?
|
||||||
- Tweaks
|
x Favicon templates
|
||||||
- Favicon templates
|
|
||||||
- Title templates
|
- Title templates
|
||||||
|
- Tweaks
|
||||||
- Quickbar tweaks (ctrl+enter)?
|
- Quickbar tweaks (ctrl+enter)?
|
||||||
- Video player?
|
- Video player?
|
||||||
- Media_seek
|
- Media_seek
|
||||||
|
@ -67,7 +67,7 @@ const i=(i,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,
|
|||||||
|
|
||||||
// Loads in ha-config-dashboard which is used to copy styling
|
// Loads in ha-config-dashboard which is used to copy styling
|
||||||
// Also provides ha-settings-row
|
// Also provides ha-settings-row
|
||||||
const loadDevTools = async () => {
|
const loadConfigDashboard = async () => {
|
||||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
||||||
await customElements.whenDefined("partial-panel-resolver");
|
await customElements.whenDefined("partial-panel-resolver");
|
||||||
const ppResolver = document.createElement("partial-panel-resolver");
|
const ppResolver = document.createElement("partial-panel-resolver");
|
||||||
@ -84,7 +84,24 @@ const loadDevTools = async () => {
|
|||||||
await ((_l = (_k = (_j = (_h = configRouter === null || configRouter === void 0 ? void 0 : configRouter.routerOptions) === null || _h === void 0 ? void 0 : _h.routes) === null || _j === void 0 ? void 0 : _j.cloud) === null || _k === void 0 ? void 0 : _k.load) === null || _l === void 0 ? void 0 : _l.call(_k)); // Load ha-settings-row
|
await ((_l = (_k = (_j = (_h = configRouter === null || configRouter === void 0 ? void 0 : configRouter.routerOptions) === null || _h === void 0 ? void 0 : _h.routes) === null || _j === void 0 ? void 0 : _j.cloud) === null || _k === void 0 ? void 0 : _k.load) === null || _l === void 0 ? void 0 : _l.call(_k)); // Load ha-settings-row
|
||||||
await customElements.whenDefined("ha-config-dashboard");
|
await customElements.whenDefined("ha-config-dashboard");
|
||||||
};
|
};
|
||||||
|
const loadDeveloperToolsTemplate = async () => {
|
||||||
|
var _a, _b, _c, _d, _e, _f, _g;
|
||||||
|
await customElements.whenDefined("partial-panel-resolver");
|
||||||
|
await customElements.whenDefined("partial-panel-resolver");
|
||||||
|
const ppResolver = document.createElement("partial-panel-resolver");
|
||||||
|
const routes = ppResolver.getRoutes([
|
||||||
|
{
|
||||||
|
component_name: "developer-tools",
|
||||||
|
url_path: "a",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await ((_c = (_b = (_a = routes === null || routes === void 0 ? void 0 : routes.routes) === null || _a === void 0 ? void 0 : _a.a) === null || _b === void 0 ? void 0 : _b.load) === null || _c === void 0 ? void 0 : _c.call(_b));
|
||||||
|
const dtRouter = document.createElement("developer-tools-router");
|
||||||
|
await ((_g = (_f = (_e = (_d = dtRouter === null || dtRouter === void 0 ? void 0 : dtRouter.routerOptions) === null || _d === void 0 ? void 0 : _d.routes) === null || _e === void 0 ? void 0 : _e.template) === null || _f === void 0 ? void 0 : _f.load) === null || _g === void 0 ? void 0 : _g.call(_f));
|
||||||
|
await customElements.whenDefined("developer-tools-template");
|
||||||
|
};
|
||||||
|
|
||||||
|
loadDeveloperToolsTemplate();
|
||||||
class BrowserModSettingsCard extends s {
|
class BrowserModSettingsCard extends s {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
@ -92,6 +109,7 @@ class BrowserModSettingsCard extends s {
|
|||||||
}
|
}
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
window.browser_mod.addEventListener("browser-mod-config-update", () => this.requestUpdate());
|
window.browser_mod.addEventListener("browser-mod-config-update", () => this.requestUpdate());
|
||||||
|
window.browser_mod.addEventListener("browser-mod-favicon-update", () => this.requestUpdate());
|
||||||
}
|
}
|
||||||
_handleSwitchTab(ev) {
|
_handleSwitchTab(ev) {
|
||||||
this._selectedTab = parseInt(ev.detail.index, 10);
|
this._selectedTab = parseInt(ev.detail.index, 10);
|
||||||
@ -101,12 +119,30 @@ class BrowserModSettingsCard extends s {
|
|||||||
return $ `
|
return $ `
|
||||||
<ha-card header="Frontend settings" outlined>
|
<ha-card header="Frontend settings" outlined>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
<p>
|
||||||
|
Please note: Those settings severely change the way the Home
|
||||||
|
Assistant frontend works and looks. It is very easy to forget that
|
||||||
|
you made a setting here when you switch devices or user.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Do not report any issues to Home Assistant before clearing
|
||||||
|
<b>EVERY</b> setting here and thouroghly clearing all your browser
|
||||||
|
caches. Failure to do so means you risk wasting a lot of peoples
|
||||||
|
time, and you will be severly and rightfully ridiculed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Global settings are applied for all users and browsers.</br>
|
||||||
|
User settings are applied to the current user and overrides any Global settings.</br>
|
||||||
|
Browser settings are applied for the current browser and overrides any User or Global settings.
|
||||||
|
</p>
|
||||||
<mwc-tab-bar
|
<mwc-tab-bar
|
||||||
.activeIndex=${this._selectedTab}
|
.activeIndex=${this._selectedTab}
|
||||||
@MDCTabBar:activated=${this._handleSwitchTab}
|
@MDCTabBar:activated=${this._handleSwitchTab}
|
||||||
>
|
>
|
||||||
<mwc-tab .label=${"Browser"}></mwc-tab>
|
<mwc-tab .label=${"Browser"}></mwc-tab>
|
||||||
|
<ha-icon .icon=${"mdi:chevron-double-right"}></ha-icon>
|
||||||
<mwc-tab .label=${"User (" + this.hass.user.name + ")"}></mwc-tab>
|
<mwc-tab .label=${"User (" + this.hass.user.name + ")"}></mwc-tab>
|
||||||
|
<ha-icon .icon=${"mdi:chevron-double-right"}></ha-icon>
|
||||||
<mwc-tab .label=${"Global"}></mwc-tab>
|
<mwc-tab .label=${"Global"}></mwc-tab>
|
||||||
</mwc-tab-bar>
|
</mwc-tab-bar>
|
||||||
|
|
||||||
@ -120,7 +156,7 @@ class BrowserModSettingsCard extends s {
|
|||||||
const user = window.browser_mod.user_settings;
|
const user = window.browser_mod.user_settings;
|
||||||
const browser = window.browser_mod.browser_settings;
|
const browser = window.browser_mod.browser_settings;
|
||||||
const current = { global, user, browser }[level];
|
const current = { global, user, browser }[level];
|
||||||
const DESC_BOOLEAN = (val) => ({ true: "Enabled", false: "Disabled", undefined: "unset" }[String(val)]);
|
const DESC_BOOLEAN = (val) => ({ true: "Enabled", false: "Disabled", undefined: "Unset" }[String(val)]);
|
||||||
const DESC_SET_UNSET = (val) => (val === undefined ? "Unset" : "Set");
|
const DESC_SET_UNSET = (val) => (val === undefined ? "Unset" : "Set");
|
||||||
const OVERRIDDEN = (key) => {
|
const OVERRIDDEN = (key) => {
|
||||||
if (level !== "browser" && browser[key] !== undefined)
|
if (level !== "browser" && browser[key] !== undefined)
|
||||||
@ -129,112 +165,133 @@ class BrowserModSettingsCard extends s {
|
|||||||
return $ `<br />Overridden by user setting`;
|
return $ `<br />Overridden by user setting`;
|
||||||
};
|
};
|
||||||
return $ `
|
return $ `
|
||||||
<ha-settings-row>
|
<div class="box">
|
||||||
<span slot="heading">Hide Sidebar</span>
|
<ha-settings-row>
|
||||||
<span slot="description">Hide the sidebar and hamburger menu</span>
|
<span slot="heading">Favicon template</span>
|
||||||
Currenty: ${DESC_BOOLEAN(current.hideSidebar)}
|
${OVERRIDDEN("faviconTemplate")}
|
||||||
${OVERRIDDEN("hideSidebar")}
|
<img src="${window.browser_mod._currentFavicon}" class="favicon" />
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
<ha-settings-row>
|
<ha-code-editor
|
||||||
<mwc-button
|
.hass=${this.hass}
|
||||||
@click=${() => window.browser_mod.set_setting("hideSidebar", true, level)}
|
.value=${current.faviconTemplate}
|
||||||
>
|
@value-changed=${(ev) => {
|
||||||
Enable
|
const tpl = ev.detail.value || undefined;
|
||||||
</mwc-button>
|
window.browser_mod.set_setting("faviconTemplate", tpl, level);
|
||||||
<mwc-button
|
}}
|
||||||
@click=${() => window.browser_mod.set_setting("hideSidebar", false, level)}
|
></ha-code-editor>
|
||||||
>
|
<ha-settings-row>
|
||||||
Disable
|
<mwc-button
|
||||||
</mwc-button>
|
@click=${() => window.browser_mod.set_setting("faviconTemplate", undefined, level)}
|
||||||
<mwc-button
|
>
|
||||||
@click=${() => window.browser_mod.set_setting("hideSidebar", undefined, level)}
|
Clear
|
||||||
>
|
</mwc-button>
|
||||||
Clear
|
</ha-settings-row>
|
||||||
</mwc-button>
|
|
||||||
</ha-settings-row>
|
|
||||||
|
|
||||||
<ha-settings-row>
|
<div class="separator"></div>
|
||||||
<span slot="heading">Hide Header</span>
|
|
||||||
<span slot="description">Hide the header on all pages</span>
|
|
||||||
Currenty: ${DESC_BOOLEAN(current.hideHeader)}
|
|
||||||
${OVERRIDDEN("hideHeader")}
|
|
||||||
</ha-settings-row>
|
|
||||||
<ha-settings-row>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() => window.browser_mod.set_setting("hideHeader", true, level)}
|
|
||||||
>
|
|
||||||
Enable
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() => window.browser_mod.set_setting("hideHeader", false, level)}
|
|
||||||
>
|
|
||||||
Disable
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() => window.browser_mod.set_setting("hideHeader", undefined, level)}
|
|
||||||
>
|
|
||||||
Clear
|
|
||||||
</mwc-button>
|
|
||||||
</ha-settings-row>
|
|
||||||
|
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
<span slot="heading">Sidebar order</span>
|
<span slot="heading">Hide Sidebar</span>
|
||||||
<span slot="description">Order and visibility of sidebar buttons</span>
|
<span slot="description">Hide the sidebar and hamburger menu</span>
|
||||||
Currenty: ${DESC_SET_UNSET(current.sidebarPanelOrder)}
|
Currenty: ${DESC_BOOLEAN(current.hideSidebar)}
|
||||||
${OVERRIDDEN("sidebarPanelOrder")}
|
${OVERRIDDEN("hideSidebar")}
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@click=${() => {
|
@click=${() => window.browser_mod.set_setting("hideSidebar", true, level)}
|
||||||
|
>
|
||||||
|
Enable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => window.browser_mod.set_setting("hideSidebar", false, level)}
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => window.browser_mod.set_setting("hideSidebar", undefined, level)}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="heading">Hide Header</span>
|
||||||
|
<span slot="description">Hide the header on all pages</span>
|
||||||
|
Currenty: ${DESC_BOOLEAN(current.hideHeader)}
|
||||||
|
${OVERRIDDEN("hideHeader")}
|
||||||
|
</ha-settings-row>
|
||||||
|
<ha-settings-row>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => window.browser_mod.set_setting("hideHeader", true, level)}
|
||||||
|
>
|
||||||
|
Enable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => window.browser_mod.set_setting("hideHeader", false, level)}
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => window.browser_mod.set_setting("hideHeader", undefined, level)}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="heading">Sidebar order</span>
|
||||||
|
<span slot="description"
|
||||||
|
>Order and visibility of sidebar buttons</span
|
||||||
|
>
|
||||||
|
Currenty: ${DESC_SET_UNSET(current.sidebarPanelOrder)}
|
||||||
|
${OVERRIDDEN("sidebarPanelOrder")}
|
||||||
|
</ha-settings-row>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="description">
|
||||||
|
Clearing this does NOT restore the original button order.
|
||||||
|
</span>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => {
|
||||||
window.browser_mod.set_setting("sidebarPanelOrder", localStorage.getItem("sidebarPanelOrder"), level);
|
window.browser_mod.set_setting("sidebarPanelOrder", localStorage.getItem("sidebarPanelOrder"), level);
|
||||||
window.browser_mod.set_setting("sidebarHiddenPanels", localStorage.getItem("sidebarHiddenPanels"), level);
|
window.browser_mod.set_setting("sidebarHiddenPanels", localStorage.getItem("sidebarHiddenPanels"), level);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Set
|
Set
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
window.browser_mod.set_setting("sidebarPanelOrder", undefined, level);
|
window.browser_mod.set_setting("sidebarPanelOrder", undefined, level);
|
||||||
window.browser_mod.set_setting("sidebarHiddenPanels", undefined, level);
|
window.browser_mod.set_setting("sidebarHiddenPanels", undefined, level);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
_render_user() {
|
static get styles() {
|
||||||
return $ `
|
return r$2 `
|
||||||
User
|
.box {
|
||||||
<ha-settings-row>
|
border: 1px solid var(--divider-color);
|
||||||
<span slot="heading">Kiosk mode</span>
|
padding: 8px;
|
||||||
<span slot="description"> Hide sidebar and header </span>
|
}
|
||||||
Currenty: Overridden
|
.separator {
|
||||||
</ha-settings-row>
|
border-bottom: 1px solid var(--divider-color);
|
||||||
<ha-settings-row>
|
margin: 0 -8px;
|
||||||
<span slot="heading">Set screensaver</span>
|
}
|
||||||
<span slot="description"> Set screensaver card </span>
|
img.favicon {
|
||||||
<mwc-button>Enable</mwc-button>
|
width: 64px;
|
||||||
<mwc-button>Disable</mwc-button>
|
height: 64px;
|
||||||
<mwc-button>Clear</mwc-button>
|
margin-left: 16px;
|
||||||
</ha-settings-row>
|
}
|
||||||
`;
|
mwc-tab-bar ha-icon {
|
||||||
}
|
display: flex;
|
||||||
_render_browser() {
|
align-items: center;
|
||||||
return $ `
|
}
|
||||||
Browser
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading">Kiosk mode</span>
|
|
||||||
<span slot="description"> Hide sidebar and header </span>
|
|
||||||
Currenty: Overridden
|
|
||||||
</ha-settings-row>
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading">Set screensaver</span>
|
|
||||||
<span slot="description"> Set screensaver card </span>
|
|
||||||
<mwc-button>Enable</mwc-button>
|
|
||||||
<mwc-button>Disable</mwc-button>
|
|
||||||
<mwc-button>Clear</mwc-button>
|
|
||||||
</ha-settings-row>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,7 +304,7 @@ __decorate([
|
|||||||
customElements.define("browser-mod-settings-card", BrowserModSettingsCard);
|
customElements.define("browser-mod-settings-card", BrowserModSettingsCard);
|
||||||
|
|
||||||
const bmWindow = window;
|
const bmWindow = window;
|
||||||
loadDevTools().then(() => {
|
loadConfigDashboard().then(() => {
|
||||||
class BrowserModPanel extends s {
|
class BrowserModPanel extends s {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(...arguments);
|
super(...arguments);
|
||||||
|
@ -16,6 +16,8 @@ class Settings:
|
|||||||
defaultPanel = attr.ib(type=str, default=None)
|
defaultPanel = attr.ib(type=str, default=None)
|
||||||
sidebarPanelOrder = attr.ib(type=list, default=None)
|
sidebarPanelOrder = attr.ib(type=list, default=None)
|
||||||
sidebarHiddenPanels = attr.ib(type=list, default=None)
|
sidebarHiddenPanels = attr.ib(type=list, default=None)
|
||||||
|
faviconTemplate = attr.ib(type=str, default=None)
|
||||||
|
titleTemplate = attr.ib(type=str, default=None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, data):
|
def from_dict(cls, data):
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { LitElement, html, css } from "lit";
|
import { LitElement, html, css } from "lit";
|
||||||
import { property } from "lit/decorators.js";
|
import { property } from "lit/decorators.js";
|
||||||
import { loadDevTools } from "../helpers";
|
import { loadConfigDashboard } from "../helpers";
|
||||||
import { loadHaForm } from "../helpers";
|
import { loadHaForm } from "../helpers";
|
||||||
|
|
||||||
import "./settings-card";
|
import "./settings-card";
|
||||||
|
|
||||||
const bmWindow = window as any;
|
const bmWindow = window as any;
|
||||||
|
|
||||||
loadDevTools().then(() => {
|
loadConfigDashboard().then(() => {
|
||||||
class BrowserModPanel extends LitElement {
|
class BrowserModPanel extends LitElement {
|
||||||
@property() hass;
|
@property() hass;
|
||||||
@property() narrow;
|
@property() narrow;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { LitElement, html, css } from "lit";
|
import { LitElement, html, css } from "lit";
|
||||||
import { property, state } from "lit/decorators.js";
|
import { property, state } from "lit/decorators.js";
|
||||||
|
import { loadDeveloperToolsTemplate } from "../helpers";
|
||||||
|
|
||||||
|
loadDeveloperToolsTemplate();
|
||||||
|
|
||||||
class BrowserModSettingsCard extends LitElement {
|
class BrowserModSettingsCard extends LitElement {
|
||||||
@property() hass;
|
@property() hass;
|
||||||
@ -10,6 +13,9 @@ class BrowserModSettingsCard extends LitElement {
|
|||||||
window.browser_mod.addEventListener("browser-mod-config-update", () =>
|
window.browser_mod.addEventListener("browser-mod-config-update", () =>
|
||||||
this.requestUpdate()
|
this.requestUpdate()
|
||||||
);
|
);
|
||||||
|
window.browser_mod.addEventListener("browser-mod-favicon-update", () =>
|
||||||
|
this.requestUpdate()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleSwitchTab(ev: CustomEvent) {
|
_handleSwitchTab(ev: CustomEvent) {
|
||||||
@ -21,12 +27,30 @@ class BrowserModSettingsCard extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<ha-card header="Frontend settings" outlined>
|
<ha-card header="Frontend settings" outlined>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
<p>
|
||||||
|
Please note: Those settings severely change the way the Home
|
||||||
|
Assistant frontend works and looks. It is very easy to forget that
|
||||||
|
you made a setting here when you switch devices or user.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Do not report any issues to Home Assistant before clearing
|
||||||
|
<b>EVERY</b> setting here and thouroghly clearing all your browser
|
||||||
|
caches. Failure to do so means you risk wasting a lot of peoples
|
||||||
|
time, and you will be severly and rightfully ridiculed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Global settings are applied for all users and browsers.</br>
|
||||||
|
User settings are applied to the current user and overrides any Global settings.</br>
|
||||||
|
Browser settings are applied for the current browser and overrides any User or Global settings.
|
||||||
|
</p>
|
||||||
<mwc-tab-bar
|
<mwc-tab-bar
|
||||||
.activeIndex=${this._selectedTab}
|
.activeIndex=${this._selectedTab}
|
||||||
@MDCTabBar:activated=${this._handleSwitchTab}
|
@MDCTabBar:activated=${this._handleSwitchTab}
|
||||||
>
|
>
|
||||||
<mwc-tab .label=${"Browser"}></mwc-tab>
|
<mwc-tab .label=${"Browser"}></mwc-tab>
|
||||||
|
<ha-icon .icon=${"mdi:chevron-double-right"}></ha-icon>
|
||||||
<mwc-tab .label=${"User (" + this.hass.user.name + ")"}></mwc-tab>
|
<mwc-tab .label=${"User (" + this.hass.user.name + ")"}></mwc-tab>
|
||||||
|
<ha-icon .icon=${"mdi:chevron-double-right"}></ha-icon>
|
||||||
<mwc-tab .label=${"Global"}></mwc-tab>
|
<mwc-tab .label=${"Global"}></mwc-tab>
|
||||||
</mwc-tab-bar>
|
</mwc-tab-bar>
|
||||||
|
|
||||||
@ -43,7 +67,7 @@ class BrowserModSettingsCard extends LitElement {
|
|||||||
const current = { global, user, browser }[level];
|
const current = { global, user, browser }[level];
|
||||||
|
|
||||||
const DESC_BOOLEAN = (val) =>
|
const DESC_BOOLEAN = (val) =>
|
||||||
({ true: "Enabled", false: "Disabled", undefined: "unset" }[String(val)]);
|
({ true: "Enabled", false: "Disabled", undefined: "Unset" }[String(val)]);
|
||||||
const DESC_SET_UNSET = (val) => (val === undefined ? "Unset" : "Set");
|
const DESC_SET_UNSET = (val) => (val === undefined ? "Unset" : "Set");
|
||||||
const OVERRIDDEN = (key) => {
|
const OVERRIDDEN = (key) => {
|
||||||
if (level !== "browser" && browser[key] !== undefined)
|
if (level !== "browser" && browser[key] !== undefined)
|
||||||
@ -53,136 +77,161 @@ class BrowserModSettingsCard extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-settings-row>
|
<div class="box">
|
||||||
<span slot="heading">Hide Sidebar</span>
|
<ha-settings-row>
|
||||||
<span slot="description">Hide the sidebar and hamburger menu</span>
|
<span slot="heading">Favicon template</span>
|
||||||
Currenty: ${DESC_BOOLEAN(current.hideSidebar)}
|
${OVERRIDDEN("faviconTemplate")}
|
||||||
${OVERRIDDEN("hideSidebar")}
|
<img src="${window.browser_mod._currentFavicon}" class="favicon" />
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
<ha-settings-row>
|
<ha-code-editor
|
||||||
<mwc-button
|
.hass=${this.hass}
|
||||||
@click=${() =>
|
.value=${current.faviconTemplate}
|
||||||
window.browser_mod.set_setting("hideSidebar", true, level)}
|
@value-changed=${(ev) => {
|
||||||
>
|
const tpl = ev.detail.value || undefined;
|
||||||
Enable
|
window.browser_mod.set_setting("faviconTemplate", tpl, level);
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() =>
|
|
||||||
window.browser_mod.set_setting("hideSidebar", false, level)}
|
|
||||||
>
|
|
||||||
Disable
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() =>
|
|
||||||
window.browser_mod.set_setting("hideSidebar", undefined, level)}
|
|
||||||
>
|
|
||||||
Clear
|
|
||||||
</mwc-button>
|
|
||||||
</ha-settings-row>
|
|
||||||
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading">Hide Header</span>
|
|
||||||
<span slot="description">Hide the header on all pages</span>
|
|
||||||
Currenty: ${DESC_BOOLEAN(current.hideHeader)}
|
|
||||||
${OVERRIDDEN("hideHeader")}
|
|
||||||
</ha-settings-row>
|
|
||||||
<ha-settings-row>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() =>
|
|
||||||
window.browser_mod.set_setting("hideHeader", true, level)}
|
|
||||||
>
|
|
||||||
Enable
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() =>
|
|
||||||
window.browser_mod.set_setting("hideHeader", false, level)}
|
|
||||||
>
|
|
||||||
Disable
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() =>
|
|
||||||
window.browser_mod.set_setting("hideHeader", undefined, level)}
|
|
||||||
>
|
|
||||||
Clear
|
|
||||||
</mwc-button>
|
|
||||||
</ha-settings-row>
|
|
||||||
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading">Sidebar order</span>
|
|
||||||
<span slot="description">Order and visibility of sidebar buttons</span>
|
|
||||||
Currenty: ${DESC_SET_UNSET(current.sidebarPanelOrder)}
|
|
||||||
${OVERRIDDEN("sidebarPanelOrder")}
|
|
||||||
</ha-settings-row>
|
|
||||||
<ha-settings-row>
|
|
||||||
<mwc-button
|
|
||||||
@click=${() => {
|
|
||||||
window.browser_mod.set_setting(
|
|
||||||
"sidebarPanelOrder",
|
|
||||||
localStorage.getItem("sidebarPanelOrder"),
|
|
||||||
level
|
|
||||||
);
|
|
||||||
window.browser_mod.set_setting(
|
|
||||||
"sidebarHiddenPanels",
|
|
||||||
localStorage.getItem("sidebarHiddenPanels"),
|
|
||||||
level
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
>
|
></ha-code-editor>
|
||||||
Set
|
<ha-settings-row>
|
||||||
</mwc-button>
|
<mwc-button
|
||||||
<mwc-button
|
@click=${() =>
|
||||||
@click=${() => {
|
window.browser_mod.set_setting(
|
||||||
window.browser_mod.set_setting(
|
"faviconTemplate",
|
||||||
"sidebarPanelOrder",
|
undefined,
|
||||||
undefined,
|
level
|
||||||
level
|
)}
|
||||||
);
|
>
|
||||||
window.browser_mod.set_setting(
|
Clear
|
||||||
"sidebarHiddenPanels",
|
</mwc-button>
|
||||||
undefined,
|
</ha-settings-row>
|
||||||
level
|
|
||||||
);
|
<div class="separator"></div>
|
||||||
}}
|
|
||||||
>
|
<ha-settings-row>
|
||||||
Clear
|
<span slot="heading">Hide Sidebar</span>
|
||||||
</mwc-button>
|
<span slot="description">Hide the sidebar and hamburger menu</span>
|
||||||
</ha-settings-row>
|
Currenty: ${DESC_BOOLEAN(current.hideSidebar)}
|
||||||
|
${OVERRIDDEN("hideSidebar")}
|
||||||
|
</ha-settings-row>
|
||||||
|
<ha-settings-row>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() =>
|
||||||
|
window.browser_mod.set_setting("hideSidebar", true, level)}
|
||||||
|
>
|
||||||
|
Enable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() =>
|
||||||
|
window.browser_mod.set_setting("hideSidebar", false, level)}
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() =>
|
||||||
|
window.browser_mod.set_setting("hideSidebar", undefined, level)}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="heading">Hide Header</span>
|
||||||
|
<span slot="description">Hide the header on all pages</span>
|
||||||
|
Currenty: ${DESC_BOOLEAN(current.hideHeader)}
|
||||||
|
${OVERRIDDEN("hideHeader")}
|
||||||
|
</ha-settings-row>
|
||||||
|
<ha-settings-row>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() =>
|
||||||
|
window.browser_mod.set_setting("hideHeader", true, level)}
|
||||||
|
>
|
||||||
|
Enable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() =>
|
||||||
|
window.browser_mod.set_setting("hideHeader", false, level)}
|
||||||
|
>
|
||||||
|
Disable
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() =>
|
||||||
|
window.browser_mod.set_setting("hideHeader", undefined, level)}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="heading">Sidebar order</span>
|
||||||
|
<span slot="description"
|
||||||
|
>Order and visibility of sidebar buttons</span
|
||||||
|
>
|
||||||
|
Currenty: ${DESC_SET_UNSET(current.sidebarPanelOrder)}
|
||||||
|
${OVERRIDDEN("sidebarPanelOrder")}
|
||||||
|
</ha-settings-row>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="description">
|
||||||
|
Clearing this does NOT restore the original button order.
|
||||||
|
</span>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => {
|
||||||
|
window.browser_mod.set_setting(
|
||||||
|
"sidebarPanelOrder",
|
||||||
|
localStorage.getItem("sidebarPanelOrder"),
|
||||||
|
level
|
||||||
|
);
|
||||||
|
window.browser_mod.set_setting(
|
||||||
|
"sidebarHiddenPanels",
|
||||||
|
localStorage.getItem("sidebarHiddenPanels"),
|
||||||
|
level
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
@click=${() => {
|
||||||
|
window.browser_mod.set_setting(
|
||||||
|
"sidebarPanelOrder",
|
||||||
|
undefined,
|
||||||
|
level
|
||||||
|
);
|
||||||
|
window.browser_mod.set_setting(
|
||||||
|
"sidebarHiddenPanels",
|
||||||
|
undefined,
|
||||||
|
level
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_render_user() {
|
static get styles() {
|
||||||
return html`
|
return css`
|
||||||
User
|
.box {
|
||||||
<ha-settings-row>
|
border: 1px solid var(--divider-color);
|
||||||
<span slot="heading">Kiosk mode</span>
|
padding: 8px;
|
||||||
<span slot="description"> Hide sidebar and header </span>
|
}
|
||||||
Currenty: Overridden
|
.separator {
|
||||||
</ha-settings-row>
|
border-bottom: 1px solid var(--divider-color);
|
||||||
<ha-settings-row>
|
margin: 0 -8px;
|
||||||
<span slot="heading">Set screensaver</span>
|
}
|
||||||
<span slot="description"> Set screensaver card </span>
|
img.favicon {
|
||||||
<mwc-button>Enable</mwc-button>
|
width: 64px;
|
||||||
<mwc-button>Disable</mwc-button>
|
height: 64px;
|
||||||
<mwc-button>Clear</mwc-button>
|
margin-left: 16px;
|
||||||
</ha-settings-row>
|
}
|
||||||
`;
|
mwc-tab-bar ha-icon {
|
||||||
}
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
_render_browser() {
|
}
|
||||||
return html`
|
|
||||||
Browser
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading">Kiosk mode</span>
|
|
||||||
<span slot="description"> Hide sidebar and header </span>
|
|
||||||
Currenty: Overridden
|
|
||||||
</ha-settings-row>
|
|
||||||
<ha-settings-row>
|
|
||||||
<span slot="heading">Set screensaver</span>
|
|
||||||
<span slot="description"> Set screensaver card </span>
|
|
||||||
<mwc-button>Enable</mwc-button>
|
|
||||||
<mwc-button>Disable</mwc-button>
|
|
||||||
<mwc-button>Clear</mwc-button>
|
|
||||||
</ha-settings-row>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ export const loadHaForm = async () => {
|
|||||||
|
|
||||||
// Loads in ha-config-dashboard which is used to copy styling
|
// Loads in ha-config-dashboard which is used to copy styling
|
||||||
// Also provides ha-settings-row
|
// Also provides ha-settings-row
|
||||||
export const loadDevTools = async () => {
|
export const loadConfigDashboard = async () => {
|
||||||
await customElements.whenDefined("partial-panel-resolver");
|
await customElements.whenDefined("partial-panel-resolver");
|
||||||
const ppResolver = document.createElement("partial-panel-resolver");
|
const ppResolver = document.createElement("partial-panel-resolver");
|
||||||
const routes = (ppResolver as any).getRoutes([
|
const routes = (ppResolver as any).getRoutes([
|
||||||
@ -99,8 +99,24 @@ export const loadDevTools = async () => {
|
|||||||
]);
|
]);
|
||||||
await routes?.routes?.a?.load?.();
|
await routes?.routes?.a?.load?.();
|
||||||
await customElements.whenDefined("ha-panel-config");
|
await customElements.whenDefined("ha-panel-config");
|
||||||
const configRouter = document.createElement("ha-panel-config");
|
const configRouter: any = document.createElement("ha-panel-config");
|
||||||
await (configRouter as any)?.routerOptions?.routes?.dashboard?.load?.(); // Load ha-config-dashboard
|
await configRouter?.routerOptions?.routes?.dashboard?.load?.(); // Load ha-config-dashboard
|
||||||
await (configRouter as any)?.routerOptions?.routes?.cloud?.load?.(); // Load ha-settings-row
|
await configRouter?.routerOptions?.routes?.cloud?.load?.(); // Load ha-settings-row
|
||||||
await customElements.whenDefined("ha-config-dashboard");
|
await customElements.whenDefined("ha-config-dashboard");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const loadDeveloperToolsTemplate = async () => {
|
||||||
|
await customElements.whenDefined("partial-panel-resolver");
|
||||||
|
await customElements.whenDefined("partial-panel-resolver");
|
||||||
|
const ppResolver = document.createElement("partial-panel-resolver");
|
||||||
|
const routes = (ppResolver as any).getRoutes([
|
||||||
|
{
|
||||||
|
component_name: "developer-tools",
|
||||||
|
url_path: "a",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await routes?.routes?.a?.load?.();
|
||||||
|
const dtRouter: any = document.createElement("developer-tools-router");
|
||||||
|
await dtRouter?.routerOptions?.routes?.template?.load?.();
|
||||||
|
await customElements.whenDefined("developer-tools-template");
|
||||||
|
};
|
||||||
|
@ -2,10 +2,16 @@ import { selectTree } from "../helpers";
|
|||||||
|
|
||||||
export const AutoSettingsMixin = (SuperClass) => {
|
export const AutoSettingsMixin = (SuperClass) => {
|
||||||
return class AutoSettingsMixinClass extends SuperClass {
|
return class AutoSettingsMixinClass extends SuperClass {
|
||||||
|
_faviconTemplateSubscription;
|
||||||
|
_titleTemplateSubscription;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._auto_settings_setup();
|
this._auto_settings_setup();
|
||||||
|
this.addEventListener("browser-mod-config-update", () =>
|
||||||
|
this._auto_settings_setup()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _auto_settings_setup() {
|
async _auto_settings_setup() {
|
||||||
@ -13,6 +19,7 @@ export const AutoSettingsMixin = (SuperClass) => {
|
|||||||
|
|
||||||
const settings = this.settings;
|
const settings = this.settings;
|
||||||
|
|
||||||
|
// Sidebar panel order and hiding
|
||||||
if (settings.sidebarPanelOrder) {
|
if (settings.sidebarPanelOrder) {
|
||||||
localStorage.setItem("sidebarPanelOrder", settings.sidebarPanelOrder);
|
localStorage.setItem("sidebarPanelOrder", settings.sidebarPanelOrder);
|
||||||
}
|
}
|
||||||
@ -23,6 +30,7 @@ export const AutoSettingsMixin = (SuperClass) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide sidebar
|
||||||
if (settings.hideSidebar === true) {
|
if (settings.hideSidebar === true) {
|
||||||
selectTree(
|
selectTree(
|
||||||
document.body,
|
document.body,
|
||||||
@ -33,6 +41,8 @@ export const AutoSettingsMixin = (SuperClass) => {
|
|||||||
"home-assistant$home-assistant-main$app-drawer-layout app-drawer"
|
"home-assistant$home-assistant-main$app-drawer-layout app-drawer"
|
||||||
).then((el) => el.remove());
|
).then((el) => el.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide header
|
||||||
if (settings.hideHeader === true) {
|
if (settings.hideHeader === true) {
|
||||||
customElements.whenDefined("app-header-layout").then(() => {
|
customElements.whenDefined("app-header-layout").then(() => {
|
||||||
const appHeader = customElements.get("app-header").prototype;
|
const appHeader = customElements.get("app-header").prototype;
|
||||||
@ -43,6 +53,45 @@ export const AutoSettingsMixin = (SuperClass) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Favicon template
|
||||||
|
if (settings.faviconTemplate !== undefined) {
|
||||||
|
(async () => {
|
||||||
|
if (this._faviconTemplateSubscription) {
|
||||||
|
this._faviconTemplateSubscription();
|
||||||
|
}
|
||||||
|
this._faviconTemplateSubscription = undefined;
|
||||||
|
this._faviconTemplateSubscription =
|
||||||
|
await this.connection.subscribeMessage(this._updateFavicon, {
|
||||||
|
type: "render_template",
|
||||||
|
template: settings.faviconTemplate,
|
||||||
|
variables: {},
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title template
|
||||||
|
if (settings.titleTemplate !== undefined) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get _currentFavicon() {
|
||||||
|
const link: any = document.head.querySelector("link[rel~='icon']");
|
||||||
|
return link?.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateFavicon({ result }) {
|
||||||
|
// TEMP: Template for testing
|
||||||
|
/*
|
||||||
|
{% if is_state("light.bed_light", "on") %}
|
||||||
|
/local/workspace/test/icons/green.png
|
||||||
|
{% else %}
|
||||||
|
/local/workspace/test/icons/red.png
|
||||||
|
{% endif %}
|
||||||
|
*/
|
||||||
|
const link: any = document.head.querySelector("link[rel~='icon']");
|
||||||
|
link.href = result;
|
||||||
|
window.browser_mod.fireEvent("browser-mod-favicon-update");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -55,9 +55,9 @@ import { AutoSettingsMixin } from "./auto-settings";
|
|||||||
x Kiosk mode
|
x Kiosk mode
|
||||||
- Default panel?
|
- Default panel?
|
||||||
- Screensaver?
|
- Screensaver?
|
||||||
- Tweaks
|
x Favicon templates
|
||||||
- Favicon templates
|
|
||||||
- Title templates
|
- Title templates
|
||||||
|
- Tweaks
|
||||||
- Quickbar tweaks (ctrl+enter)?
|
- Quickbar tweaks (ctrl+enter)?
|
||||||
- Video player?
|
- Video player?
|
||||||
- Media_seek
|
- Media_seek
|
||||||
|
Loading…
x
Reference in New Issue
Block a user