diff --git a/packages/core/src/generic_steps/git_clone.js b/packages/core/src/generic_steps/git_clone.js index da0a3c2..1b857c8 100644 --- a/packages/core/src/generic_steps/git_clone.js +++ b/packages/core/src/generic_steps/git_clone.js @@ -33,6 +33,7 @@ export default async (pkg, step) => { //`--depth ${step.depth ?? 1}`, //"--filter=blob:none", //"--filter=tree:0", + "--progress", "--recurse-submodules", "--remote-submodules", step.url, diff --git a/packages/core/src/handlers/apply.js b/packages/core/src/handlers/apply.js index 7b9f47e..1270b84 100644 --- a/packages/core/src/handlers/apply.js +++ b/packages/core/src/handlers/apply.js @@ -83,6 +83,7 @@ export default async function apply(pkg_id, changes = {}) { return pkg } catch (error) { global._relic_eventBus.emit(`pkg:error`, { + event: "apply", id: pkg_id, error }) diff --git a/packages/core/src/handlers/execute.js b/packages/core/src/handlers/execute.js index 4351d24..b22684b 100644 --- a/packages/core/src/handlers/execute.js +++ b/packages/core/src/handlers/execute.js @@ -19,6 +19,20 @@ export default async function execute(pkg_id, { useRemote = false, force = false return false } + if (pkg.last_status !== "installed") { + if (!force) { + BaseLog.info(`Package not installed [${pkg_id}], aborting execution`) + + global._relic_eventBus.emit(`pkg:error`, { + id: pkg_id, + event: "execute", + error: new Error("Package not valid or not installed"), + }) + + return false + } + } + const manifestPath = useRemote ? pkg.remote_manifest : pkg.local_manifest if (!fs.existsSync(manifestPath)) { diff --git a/packages/core/src/handlers/install.js b/packages/core/src/handlers/install.js index faf0919..e94274c 100644 --- a/packages/core/src/handlers/install.js +++ b/packages/core/src/handlers/install.js @@ -164,12 +164,13 @@ export default async function install(manifest) { return pkg } catch (error) { global._relic_eventBus.emit(`pkg:error`, { - id: id, - error + id: id ?? manifest.constructor.id, + event: "install", + error, }) global._relic_eventBus.emit(`pkg:update:state`, { - id: id, + id: id ?? manifest.constructor.id, last_status: "failed", status_text: `Installation failed`, }) diff --git a/packages/core/src/handlers/lastOperationRetry.js b/packages/core/src/handlers/lastOperationRetry.js new file mode 100644 index 0000000..9da6b9b --- /dev/null +++ b/packages/core/src/handlers/lastOperationRetry.js @@ -0,0 +1,76 @@ +import fs from "node:fs" +import path from "node:path" + +import Logger from "../logger" +import DB from "../db" + +import PackageInstall from "./install" +import PackageUpdate from "./update" +import PackageUninstall from "./uninstall" + +import Vars from "../vars" + +export default async function lastOperationRetry(pkg_id) { + try { + const Log = Logger.child({ service: `OPERATION_RETRY|${pkg_id}` }) + const pkg = await DB.getPackages(pkg_id) + + if (!pkg) { + Log.error(`This package doesn't exist`) + return null + } + + Log.info(`Try performing last operation retry...`) + + global._relic_eventBus.emit(`pkg:update:state`, { + id: pkg.id, + status_text: `Performing last operation retry...`, + }) + + switch (pkg.last_status) { + case "installing": + await PackageInstall(pkg.local_manifest) + break + case "updating": + await PackageUpdate(pkg_id) + break + case "uninstalling": + await PackageUninstall(pkg_id) + break + case "failed": { + // copy pkg.local_manifest to cache after uninstall + const cachedManifest = path.join(Vars.cache_path, path.basename(pkg.local_manifest)) + + await fs.promises.copyFile(pkg.local_manifest, cachedManifest) + + await PackageUninstall(pkg_id) + await PackageInstall(cachedManifest) + break + } + default: { + Log.error(`Invalid last status: ${pkg.last_status}`) + + global._relic_eventBus.emit(`pkg:error`, { + id: pkg.id, + event: "retrying last operation", + status_text: `Performing last operation retry...`, + }) + + return null + } + } + + return pkg + } catch (error) { + Logger.error(`Failed to perform last operation retry of [${pkg_id}]`) + Logger.error(error) + + global._relic_eventBus.emit(`pkg:error`, { + event: "retrying last operation", + id: pkg_id, + error: error, + }) + + return null + } +} \ No newline at end of file diff --git a/packages/core/src/handlers/uninstall.js b/packages/core/src/handlers/uninstall.js index b0ea282..f2c0984 100644 --- a/packages/core/src/handlers/uninstall.js +++ b/packages/core/src/handlers/uninstall.js @@ -20,21 +20,33 @@ export default async function uninstall(pkg_id) { const Log = Logger.child({ service: `UNINSTALLER|${pkg.id}` }) Log.info(`Uninstalling package...`) + global._relic_eventBus.emit(`pkg:update:state`, { id: pkg.id, status_text: `Uninstalling package...`, }) - const ManifestRead = await ManifestReader(pkg.local_manifest) - const manifest = await ManifestVM(ManifestRead.code) + try { + const ManifestRead = await ManifestReader(pkg.local_manifest) + const manifest = await ManifestVM(ManifestRead.code) - if (typeof manifest.uninstall === "function") { - Log.info(`Performing uninstall hook...`) - global._relic_eventBus.emit(`pkg:update:state`, { + if (typeof manifest.uninstall === "function") { + Log.info(`Performing uninstall hook...`) + + global._relic_eventBus.emit(`pkg:update:state`, { + id: pkg.id, + status_text: `Performing uninstall hook...`, + }) + + await manifest.uninstall(pkg) + } + } catch (error) { + Log.error(`Failed to perform uninstall hook`, error) + global._relic_eventBus.emit(`pkg:error`, { + event: "uninstall", id: pkg.id, - status_text: `Performing uninstall hook...`, + error }) - await manifest.uninstall(pkg) } Log.info(`Deleting package directory...`) @@ -62,6 +74,7 @@ export default async function uninstall(pkg_id) { return pkg } catch (error) { global._relic_eventBus.emit(`pkg:error`, { + event: "uninstall", id: pkg_id, error }) diff --git a/packages/core/src/handlers/update.js b/packages/core/src/handlers/update.js index 87d4ce0..ac7ca9d 100644 --- a/packages/core/src/handlers/update.js +++ b/packages/core/src/handlers/update.js @@ -116,10 +116,21 @@ export default async function update(pkg_id) { return pkg } catch (error) { global._relic_eventBus.emit(`pkg:error`, { + event: "update", id: pkg_id, - error + error, + last_status: "failed" }) + try { + await DB.updatePackageById(pkg_id, { + last_status: "failed", + }) + } catch (error) { + BaseLog.error(`Failed to update status of pkg [${pkg_id}]`) + BaseLog.error(error.stack) + } + BaseLog.error(`Failed to update package [${pkg_id}]`, error) BaseLog.error(error.stack) diff --git a/packages/core/src/index.js b/packages/core/src/index.js index de39263..5440832 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -18,6 +18,7 @@ import PackageList from "./handlers/list" import PackageRead from "./handlers/read" import PackageAuthorize from "./handlers/authorize" import PackageCheckUpdate from "./handlers/checkUpdate" +import PackageLastOperationRetry from "./handlers/lastOperationRetry" export default class RelicCore { constructor(params) { @@ -55,7 +56,8 @@ export default class RelicCore { list: PackageList, read: PackageRead, authorize: PackageAuthorize, - checkUpdate: PackageCheckUpdate + checkUpdate: PackageCheckUpdate, + lastOperationRetry: PackageLastOperationRetry, } openPath(pkg_id) { diff --git a/packages/core/src/logger.js b/packages/core/src/logger.js index 1f3d6d1..12bbc1a 100644 --- a/packages/core/src/logger.js +++ b/packages/core/src/logger.js @@ -1,4 +1,5 @@ import winston from "winston" +import WinstonTransport from "winston-transport" import colors from "cli-color" const servicesToColor = { @@ -6,10 +7,6 @@ const servicesToColor = { color: "whiteBright", background: "bgBlackBright", }, - "INSTALL": { - color: "whiteBright", - background: "bgBlueBright", - }, } const paintText = (level, service, ...args) => { @@ -27,6 +24,13 @@ const format = winston.format.printf(({ timestamp, service = "CORE", level, mess return `${paintText(level, service, `(${level}) [${service}]`)} > ${message}` }) +class EventBusTransport extends WinstonTransport { + log(info, next) { + global._relic_eventBus.emit(`logger:new`, info) + next() + } +} + export default winston.createLogger({ format: winston.format.combine( winston.format.timestamp(), @@ -34,6 +38,7 @@ export default winston.createLogger({ ), transports: [ new winston.transports.Console(), + new EventBusTransport(), //new winston.transports.File({ filename: "error.log", level: "error" }), //new winston.transports.File({ filename: "combined.log" }), ], diff --git a/packages/core/src/manifest/reader.js b/packages/core/src/manifest/reader.js index 830278f..a34915f 100644 --- a/packages/core/src/manifest/reader.js +++ b/packages/core/src/manifest/reader.js @@ -39,6 +39,15 @@ export async function readManifest(manifest) { throw new Error(`Manifest is not a file: ${target}`) } + // copy to cache + const cachedManifest = path.join(Vars.cache_path, path.basename(target)) + + await fs.promises.copyFile(target, cachedManifest) + + if (!fs.existsSync(cachedManifest)) { + throw new Error(`Manifest copy failed: ${target}`) + } + return { remote_manifest: undefined, local_manifest: target, diff --git a/packages/gui/src/main/classes/CoreAdapter.js b/packages/gui/src/main/classes/CoreAdapter.js index 9fa931d..12be69a 100644 --- a/packages/gui/src/main/classes/CoreAdapter.js +++ b/packages/gui/src/main/classes/CoreAdapter.js @@ -1,14 +1,76 @@ import sendToRender from "../utils/sendToRender" +import { ipcMain } from "electron" export default class CoreAdapter { constructor(electronApp, RelicCore) { this.app = electronApp this.core = RelicCore - - this.initialize() + this.initialized = false } - events = { + loggerWindow = null + + ipcEvents = { + "pkg:list": async () => { + return await this.core.package.list() + }, + "pkg:get": async (event, pkg_id) => { + return await this.core.db.getPackages(pkg_id) + }, + "pkg:read": async (event, manifest_path, options = {}) => { + const manifest = await this.core.package.read(manifest_path, options) + + return JSON.stringify({ + ...this.core.db.defaultPackageState({ ...manifest }), + ...manifest, + name: manifest.pkg_name, + }) + }, + "pkg:install": async (event, manifest_path) => { + return await this.core.package.install(manifest_path) + }, + "pkg:update": async (event, pkg_id, { execOnFinish = false } = {}) => { + await this.core.package.update(pkg_id) + + if (execOnFinish) { + await this.core.package.execute(pkg_id) + } + + return true + }, + "pkg:apply": async (event, pkg_id, changes) => { + return await this.core.package.apply(pkg_id, changes) + }, + "pkg:uninstall": async (event, pkg_id) => { + return await this.core.package.uninstall(pkg_id) + }, + "pkg:execute": async (event, pkg_id, { force = false } = {}) => { + // check for updates first + if (!force) { + const update = await this.core.package.checkUpdate(pkg_id) + + if (update) { + return sendToRender("pkg:update_available", update) + } + } + + return await this.core.package.execute(pkg_id) + }, + "pkg:open": async (event, pkg_id) => { + return await this.core.openPath(pkg_id) + }, + "pkg:last_operation_retry": async (event, pkg_id) => { + return await this.core.package.lastOperationRetry(pkg_id) + }, + "pkg:cancel_current_operation": async (event, pkg_id) => { + return await this.core.package.cancelCurrentOperation(pkg_id) + }, + "core:open-path": async (event, pkg_id) => { + return await this.core.openPath(pkg_id) + }, + } + + coreEvents = { "pkg:new": (pkg) => { sendToRender("pkg:new", pkg) }, @@ -51,22 +113,53 @@ export default class CoreAdapter { sendToRender(`new:notification`, { type: "error", message: `An error occurred`, - description: `Something failed to ${data.event} package ${data.pkg_id}`, + description: `Something failed to ${data.event} package ${data.id}`, }) sendToRender(`pkg:update:state`, data) + }, + "logger:new": (data) => { + if (this.loggerWindow) { + this.loggerWindow.webContents.send("logger:new", data) + } } } - initialize = () => { - for (const [key, handler] of Object.entries(this.events)) { + attachLogger = (window) => { + this.loggerWindow = window + + window.webContents.send("logger:new", { + timestamp: new Date().getTime(), + message: "Core adapter Logger attached", + }) + } + + initialize = async () => { + if (this.initialized) { + return + } + + for (const [key, handler] of Object.entries(this.coreEvents)) { global._relic_eventBus.on(key, handler) } + + for (const [key, handler] of Object.entries(this.ipcEvents)) { + ipcMain.handle(key, handler) + } + + await this.core.initialize() + await this.core.setup() + + this.initialized = true } deinitialize = () => { - for (const [key, handler] of Object.entries(this.events)) { + for (const [key, handler] of Object.entries(this.coreEvents)) { global._relic_eventBus.off(key, handler) } + + for (const [key, handler] of Object.entries(this.ipcEvents)) { + ipcMain.removeHandler(key, handler) + } } } \ No newline at end of file diff --git a/packages/gui/src/main/index.js b/packages/gui/src/main/index.js index b8fc8e8..42bd7c2 100644 --- a/packages/gui/src/main/index.js +++ b/packages/gui/src/main/index.js @@ -26,62 +26,54 @@ const ProtocolRegistry = require("protocol-registry") const protocolRegistryNamespace = "relic" +class LogsViewer { + window = null + + async createWindow() { + this.window = new BrowserWindow({ + width: 800, + height: 600, + show: false, + resizable: true, + autoHideMenuBar: true, + icon: "../../resources/icon.png", + webPreferences: { + preload: path.join(__dirname, "../preload/index.js"), + sandbox: false, + }, + }) + + if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { + this.window.loadURL(`${process.env["ELECTRON_RENDERER_URL"]}/logs`) + } else { + this.window.loadFile(path.join(__dirname, "../renderer/index.html")) + } + + await new Promise((resolve) => this.window.once("ready-to-show", resolve)) + + this.window.show() + + return this.window + } + + closeWindow() { + if (this.window) { + this.window.close() + } + } +} + class ElectronApp { constructor() { - this.win = null this.core = new RelicCore() this.adapter = new CoreAdapter(this, this.core) } + window = null + + logsViewer = new LogsViewer() + handlers = { - "pkg:list": async () => { - return await this.core.package.list() - }, - "pkg:get": async (event, pkg_id) => { - return await this.core.db.getPackages(pkg_id) - }, - "pkg:read": async (event, manifest_path, options = {}) => { - const manifest = await this.core.package.read(manifest_path, options) - - return JSON.stringify({ - ...this.core.db.defaultPackageState({ ...manifest }), - ...manifest, - name: manifest.pkg_name, - }) - }, - "pkg:install": async (event, manifest_path) => { - return await this.core.package.install(manifest_path) - }, - "pkg:update": async (event, pkg_id, { execOnFinish = false } = {}) => { - await this.core.package.update(pkg_id) - - if (execOnFinish) { - await this.core.package.execute(pkg_id) - } - - return true - }, - "pkg:apply": async (event, pkg_id, changes) => { - return await this.core.package.apply(pkg_id, changes) - }, - "pkg:uninstall": async (event, pkg_id) => { - return await this.core.package.uninstall(pkg_id) - }, - "pkg:execute": async (event, pkg_id, { force = false } = {}) => { - // check for updates first - if (!force) { - const update = await this.core.package.checkUpdate(pkg_id) - - if (update) { - return sendToRender("pkg:update_available", update) - } - } - - return await this.core.package.execute(pkg_id) - }, - "pkg:open": async (event, pkg_id) => { - return await this.core.openPath(pkg_id) - }, "updater:check": () => { autoUpdater.checkForUpdates() }, @@ -90,22 +82,31 @@ class ElectronApp { autoUpdater.quitAndInstall() }, 3000) }, - "settings:get": (e, key) => { + "settings:get": (event, key) => { return global.SettingsStore.get(key) }, - "settings:set": (e, key, value) => { + "settings:set": (event, key, value) => { return global.SettingsStore.set(key, value) }, - "settings:delete": (e, key) => { + "settings:delete": (event, key) => { return global.SettingsStore.delete(key) }, - "settings:has": (e, key) => { + "settings:has": (event, key) => { return global.SettingsStore.has(key) }, + "app:open-logs": async (event) => { + const loggerWindow = await this.logsViewer.createWindow() + + this.adapter.attachLogger(loggerWindow) + + loggerWindow.webContents.send("logger:new", { + timestamp: new Date().getTime(), + message: "Logger opened, starting watching logs", + }) + }, "app:init": async (event, data) => { try { - await this.core.initialize() - await this.core.setup() + await this.adapter.initialize() return { pkg: pkg, @@ -126,19 +127,8 @@ class ElectronApp { } } - events = { - "open-runtime-path": () => { - return this.core.openPath() - }, - "open-dev-logs": () => { - return sendToRender("new:message", { - message: "Not implemented yet", - }) - } - } - createWindow() { - this.win = global.win = new BrowserWindow({ + this.window = global.mainWindow = new BrowserWindow({ width: 450, height: 670, show: false, @@ -151,20 +141,20 @@ class ElectronApp { } }) - this.win.on("ready-to-show", () => { - this.win.show() + this.window.on("ready-to-show", () => { + this.window.show() }) - this.win.webContents.setWindowOpenHandler((details) => { + this.window.webContents.setWindowOpenHandler((details) => { shell.openExternal(details.url) return { action: "deny" } }) if (is.dev && process.env["ELECTRON_RENDERER_URL"]) { - this.win.loadURL(process.env["ELECTRON_RENDERER_URL"]) + this.window.loadURL(process.env["ELECTRON_RENDERER_URL"]) } else { - this.win.loadFile(path.join(__dirname, "../renderer/index.html")) + this.window.loadFile(path.join(__dirname, "../renderer/index.html")) } } @@ -206,12 +196,12 @@ class ElectronApp { event.preventDefault() // Someone tried to run a second instance, we should focus our window. - if (this.win) { - if (this.win.isMinimized()) { - this.win.restore() + if (this.window) { + if (this.window.isMinimized()) { + this.window.restore() } - this.win.focus() + this.window.focus() } console.log(`Second instance >`, commandLine) @@ -235,10 +225,6 @@ class ElectronApp { ipcMain.handle(key, this.handlers[key]) } - for (const key in this.events) { - ipcMain.on(key, this.events[key]) - } - app.on("second-instance", this.handleOnSecondInstance) app.on("open-url", (event, url) => { @@ -308,4 +294,4 @@ class ElectronApp { } } -new ElectronApp().initialize() +new ElectronApp().initialize() \ No newline at end of file diff --git a/packages/gui/src/main/utils/sendToRender.js b/packages/gui/src/main/utils/sendToRender.js index 6fc3784..df4266f 100644 --- a/packages/gui/src/main/utils/sendToRender.js +++ b/packages/gui/src/main/utils/sendToRender.js @@ -32,7 +32,7 @@ export default (event, data) => { return copy } - global.win.webContents.send(event, serializeIpc(data)) + global.mainWindow.webContents.send(event, serializeIpc(data)) } catch (error) { console.error(error) } diff --git a/packages/gui/src/renderer/src/App.jsx b/packages/gui/src/renderer/src/App.jsx index 3323cb8..02f26ee 100644 --- a/packages/gui/src/renderer/src/App.jsx +++ b/packages/gui/src/renderer/src/App.jsx @@ -12,14 +12,19 @@ import AppDrawer from "layout/components/Drawer" import { InternalRouter, PageRender } from "./router.jsx" +import CrashError from "components/Crash" +import LogsViewer from "./pages/logs" + // create a global app context window.app = GlobalApp class App extends React.Component { state = { - initializing: true, pkg: null, + crash: null, + initializing: true, + appSetup: { error: false, installed: false, @@ -98,6 +103,15 @@ class App extends React.Component { console.log(`React version > ${versions["react"]}`) console.log(`DOMRouter version > ${versions["react-router-dom"]}`) + //check if path is /logs + if (window.location.pathname === "/logs") { + return await this.setState({ + initializing: false, + no_layout: true, + log_viewer_mode: true, + }) + } + window.app.style.appendClassname("initializing") for (const event in this.ipcEvents) { @@ -133,17 +147,34 @@ class App extends React.Component { algorithm: antd.theme.darkAlgorithm }} > - - + { + this.state.log_viewer_mode && + } - - + { + !this.state.log_viewer_mode && <> + + + { + !this.state.crash && <> + + - - - - - + + + + + } + + { + this.state.crash && + } + + + + } } } diff --git a/packages/gui/src/renderer/src/components/Crash/index.less b/packages/gui/src/renderer/src/components/Crash/index.less index 54aa325..587ef18 100644 --- a/packages/gui/src/renderer/src/components/Crash/index.less +++ b/packages/gui/src/renderer/src/components/Crash/index.less @@ -6,6 +6,8 @@ gap: 20px; + padding: 20px; + h1 { font-size: 1.5rem; font-weight: bold; diff --git a/packages/gui/src/renderer/src/components/PackageItem/index.jsx b/packages/gui/src/renderer/src/components/PackageItem/index.jsx index b0f6bd9..3986322 100644 --- a/packages/gui/src/renderer/src/components/PackageItem/index.jsx +++ b/packages/gui/src/renderer/src/components/PackageItem/index.jsx @@ -14,7 +14,7 @@ const PackageItem = (props) => { const isLoading = manifest.last_status === "loading" || manifest.last_status === "installing" || manifest.last_status === "updating" const isInstalling = manifest.last_status === "installing" const isInstalled = !!manifest.installed_at - const isFailed = manifest.last_status === "error" + const isFailed = manifest.last_status === "failed" console.log(manifest, { isLoading, @@ -38,7 +38,7 @@ const PackageItem = (props) => { } const onClickFolder = () => { - ipc.exec("pkg:open", manifest.id) + ipc.exec("core:open-path", manifest.id) } const onClickDelete = () => { @@ -60,7 +60,7 @@ const PackageItem = (props) => { } const onClickRetryInstall = () => { - ipc.exec("pkg:retry_install", manifest.id) + ipc.exec("pkg:last_operation_retry", manifest.id) } function handleUpdate(event, data) { @@ -75,7 +75,7 @@ const PackageItem = (props) => { return manifest.last_status } - return `v${manifest.version}` ?? "N/A" + return `${isFailed ? "failed |" : ""} v${manifest.version}` ?? "N/A" } const MenuProps = { @@ -148,7 +148,6 @@ const PackageItem = (props) => { manifest.icon && } -

{ @@ -164,16 +163,24 @@ const PackageItem = (props) => {
{ - isFailed && - Retry - + isFailed && <> + + Retry + + + } + type="primary" + onClick={onClickDelete} + /> + } { - isInstalled && manifest.executable && { } { - isInstalled && !manifest.executable && @@ -199,7 +206,7 @@ const PackageItem = (props) => { } { - isInstalling && diff --git a/packages/gui/src/renderer/src/pages/logs/index.jsx b/packages/gui/src/renderer/src/pages/logs/index.jsx new file mode 100644 index 0000000..c494cea --- /dev/null +++ b/packages/gui/src/renderer/src/pages/logs/index.jsx @@ -0,0 +1,67 @@ +import React from "react" + +import "./index.less" + +const Timestamp = ({ timestamp }) => { + if (isNaN(timestamp)) { + return {timestamp} + } + + return + { + new Date(timestamp).toLocaleString().split(", ").join("|") + } + +} + +const LogEntry = ({ log }) => { + return
+ + {">"} + + + {log.timestamp && } + + {!log.timestamp && - no timestamp -} + +

+ {log.message ?? "No message"} +

+
+} + +const LogsViewer = () => { + const listRef = React.useRef() + const [timeline, setTimeline] = React.useState([]) + + const events = { + "logger:new": (event, log) => { + setTimeline((timeline) => [...timeline, log]) + + listRef.current.scrollTop = listRef.current.scrollHeight + } + } + + React.useEffect(() => { + for (const event in events) { + ipc.exclusiveListen(event, events[event]) + } + }, []) + + return
+ { + timeline.length === 0 &&

No logs

+ } + + { + timeline.map((log) => ) + } +
+} + +export default LogsViewer \ No newline at end of file diff --git a/packages/gui/src/renderer/src/pages/logs/index.less b/packages/gui/src/renderer/src/pages/logs/index.less new file mode 100644 index 0000000..74b6c71 --- /dev/null +++ b/packages/gui/src/renderer/src/pages/logs/index.less @@ -0,0 +1,47 @@ +.app-logs { + display: flex; + flex-direction: column; + + padding: 10px; + + font-family: "DM Mono", monospace; + + overflow-x: hidden; + overflow-y: scroll; + + height: 100vh; + + .log-entry { + display: flex; + flex-direction: row; + + align-items: flex-start; + + gap: 7px; + font-size: 0.8rem; + line-height: 0.8rem; + + border-radius: 8px; + + padding: 8px; + + color: var(--text-color); + + span { + color: var(--text-color); + + white-space: nowrap; + word-break: break-all; + } + + .timestamp { + opacity: 0.9; + + font-size: 0.7rem; + } + + &:nth-child(odd) { + background-color: var(--background-color-secondary); + } + } +} \ No newline at end of file diff --git a/packages/gui/src/renderer/src/pages/pkg/[pkg_id].jsx b/packages/gui/src/renderer/src/pages/pkg/[pkg_id].jsx index a8cad61..565325b 100644 --- a/packages/gui/src/renderer/src/pages/pkg/[pkg_id].jsx +++ b/packages/gui/src/renderer/src/pages/pkg/[pkg_id].jsx @@ -307,8 +307,6 @@ const PackageOptionsLoader = (props) => { }) } - console.log(manifest) - if (!manifest) { return } diff --git a/packages/gui/src/renderer/src/router.jsx b/packages/gui/src/renderer/src/router.jsx index 33c696b..599c16e 100644 --- a/packages/gui/src/renderer/src/router.jsx +++ b/packages/gui/src/renderer/src/router.jsx @@ -7,7 +7,6 @@ import loadable from "@loadable/component" import GlobalStateContext from "contexts/global" import SplashScreen from "components/Splash" -import CrashError from "components/Crash" const DefaultNotFoundRender = () => { return
Not found
@@ -131,18 +130,6 @@ export const InternalRouter = (props) => { } export const PageRender = (props) => { - const globalState = React.useContext(GlobalStateContext) - - if (globalState.crash) { - return - } - - if (globalState.initializing) { - return - } - const routes = React.useMemo(() => { let paths = { ...import.meta.glob("/src/pages/**/[a-z[]*.jsx"), @@ -167,6 +154,12 @@ export const PageRender = (props) => { return paths }, []) + const globalState = React.useContext(GlobalStateContext) + + if (globalState.initializing) { + return + } + return { routes.map((route, index) => { diff --git a/packages/gui/src/renderer/src/settings_list.jsx b/packages/gui/src/renderer/src/settings_list.jsx index 1478796..809b799 100644 --- a/packages/gui/src/renderer/src/settings_list.jsx +++ b/packages/gui/src/renderer/src/settings_list.jsx @@ -20,6 +20,7 @@ export default [ render: (props) => { return (