` Component
-
-## Usage
-This applet uses the navigation.connection API to get the round-trip time and display it as milliseconds. If the API is not available, it will display `NaN`.
-
-### Script
-```html
-
-```
-
-### Body
-```html
-
-```
-
-
-
-## Attributes
-* `remove-when-unsupported`: If set, the applet will not be rendered if the API is not available.
\ No newline at end of file
diff --git a/src/components/applet-battery.js b/src/components/applet-battery.js
new file mode 100644
index 0000000..50f5ee1
--- /dev/null
+++ b/src/components/applet-battery.js
@@ -0,0 +1,177 @@
+class AppletBattery extends HTMLElement {
+ constructor() {
+ super();
+ this.attachShadow({ mode: 'open' });
+
+ this.title = 'Battery status: fetching...';
+ this.text = '?';
+ this.charging = false;
+ this.fill = 0;
+ this.percent = undefined;
+
+ this.render();
+ this.init();
+ }
+
+ connectedCallback() {
+ this.render();
+ }
+
+ css() {
+ return `
+ :host {
+ display: block;
+ user-select: none;
+ aspect-ratio: 1 / 1;
+ }
+
+ svg {
+ height: 100%;
+ fill: white;
+ }
+ `;
+ }
+
+ template() {
+ return `
+
+ `;
+ }
+
+ generateFillClasses() {
+ let classes = '';
+ for (let i = 1; i <= 10; i++) {
+ if (this.fill < i) {
+ classes += `.fill-${i} { display: none; }\n`;
+ }
+ }
+ return classes;
+ }
+
+ render() {
+ this.shadowRoot.innerHTML = `
+
+ ${this.template()}
+ `;
+ }
+
+ updateBatteryStatus(battery) {
+ const convertSecondsToTime = (seconds) => {
+ if (seconds === Infinity) return '∞';
+ const hours = Math.floor(seconds / 3600);
+ const minutes = Math.floor((seconds % 3600) / 60);
+ return `${hours}h ${minutes}m`;
+ };
+
+ const { level, charging } = battery;
+ const percent = Math.round(level * 100);
+ let message = `Battery status: ${percent}% `;
+
+ if (charging) {
+ message += battery.chargingTime === Infinity
+ ? 'available (plugged in)'
+ : `(${convertSecondsToTime(battery.chargingTime)} until full)`;
+ } else {
+ message += battery.dischargingTime === Infinity
+ ? 'remaining'
+ : `(${convertSecondsToTime(battery.dischargingTime)} left)`;
+ }
+
+ this.title = message;
+ this.charging = charging;
+ this.percent = percent;
+
+ if (this.content === 'percent') {
+ this.fill = 0;
+ this.text = this.percent
+ } else if (this.content === 'fill') {
+ this.text = '';
+ this.fill = Math.round(this.percent / 10);
+ }
+
+ this.render();
+ console.debug(`[setBatteryStatus] battery title updated to: '${message}'`);
+ }
+
+ init() {
+ if ('getBattery' in navigator) {
+ navigator.getBattery().then((battery) => {
+ battery.addEventListener('chargingchange', () => this.updateBatteryStatus(battery));
+ battery.addEventListener('chargingtimechange', () => this.updateBatteryStatus(battery));
+ battery.addEventListener('dischargingtimechange', () => this.updateBatteryStatus(battery));
+ battery.addEventListener('levelchange', () => this.updateBatteryStatus(battery));
+ this.updateBatteryStatus(battery);
+ });
+ } else {
+ this.title = 'Battery API is not supported';
+ this.charging = false;
+ this.fill = 0;
+ this.text = 'X';
+ this.render();
+ }
+ }
+
+ static get observedAttributes() {
+ return ['content'];
+ }
+
+ attributeChangedCallback(name, oldValue, newValue) {
+ if (name === 'content' && (newValue !== 'fill' && newValue !== 'percent')) {
+ console.warn(`BatteryApplet: Invalid attribute for 'content', value: '${newValue}'`);
+ } else {
+ console.debug(`BatteryApplet: Attribute 'data-content' changed from '${oldValue}' to '${newValue}'`);
+ }
+ this.render();
+ }
+
+ get content() {
+ const value = this.getAttribute('content');
+ if (value !== 'fill' && value !== 'percent') {
+ return 'fill';
+ }
+ return value;
+ }
+}
+
+customElements.define('applet-battery', AppletBattery);
\ No newline at end of file
diff --git a/src/components/applet/cpu.js b/src/components/applet-cpu.js
similarity index 85%
rename from src/components/applet/cpu.js
rename to src/components/applet-cpu.js
index f03a4fd..059edf1 100644
--- a/src/components/applet/cpu.js
+++ b/src/components/applet-cpu.js
@@ -1,4 +1,4 @@
-class CPUApplet extends HTMLElement {
+class AppletCPU extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -31,16 +31,9 @@ class CPUApplet extends HTMLElement {
fill: var(--icon-color);
}
- p {
- margin: 0;
- padding: 0;
- width: min-content;
- height: 100%;
+ span {
color: var(--text-color);
text-align: center;
- display: flex;
- align-items: center;
- justify-content: center;
}
`;
}
@@ -50,7 +43,7 @@ class CPUApplet extends HTMLElement {
- ${this.percent}%
+ ${this.percent}%
`;
}
@@ -67,4 +60,4 @@ class CPUApplet extends HTMLElement {
}
}
-customElements.define('applet-cpu', CPUApplet);
\ No newline at end of file
+customElements.define('applet-cpu', AppletCPU);
\ No newline at end of file
diff --git a/src/components/applet/network-down.js b/src/components/applet-network-down.js
similarity index 75%
rename from src/components/applet/network-down.js
rename to src/components/applet-network-down.js
index 7d11113..79e53a7 100644
--- a/src/components/applet/network-down.js
+++ b/src/components/applet-network-down.js
@@ -1,4 +1,4 @@
-class NetworkDown extends HTMLElement {
+class AppletNetworkDown extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -24,6 +24,7 @@ class NetworkDown extends HTMLElement {
align-items: center;
justify-content: center;
user-select: none;
+ height: 100%;
}
svg {
@@ -36,15 +37,9 @@ class NetworkDown extends HTMLElement {
fill: none;
}
- p {
- margin: 0;
- padding: 0;
- height: 100%;
+ span {
color: var(--text-color);
text-align: center;
- display: flex;
- align-items: center;
- justify-content: center;
}
`;
}
@@ -54,7 +49,7 @@ class NetworkDown extends HTMLElement {
- ${ typeof(this.speed) === 'number' ? `${this.speed} Mbps
` : `${this.speed}
` }
+ ${ typeof(this.speed) === 'number' ? `${this.speed} Mbps` : `${this.speed}` }
`;
}
@@ -87,23 +82,24 @@ class NetworkDown extends HTMLElement {
} else {
this.speed = 'NaN';
this.render();
- if(this.removeWhenUnsupported === true) {
+ if (this.removeIfUnsupported) {
+ console.log('Network Down: Removing unsupported component.');
this.remove();
}
}
}
- set removeWhenUnsupported(value) {
+ set removeIfUnsupported(value) {
if (value) {
- this.setAttribute('remove-when-unsupported', '');
+ this.setAttribute('data-remove-if-unsupported', '');
} else {
- this.removeAttribute('remove-when-unsupported');
+ this.removeAttribute('data-remove-if-unsupported');
}
}
- get removeWhenUnsupported() {
- return this.hasAttribute('remove-when-unsupported');
+ get removeIfUnsupported() {
+ return this.hasAttribute('data-remove-if-unsupported');
}
}
-customElements.define('applet-network_down', NetworkDown);
\ No newline at end of file
+customElements.define('applet-network-down', AppletNetworkDown);
\ No newline at end of file
diff --git a/src/components/applet/network-rtt.js b/src/components/applet-network-rtt.js
similarity index 75%
rename from src/components/applet/network-rtt.js
rename to src/components/applet-network-rtt.js
index 7d7eb4d..25158b2 100644
--- a/src/components/applet/network-rtt.js
+++ b/src/components/applet-network-rtt.js
@@ -1,4 +1,4 @@
-class NetworkRTT extends HTMLElement {
+class AppletNetworkRTT extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -24,6 +24,7 @@ class NetworkRTT extends HTMLElement {
align-items: center;
justify-content: center;
user-select: none;
+ height: 100%;
}
svg {
@@ -36,15 +37,9 @@ class NetworkRTT extends HTMLElement {
fill: none;
}
- p {
- margin: 0;
- padding: 0;
- height: 100%;
+ span {
color: var(--text-color);
text-align: center;
- display: flex;
- align-items: center;
- justify-content: center;
}
`;
}
@@ -54,7 +49,7 @@ class NetworkRTT extends HTMLElement {
- ${ typeof(this.speed) === 'number' ? `${this.speed} ms
` : `${this.speed}
` }
+ ${ typeof(this.speed) === 'number' ? `${this.speed} ms` : `${this.speed}` }
`;
}
@@ -86,7 +81,8 @@ class NetworkRTT extends HTMLElement {
} else {
this.speed = 'NaN';
this.render();
- if(this.removeWhenUnsupported === true) {
+ if (this.removeIfUnsupported) {
+ console.log('Network RTT: Removing unsupported component.');
this.remove();
}
}
@@ -94,16 +90,16 @@ class NetworkRTT extends HTMLElement {
- set removeWhenUnsupported(value) {
+ set removeIfUnsupported(value) {
if (value) {
- this.setAttribute('remove-when-unsupported', '');
+ this.setAttribute('data-remove-if-unsupported', '');
} else {
- this.removeAttribute('remove-when-unsupported');
+ this.removeAttribute('data-remove-if-unsupported');
}
}
- get removeWhenUnsupported() {
- return this.hasAttribute('remove-when-unsupported');
+ get removeIfUnsupported() {
+ return this.hasAttribute('data-remove-if-unsupported');
}
}
-customElements.define('applet-network_rtt', NetworkRTT);
\ No newline at end of file
+customElements.define('applet-network-rtt', AppletNetworkRTT);
\ No newline at end of file
diff --git a/src/components/applet/wifi.js b/src/components/applet-network.js
similarity index 88%
rename from src/components/applet/wifi.js
rename to src/components/applet-network.js
index 3098c0e..0516f6d 100644
--- a/src/components/applet/wifi.js
+++ b/src/components/applet-network.js
@@ -1,4 +1,4 @@
-class WifiApplet extends HTMLElement {
+class AppletNetwork extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -63,20 +63,25 @@ class WifiApplet extends HTMLElement {
css() {
return `
:host {
- display: inline-block;
+ display: flex;
+ justify-content: center;
+ align-items: center;
user-select: none;
+ aspect-ratio: 1 / 1;
}
img {
height: 100%;
+ min-height: 100%;
width: 100%;
+ min-width: 100%;
}
`;
}
template() {
return `
-
+
`;
}
@@ -88,4 +93,4 @@ class WifiApplet extends HTMLElement {
}
}
-customElements.define('applet-wifi', WifiApplet);
+customElements.define('applet-network', AppletNetwork);
diff --git a/src/components/applet/ram.js b/src/components/applet-ram.js
similarity index 85%
rename from src/components/applet/ram.js
rename to src/components/applet-ram.js
index bf1e146..f3a531b 100644
--- a/src/components/applet/ram.js
+++ b/src/components/applet-ram.js
@@ -1,4 +1,4 @@
-class RAMApplet extends HTMLElement {
+class AppletRAM extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -31,16 +31,9 @@ class RAMApplet extends HTMLElement {
fill: var(--icon-color);
}
- p {
- margin: 0;
- padding: 0;
- width: min-content;
- height: 100%;
+ span {
color: var(--text-color);
text-align: center;
- display: flex;
- align-items: center;
- justify-content: center;
}
`;
}
@@ -50,7 +43,7 @@ class RAMApplet extends HTMLElement {
- ${this.percent}%
+ ${this.percent}%
`;
}
@@ -67,4 +60,4 @@ class RAMApplet extends HTMLElement {
}
}
-customElements.define('applet-ram', RAMApplet);
\ No newline at end of file
+customElements.define('applet-ram', AppletRAM);
\ No newline at end of file
diff --git a/src/components/applet/sound.js b/src/components/applet-sound.js
similarity index 89%
rename from src/components/applet/sound.js
rename to src/components/applet-sound.js
index ff7a26b..ff74381 100644
--- a/src/components/applet/sound.js
+++ b/src/components/applet-sound.js
@@ -1,4 +1,4 @@
-class SoundApplet extends HTMLElement {
+class AppletSound extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -20,6 +20,7 @@ class SoundApplet extends HTMLElement {
justify-content: center;
align-items: center;
user-select: none;
+ aspect-ratio: 1 / 1;
}
svg {
@@ -65,7 +66,7 @@ class SoundApplet extends HTMLElement {
get volume() {
let volume;
- volume = this.getAttribute('volume') || 0;
+ volume = this.getAttribute('volume') || 40;
volume = parseInt(volume);
if (volume < 0) {
console.warn(`[setSpeakerVolume] the volume is smaller than 0: ${volume}\nthe volume should be between 0 and 100 (inclusive)`);
@@ -94,15 +95,4 @@ class SoundApplet extends HTMLElement {
}
}
-customElements.define('applet-sound', SoundApplet);
-
-
-/*
-
-
-
-
-
-*/
+customElements.define('applet-sound', AppletSound);
\ No newline at end of file
diff --git a/src/components/applet/battery.js b/src/components/applet/battery.js
deleted file mode 100644
index 4856d26..0000000
--- a/src/components/applet/battery.js
+++ /dev/null
@@ -1,179 +0,0 @@
-class BatteryApplet extends HTMLElement {
- constructor() {
- super();
- this.attachShadow({ mode: 'open' });
-
- this.title = 'Battery status: fetching...';
- this.charging = false;
- this.fill = 0;
- this.text = '?';
- this.content = this.getAttribute('content') || 'fill';
-
- this.render();
- this.init();
- }
-
- connectedCallback() {
- this.render();
- }
-
- css() {
- return `
- :host {
- display: block;
- user-select: none;
- }
-
- svg {
- height: 100%;
- fill: white;
- }
- `;
- }
-
- template() {
- if (this.text != '' && this.fill != 0) console.warn('BatteryApplet: text and fill are both set');
-
- return `
-
- `;
- }
-
- render() {
- this.shadowRoot.innerHTML = `
-
- ${this.template()}
- `;
- }
-
- updateBatteryStatus(battery) {
- function convertSecondsToTime(seconds) {
- if (seconds === Infinity) return '∞';
- const hours = Math.floor(seconds / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
- return `${hours}h ${minutes}m`;
- }
-
- const { level, charging } = battery;
- const percent = Math.round(level * 100);
- let message = `Battery status: ${percent}% `;
-
- if (charging) {
- if (battery.chargingTime === Infinity) {
- message += 'available (plugged in)';
- } else {
- message += `(${convertSecondsToTime(battery.chargingTime)} until full)`;
- }
- } else {
- if (battery.dischargingTime === Infinity) {
- message += 'remaining';
- } else {
- message += `(${convertSecondsToTime(battery.dischargingTime)} left)`;
- }
- }
-
- this.title = message;
- this.charging = charging;
-
- if (this.content === 'percent') {
- this.fill = 0;
- this.text = percent;
- } else {
- this.fill = Math.round(level * 10);
- this.text = '';
- }
-
- this.render();
-
- console.debug(`[setBatteryStatus] battery title updated to: '${message}'`);
- }
-
- init() {
- if ('getBattery' in navigator) {
- navigator.getBattery().then((battery) => {
- battery.addEventListener('chargingchange', () => this.updateBatteryStatus(battery));
- battery.addEventListener('chargingtimechange', () => this.updateBatteryStatus(battery));
- battery.addEventListener('dischargingtimechange', () => this.updateBatteryStatus(battery));
- battery.addEventListener('levelchange', () => this.updateBatteryStatus(battery));
- this.updateBatteryStatus(battery);
- });
- } else {
- this.title = 'Battery API is not supported';
- this.charging = false;
- this.fill = 0;
- this.text = 'X';
- this.render();
- }
- }
-
- static get observedAttributes() {
- return ['content'];
- }
-
- attributeChangedCallback(name, oldValue, newValue) {
- if (name === 'content') {
- if (newValue === 'fill' || newValue === 'percent') {
- this.content = newValue;
- console.debug(`BatteryApplet: content set to '${newValue}', value will be updated on next battery status change`);
- } else {
- console.warn(`BatteryApplet: Invalid attribute for '${name}', value: '${newValue}'`);
- }
- }
- }
-}
-
-customElements.define('applet-battery', BatteryApplet);
\ No newline at end of file
diff --git a/src/components/applet/power.js b/src/components/button/power.js
similarity index 96%
rename from src/components/applet/power.js
rename to src/components/button/power.js
index a12a724..d5f2e57 100644
--- a/src/components/applet/power.js
+++ b/src/components/button/power.js
@@ -1,4 +1,4 @@
-class PowerApplet extends HTMLElement {
+class PowerButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
@@ -11,7 +11,7 @@ class PowerApplet extends HTMLElement {
display: block;
user-select: none;
position: relative;
-
+ aspect-ratio: 1 / 1;
--hover-color: rgba(255, 255, 255, 0.2);
--active-color: rgba(255, 255, 255, 0.4);
}
@@ -21,6 +21,8 @@ class PowerApplet extends HTMLElement {
align-items: center;
justify-content: center;
border-radius: 5px;
+ margin: 0;
+ padding: 0;
height: 100%;
width: 100%;
border: none;
@@ -167,7 +169,7 @@ class PowerApplet extends HTMLElement {
document.addEventListener('serviceWorkerUpdate', () => {
this.classList.add('updateAvailable');
- console.debug('applet-power: Service Worker Update Available');
+ console.debug('button-power: Service Worker Update Available');
});
}
@@ -186,4 +188,4 @@ class PowerApplet extends HTMLElement {
}
}
-customElements.define('applet-power', PowerApplet);
+customElements.define('button-power', PowerButton);
diff --git a/src/components/buttons/lock.js b/src/components/buttons/lock.js
deleted file mode 100644
index 95cfc17..0000000
--- a/src/components/buttons/lock.js
+++ /dev/null
@@ -1,76 +0,0 @@
-class LockButton extends HTMLElement {
- constructor() {
- super();
- this.attachShadow({ mode: 'open' });
- this.render();
- }
-
- css() {
- return `
- :host {
- display: block;
- width: 100px;
- height: 100px;
- }
-
- button {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- background-color: DimGray;
- border-radius: 10px;
- height: 100%;
- width: 100%;
- border: none;
- color: white;
- font-size: 24px;
- cursor: pointer;
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
- transition: background-color 0.3s ease;
- }
-
- button:hover {
- background-color: gray;
- }
-
- button:active {
- background-color: gray;
- }
-
- svg {
- fill: white;
- height: 100%;
- width: 100%;
- }
- `;
- }
-
- template() {
- return `
-
- `;
- }
-
- render() {
- this.shadowRoot.innerHTML = `
-
- ${this.template()}
- `;
- }
-
- connectedCallback() {
- this.shadowRoot.querySelector('button').addEventListener('click', this.handle);
- }
-
- disconnectedCallback() {
- this.shadowRoot.querySelector('button').removeEventListener('click', this.handle);
- }
-
- handle() {
- window.location.href = '/lock';
- }
-}
-
-customElements.define('button-lock', LockButton);
diff --git a/src/components/buttons/power.js b/src/components/buttons/power.js
deleted file mode 100644
index 7013604..0000000
--- a/src/components/buttons/power.js
+++ /dev/null
@@ -1,76 +0,0 @@
-class PowerButton extends HTMLElement {
- constructor() {
- super();
- this.attachShadow({ mode: 'open' });
- this.render();
- }
-
- css() {
- return `
- :host {
- display: block;
- width: 100px;
- height: 100px;
- }
-
- button {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- background-color: DimGray;
- border-radius: 10px;
- height: 100%;
- width: 100%;
- border: none;
- color: white;
- font-size: 24px;
- cursor: pointer;
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
- transition: background-color 0.3s ease;
- }
-
- button:hover {
- background-color: gray;
- }
-
- button:active {
- background-color: gray;
- }
-
- svg {
- fill: white;
- height: 100%;
- width: 100%;
- }
- `;
- }
-
- template() {
- return `
-
- `;
- }
-
- render() {
- this.shadowRoot.innerHTML = `
-
- ${this.template()}
- `;
- }
-
- connectedCallback() {
- this.shadowRoot.querySelector('button').addEventListener('click', this.handle);
- }
-
- disconnectedCallback() {
- this.shadowRoot.querySelector('button').removeEventListener('click', this.handle);
- }
-
- handle() {
- window.location.href = '/reboot/';
- }
-}
-
-customElements.define('button-power', PowerButton);
diff --git a/src/css/menu-bar.css b/src/css/menu-bar.css
index e861637..ba7e963 100644
--- a/src/css/menu-bar.css
+++ b/src/css/menu-bar.css
@@ -114,8 +114,8 @@
@media screen and (max-width: 1050px) {
- #menu-bar .container.status applet-network_down,
- #menu-bar .container.status applet-network_rtt {
+ #menu-bar .container.status applet-network-down,
+ #menu-bar .container.status applet-network-rtt {
display: none;
}
}
diff --git a/src/css/taskbar.css b/src/css/taskbar.css
index 67db974..43e8710 100644
--- a/src/css/taskbar.css
+++ b/src/css/taskbar.css
@@ -163,7 +163,7 @@
/* hide power button */
-#taskbar[position="bottom"] .applets applet-power {
+#taskbar[position="bottom"] .applets button-power {
display: none;
}
diff --git a/src/index.html b/src/index.html
index 64fdd49a..82ff73c 100644
--- a/src/index.html
+++ b/src/index.html
@@ -36,15 +36,15 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
@@ -103,13 +103,13 @@
@@ -172,10 +172,10 @@ Notifications
diff --git a/src/lock/index.html b/src/lock/index.html
index 9949fdc..5b3083f 100644
--- a/src/lock/index.html
+++ b/src/lock/index.html
@@ -21,9 +21,9 @@
-
-
-
+
+
+
@@ -38,8 +38,8 @@
Wednesday 00 April