diff --git a/res/static/bug.svg b/res/static/bug.svg
new file mode 100644
index 0000000..0669481
--- /dev/null
+++ b/res/static/bug.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/res/static/github.svg b/res/static/github.svg
new file mode 100644
index 0000000..408978d
--- /dev/null
+++ b/res/static/github.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/config.ts b/src/config.ts
index 4bced3e..b813519 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -12,6 +12,10 @@ export class AppConfig {
}
public readonly RELEASE_MODE: boolean;
+ public readonly MAJOR_VERSION: number;
+ public readonly MINOR_VERSION: number;
+ public readonly HOTFIX_VERSION: number;
+ public readonly VERSION_TYPE: 'd' | 'a' | 'r'; // dev, alpha, or release build
public readonly RELEASE_VERSION: string;
public readonly VOXEL_BUFFER_CHUNK_SIZE: number;
@@ -41,7 +45,11 @@ export class AppConfig {
private constructor() {
this.RELEASE_MODE = false;
- this.RELEASE_VERSION = '0.8.0d';
+ this.MAJOR_VERSION = 0;
+ this.MINOR_VERSION = 8;
+ this.HOTFIX_VERSION = 0;
+ this.VERSION_TYPE = 'd';
+ this.RELEASE_VERSION = `${this.MAJOR_VERSION}.${this.MINOR_VERSION}.${this.HOTFIX_VERSION}${this.VERSION_TYPE}`;
this.VOXEL_BUFFER_CHUNK_SIZE = 5_000;
const configFile = fs.readFileSync(PathUtil.join(AppPaths.Get.resources, 'config.json'), 'utf8');
diff --git a/src/ui/elements/header_element.ts b/src/ui/elements/header_element.ts
new file mode 100644
index 0000000..834519e
--- /dev/null
+++ b/src/ui/elements/header_element.ts
@@ -0,0 +1,140 @@
+import { shell } from 'electron';
+
+import { AppConfig } from '../../config';
+import { ASSERT } from '../../util/error_util';
+import { LOG, LOG_ERROR } from '../../util/log_util';
+import { AppPaths, PathUtil } from '../../util/path_util';
+import { UIUtil } from '../../util/ui_util';
+import { BaseUIElement } from './base_element';
+import { ToolbarItemElement } from './toolbar_item';
+
+
+export class HeaderUIElement extends BaseUIElement {
+ private static _instance: HeaderUIElement;
+ public static get Get() {
+ return this._instance || (this._instance = new this());
+ }
+
+ private _githubButton: ToolbarItemElement;
+ private _bugButton: ToolbarItemElement;
+
+ private constructor() {
+ super();
+
+ this._githubButton = new ToolbarItemElement({ icon: 'github' })
+ .onClick(() => {
+ shell.openExternal('https://github.com/LucasDower/ObjToSchematic');
+ });
+
+ this._bugButton = new ToolbarItemElement({ icon: 'bug' })
+ .onClick(() => {
+ shell.openExternal('https://github.com/LucasDower/ObjToSchematic/issues');
+ });
+ }
+
+ // Header element shouldn't be
+ protected override _onEnabledChanged(): void {
+ return;
+ }
+
+ public override generateHTML(): string {
+ return `
+
+
+
+ `;
+ }
+
+ public override registerEvents(): void {
+ this._githubButton.registerEvents();
+ this._bugButton.registerEvents();
+ }
+
+ public override finalise(): void {
+ const updateElement = UIUtil.getElementById('update-checker') as HTMLDivElement;
+ updateElement.style.animation = 'pulse-opacity 1.5s infinite';
+ updateElement.innerHTML = 'Checking for updates...';
+
+ fetch('https://api.github.com/repos/LucasDower/ObjToSchematic/releases/latest')
+ .then((response) => response.json())
+ .then((data) => {
+ const latest: string = data.tag_name; // e.g. v0.7.0
+ const versionString = latest.substring(1); // e.g. 0.7.0
+ const versionValues = versionString.split('.').map((x) => parseInt(x));
+
+ // Is the local version older than the latest release on GitHub?
+ let isGitHubVersionNewer = false;
+ if (versionValues[0] > AppConfig.Get.MAJOR_VERSION) {
+ isGitHubVersionNewer = true;
+ } else {
+ if (versionValues[1] > AppConfig.Get.MINOR_VERSION) {
+ isGitHubVersionNewer = true;
+ } else {
+ if (versionValues[2] > AppConfig.Get.HOTFIX_VERSION) {
+ isGitHubVersionNewer = true;
+ }
+ }
+ }
+
+ /*
+ let isLocalVersionNewer = false;
+ if (versionValues[0] < AppConfig.Get.MAJOR_VERSION) {
+ isLocalVersionNewer = true;
+ } else {
+ if (versionValues[1] < AppConfig.Get.MINOR_VERSION) {
+ isLocalVersionNewer = true;
+ } else {
+ if (versionValues[2] > AppConfig.Get.HOTFIX_VERSION) {
+ isLocalVersionNewer = true;
+ }
+ }
+ }
+ */
+
+ LOG(`[VERSION]: Current: ${[AppConfig.Get.MAJOR_VERSION, AppConfig.Get.MINOR_VERSION, AppConfig.Get.HOTFIX_VERSION]}, Latest: ${versionValues}`);
+
+ updateElement.style.animation = '';
+ if (isGitHubVersionNewer) {
+ updateElement.innerHTML = `New ${versionString} update available!`;
+
+ const linkElement = UIUtil.getElementById('update-link') as HTMLLinkElement;
+ linkElement.onclick = () => {
+ shell.openExternal('https://github.com/LucasDower/ObjToSchematic/releases/latest');
+ };
+ } else {
+ // Either using most up-to-date version or local version is newer (using unreleased dev or alpha build)
+ updateElement.innerHTML = `Version up-to-date!`;
+ }
+ })
+ .catch((error) => {
+ LOG_ERROR(error);
+
+ updateElement.style.animation = '';
+ updateElement.innerHTML = 'Could not check for updates';
+ });
+ }
+}
diff --git a/src/ui/layout.ts b/src/ui/layout.ts
index 200a209..5ee987c 100644
--- a/src/ui/layout.ts
+++ b/src/ui/layout.ts
@@ -19,6 +19,7 @@ import { CheckboxElement } from './elements/checkbox';
import { ComboBoxElement, ComboBoxItem } from './elements/combobox';
import { ConfigUIElement } from './elements/config_element';
import { FileInputElement } from './elements/file_input';
+import { HeaderUIElement } from './elements/header_element';
import { OutputElement } from './elements/output';
import { SliderElement } from './elements/slider';
import { ToolbarItemElement } from './elements/toolbar_item';
@@ -468,7 +469,7 @@ export class UI {
}
document.getElementById('properties')!.innerHTML = `
- ` + itemHTML + `
`;
+ ` + HeaderUIElement.Get.generateHTML() + itemHTML + ``;
// Build toolbar
let toolbarHTML = '';
@@ -567,6 +568,9 @@ export class UI {
}
public registerEvents() {
+ HeaderUIElement.Get.registerEvents();
+ HeaderUIElement.Get.finalise();
+
for (const groupName in this._ui) {
const group = this._uiDull[groupName];
for (const elementName in group.elements) {
diff --git a/styles.css b/styles.css
index bbb34c5..e7d0827 100644
--- a/styles.css
+++ b/styles.css
@@ -18,6 +18,7 @@
--prop-sunken: hsl(0, 0%, 8%);
--text-standard: hsl(0, 0%, 66%);
+ --text-muted: hsl(0, 0%, 45%);
--text-disabled: hsl(0, 0%, 33%);
--vertical-divider: hsl(0, 0%, 14%);
@@ -797,10 +798,55 @@ svg {
display: flex;
flex-direction: row;
gap: 2px;
+ align-items: center;
}
.col-item {
display: flex;
flex-direction: column;
align-items: center;
+}
+
+.logo {
+ width: 32px;
+ margin-right: 10px;
+}
+
+.title {
+ font-size: 110%;
+}
+
+.subtitle {
+ font-size: 90%;
+ font-weight: 300;
+ color: var(--text-muted);
+}
+
+.header-cols {
+ justify-content: space-between;
+ width: 100%;
+ padding-top: 4px;
+}
+
+.button-loading {
+ box-shadow: 0 0 0 0 rgba(0, 0, 0, 1);
+}
+
+@keyframes pulse-opacity {
+ 0% {
+ opacity: 1;
+ }
+
+ 50% {
+ opacity: 0.6;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+a {
+ font-weight: 400;
+ color: var(--prop-accent-hovered)
}
\ No newline at end of file