From 3205d9f0f62dcdbf6d98778cfbce14dce0635d70 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Fri, 14 Aug 2026 02:03:17 +0200 Subject: [PATCH] Update --- custom/apps/mining-checker/assets/js/app.js | 91 +++++- .../src/Infrastructure/SchemaManager.php | 4 +- .../src/Support/SqlDataImporter.php | 293 ++++++++++++++++++ .../src/Support/UploadedSqlFile.php | 35 +++ public/assets/desktop/desktop.css | 20 +- public/assets/desktop/desktop.js | 69 +++-- 6 files changed, 470 insertions(+), 42 deletions(-) create mode 100644 custom/apps/mining-checker/src/Support/SqlDataImporter.php create mode 100644 custom/apps/mining-checker/src/Support/UploadedSqlFile.php diff --git a/custom/apps/mining-checker/assets/js/app.js b/custom/apps/mining-checker/assets/js/app.js index 41fe6d85..8480c408 100644 --- a/custom/apps/mining-checker/assets/js/app.js +++ b/custom/apps/mining-checker/assets/js/app.js @@ -189,6 +189,21 @@ }).format(Number(value)); } + function deriveDailyCostAmount(totalCostAmount, runtimeMonths) { + const amount = Number(totalCostAmount); + const months = Number(runtimeMonths); + if (!Number.isFinite(amount) || !Number.isFinite(months) || months <= 0) { + return null; + } + + const runtimeDays = months * 30.4375; + if (!Number.isFinite(runtimeDays) || runtimeDays <= 0) { + return null; + } + + return amount / runtimeDays; + } + function recentMeasurementWindow(rows, windowDays) { if (!Array.isArray(rows) || rows.length === 0) { return []; @@ -544,8 +559,10 @@ } function normalizeSchemaStatus(data) { - const normalized = data && typeof data === 'object' ? data : {}; + const hasPayload = !!(data && typeof data === 'object'); + const normalized = hasPayload ? data : {}; return { + loaded: hasPayload, required_tables: Array.isArray(normalized.required_tables) ? normalized.required_tables : [], present_tables: Array.isArray(normalized.present_tables) ? normalized.present_tables : [], missing_tables: Array.isArray(normalized.missing_tables) ? normalized.missing_tables : [], @@ -1275,6 +1292,14 @@ baseAmount = fallbackUsdReference; baseCurrency = 'USD'; } + const storedDailyAmount = Number(miner.daily_cost_amount); + const dailyAmount = Number.isFinite(storedDailyAmount) && storedDailyAmount > 0 + ? storedDailyAmount + : deriveDailyCostAmount(miner.total_cost_amount, miner.runtime_months); + const dailyCurrency = String(miner.daily_cost_currency || miner.currency || '').toUpperCase(); + const dailyReportAmount = Number.isFinite(dailyAmount) && dailyCurrency + ? (dailyCurrency === reportCurrency ? dailyAmount : convertCurrencyValue(dailyAmount, dailyCurrency, reportCurrency)) + : null; return { id: `purchase-${miner.id}`, source: 'miete', @@ -1284,6 +1309,9 @@ auto_renew: effectiveAutoRenew, effective_amount: miner.total_cost_amount, effective_currency: miner.currency, + daily_cost_amount: dailyAmount, + daily_cost_currency: dailyCurrency, + daily_cost_report_amount: Number.isFinite(dailyReportAmount) ? dailyReportAmount : null, base_amount: baseAmount, base_currency: baseCurrency, miner_id: miner.id, @@ -1299,6 +1327,14 @@ }; }).concat(currentCostPlans.map((plan) => { const coverage = entryCoverageMeta(plan.starts_at, plan.runtime_months, plan.auto_renew); + const storedDailyAmount = Number(plan.daily_cost_amount); + const dailyAmount = Number.isFinite(storedDailyAmount) && storedDailyAmount > 0 + ? storedDailyAmount + : deriveDailyCostAmount(plan.total_cost_amount, plan.runtime_months); + const dailyCurrency = String(plan.daily_cost_currency || plan.currency || '').toUpperCase(); + const dailyReportAmount = Number.isFinite(dailyAmount) && dailyCurrency + ? (dailyCurrency === reportCurrency ? dailyAmount : convertCurrencyValue(dailyAmount, dailyCurrency, reportCurrency)) + : null; return { id: `plan-${plan.id}`, source: 'manual', @@ -1308,6 +1344,9 @@ auto_renew: !!plan.auto_renew, effective_amount: plan.total_cost_amount, effective_currency: plan.currency, + daily_cost_amount: dailyAmount, + daily_cost_currency: dailyCurrency, + daily_cost_report_amount: Number.isFinite(dailyReportAmount) ? dailyReportAmount : null, base_amount: plan.base_price_amount, base_currency: currentSettings.report_currency || 'EUR', payment_type: plan.payment_type, @@ -1642,6 +1681,7 @@ setSchemaStatus(normalizeSchemaStatus(schema)); } catch (err) { setSchemaStatus(normalizeSchemaStatus(null)); + setError((previous) => previous || `Schema-Status konnte nicht geladen werden: ${err.message}`); } } @@ -2984,6 +3024,13 @@ const walletBalanceCurrentAsset = payload?.summary?.payouts?.wallet_balance_current_asset; const walletBalanceCurrentAssetDirect = payload?.summary?.payouts?.wallet_balance_current_asset_direct; const holdingsCurrentAsset = payload?.summary?.payouts?.holdings_current_asset; + const minerVisibleCoins = latest ? Number(latest.coins_total_visible ?? latest.coins_total) : null; + const minerValueUsd = latest && minerVisibleCoins !== null + ? convertMeasurementMoney(latest, minerVisibleCoins, 'USD') + : null; + const minerValueReport = latest && minerVisibleCoins !== null + ? convertMeasurementMoney(latest, minerVisibleCoins, reportCurrency) + : null; return h('div', { className: 'mc-stack' }, [ panel('Berichtswährung', 'Bestimmt die Währung für Kennzahlen im Überblick. Standard kommt aus den Settings, diese Auswahl gilt nur für den aktuellen Besuch.', [ @@ -3006,8 +3053,12 @@ h(StatCard, { key: 'coins', label: `${currentCoinCurrency} im Miner`, - value: latest ? fmtNumber(latest.coins_total_visible ?? latest.coins_total, 6) : 'n/a', - sub: latest ? `Stand ${fmtDate(latest.measured_at)}` : '', + value: latest ? fmtNumber(minerVisibleCoins, 6) : 'n/a', + sub: latest ? [ + minerValueUsd !== null ? `USD ${fmtMoney(minerValueUsd, 'USD')}` : null, + reportCurrency !== 'USD' && minerValueReport !== null ? `${reportCurrency} ${fmtMoney(minerValueReport, reportCurrency)}` : null, + `Stand ${fmtDate(latest.measured_at)}`, + ].filter(Boolean).join(' · ') : '', }), h(StatCard, { key: 'earned-overall', @@ -3434,6 +3485,14 @@ h('td', { key: 'renew' }, row.payment_type === 'crypto' ? 'nein' : (row.auto_renew ? 'ja' : 'nein')), h('td', { key: 'cost' }, [ h('div', { key: 'effective' }, fmtNumber(row.effective_amount, 6)), + row.daily_cost_amount !== null && row.daily_cost_amount !== undefined && row.daily_cost_currency + ? h('div', { key: 'daily', className: 'mc-kicker' }, [ + `Pro Tag ${fmtNumber(row.daily_cost_amount, 6)} ${row.daily_cost_currency}`, + row.daily_cost_report_amount !== null && row.daily_cost_report_amount !== undefined && row.daily_cost_currency !== reportCurrency + ? ` · ${fmtMoney(row.daily_cost_report_amount, reportCurrency)}` + : '', + ].join('')) + : null, row.base_amount !== null && row.base_amount !== undefined && row.base_currency ? h('div', { key: 'base', className: 'mc-kicker' }, `Basis ${fmtNumber(row.base_amount, 6)} ${row.base_currency}`) : null, @@ -3576,12 +3635,24 @@ offer.crypto_display_price_amount !== null && offer.crypto_display_price_currency ? h('div', { key: 'price-crypto' }, [ h('div', { key: 'amount' }, `${fmtNumber(offer.crypto_display_price_amount, 6)} ${offer.crypto_display_price_currency}`), + (() => { + const offerDailyAmount = deriveDailyCostAmount(offer.crypto_display_price_amount, offer.runtime_months); + return offerDailyAmount !== null + ? h('div', { key: 'daily', className: 'mc-kicker' }, `Pro Tag ${fmtNumber(offerDailyAmount, 6)} ${offer.crypto_display_price_currency}`) + : null; + })(), h('div', { key: 'label', className: 'mc-kicker' }, 'Zu zahlen in Krypto'), ]) : null, offer.usd_display_price_amount !== null && offer.usd_display_price_currency ? h('div', { key: 'price-usd' }, [ h('div', { key: 'amount' }, `${fmtNumber(offer.usd_display_price_amount, 6)} ${offer.usd_display_price_currency}`), + (() => { + const offerDailyAmount = deriveDailyCostAmount(offer.usd_display_price_amount, offer.runtime_months); + return offerDailyAmount !== null + ? h('div', { key: 'daily', className: 'mc-kicker' }, `Pro Tag ${fmtNumber(offerDailyAmount, 6)} ${offer.usd_display_price_currency}`) + : null; + })(), h('div', { key: 'label', className: 'mc-kicker' }, 'Zu zahlen in USD'), ]) : null, @@ -3589,6 +3660,12 @@ : [ h('div', { key: 'price-main' }, [ h('div', { key: 'amount' }, `${fmtNumber(offer.effective_price_amount, 6)} ${offer.effective_price_currency}`), + (() => { + const offerDailyAmount = deriveDailyCostAmount(offer.effective_price_amount, offer.runtime_months); + return offerDailyAmount !== null + ? h('div', { key: 'daily', className: 'mc-kicker' }, `Pro Tag ${fmtNumber(offerDailyAmount, 6)} ${offer.effective_price_currency}`) + : null; + })(), h('div', { key: 'label', className: 'mc-kicker' }, 'Zu zahlen'), ]), offer.base_price_amount !== null && offer.base_price_currency @@ -3892,10 +3969,10 @@ h('div', { className: 'mc-stack' }, [ panel('Initialisierung', 'Prueft den Tabellenstatus und kann das Mining-Checker Schema neu anlegen. Reset loescht bestehende miningcheck_ Tabellen inklusive Daten.', [ h('div', { key: 'status', className: 'mc-form' }, [ - displayField('Status', schemaStatus.all_present ? 'Schema vollstaendig vorhanden' : 'Schema unvollstaendig'), - displayField('Vorhandene Tabellen', `${schemaStatus.present_count}/${schemaStatus.required_tables.length}`), - displayField('Fehlende Tabellen', schemaStatus.missing_tables.length ? schemaStatus.missing_tables.join(', ') : 'keine'), - displayField('Ausstehende Upgrades', schemaStatus.pending_upgrades.length ? schemaStatus.pending_upgrades.join(', ') : 'keine'), + displayField('Status', !schemaStatus.loaded ? 'Status unbekannt' : (schemaStatus.all_present ? 'Schema vollstaendig vorhanden' : 'Schema unvollstaendig')), + displayField('Vorhandene Tabellen', schemaStatus.loaded ? `${schemaStatus.present_count}/${schemaStatus.required_tables.length}` : 'Status konnte nicht geladen werden'), + displayField('Fehlende Tabellen', schemaStatus.loaded ? (schemaStatus.missing_tables.length ? schemaStatus.missing_tables.join(', ') : 'keine') : 'Status konnte nicht geladen werden'), + displayField('Ausstehende Upgrades', schemaStatus.loaded ? (schemaStatus.pending_upgrades.length ? schemaStatus.pending_upgrades.join(', ') : 'keine') : 'Status konnte nicht geladen werden'), ]), h('form', { key: 'form', className: 'mc-form', onSubmit: initializeModule }, [ h('label', { className: 'mc-checkbox' }, [ diff --git a/custom/apps/mining-checker/src/Infrastructure/SchemaManager.php b/custom/apps/mining-checker/src/Infrastructure/SchemaManager.php index a1a81fd3..6961e40d 100644 --- a/custom/apps/mining-checker/src/Infrastructure/SchemaManager.php +++ b/custom/apps/mining-checker/src/Infrastructure/SchemaManager.php @@ -3,9 +3,9 @@ declare(strict_types=1); namespace Modules\MiningChecker\Infrastructure; -use App\SqlDataImporter; -use App\UploadedSqlFile; use Modules\MiningChecker\Support\ApiException; +use Modules\MiningChecker\Support\SqlDataImporter; +use Modules\MiningChecker\Support\UploadedSqlFile; use PDO; final class SchemaManager diff --git a/custom/apps/mining-checker/src/Support/SqlDataImporter.php b/custom/apps/mining-checker/src/Support/SqlDataImporter.php new file mode 100644 index 00000000..8fbd6378 --- /dev/null +++ b/custom/apps/mining-checker/src/Support/SqlDataImporter.php @@ -0,0 +1,293 @@ +pdo = $pdo; + $this->driver = strtolower((string) $pdo->getAttribute(PDO::ATTR_DRIVER_NAME)); + } + + public function importString(string $sql): int + { + $statements = []; + foreach ($this->splitStatements($sql) as $statement) { + $trimmed = trim($statement); + if ($trimmed === '') { + continue; + } + + $statements[] = $this->normalizeImportStatement($trimmed); + } + + return $this->executeStatements($statements); + } + + /** + * @param list $statements + */ + private function executeStatements(array $statements): int + { + $useTransaction = $this->driver === 'pgsql' && !$this->pdo->inTransaction(); + + try { + if ($useTransaction) { + $this->pdo->beginTransaction(); + } + + $executed = $this->executeImportPass($statements); + + if ($useTransaction && $this->pdo->inTransaction()) { + $this->pdo->commit(); + } + + return $executed; + } catch (\Throwable $exception) { + if ($useTransaction && $this->pdo->inTransaction()) { + $this->pdo->rollBack(); + } + + throw $exception; + } + } + + /** + * @param list $statements + */ + private function executeImportPass(array $statements): int + { + $pendingStatements = $statements; + $executed = 0; + $maxPasses = max(1, count($pendingStatements)); + + for ($pass = 0; $pass < $maxPasses && $pendingStatements !== []; $pass++) { + $deferredStatements = []; + $progressMade = false; + + foreach ($pendingStatements as $statement) { + try { + $this->execStatementWithRecovery($statement); + $executed++; + $progressMade = true; + } catch (\Throwable $exception) { + if ($this->shouldRetryDeferredImportStatement($exception, $statement)) { + $deferredStatements[] = $statement; + continue; + } + + throw new \RuntimeException($statement, 0, $exception); + } + } + + if ($deferredStatements === []) { + return $executed; + } + + if (!$progressMade) { + try { + $this->execStatementWithRecovery($deferredStatements[0]); + } catch (\Throwable $exception) { + throw new \RuntimeException($deferredStatements[0], 0, $exception); + } + } + + $pendingStatements = $deferredStatements; + } + + return $executed; + } + + private function execStatementWithRecovery(string $statement): void + { + if ($this->driver !== 'pgsql' || !$this->pdo->inTransaction()) { + $this->pdo->exec($statement); + return; + } + + $savepoint = 'sql_import_' . substr(sha1($statement . microtime(true)), 0, 12); + $this->pdo->exec('SAVEPOINT ' . $savepoint); + + try { + $this->pdo->exec($statement); + $this->pdo->exec('RELEASE SAVEPOINT ' . $savepoint); + } catch (\Throwable $exception) { + try { + $this->pdo->exec('ROLLBACK TO SAVEPOINT ' . $savepoint); + $this->pdo->exec('RELEASE SAVEPOINT ' . $savepoint); + } catch (\Throwable) { + } + + throw $exception; + } + } + + private function shouldRetryDeferredImportStatement(\Throwable $exception, string $statement): bool + { + if ($this->driver !== 'pgsql') { + return false; + } + + if (!preg_match('/^(INSERT|COPY)\b/i', ltrim($statement))) { + return false; + } + + return (string) $exception->getCode() === '23503'; + } + + private function normalizeImportStatement(string $statement): string + { + if ($this->driver !== 'pgsql') { + return $statement; + } + + $resolvedSetval = $this->normalizePgsqlSetvalStatement($statement); + return $resolvedSetval ?? $statement; + } + + private function normalizePgsqlSetvalStatement(string $statement): ?string + { + $pattern = "/^SELECT\\s+setval\\(\\s*'([^']+)'\\s*,\\s*([0-9]+)\\s*,\\s*(true|false)\\s*\\)$/i"; + if (!preg_match($pattern, trim($statement), $matches)) { + return null; + } + + $sequenceReference = $matches[1]; + $nextValue = $matches[2]; + $isCalled = strtolower($matches[3]); + + if ($this->pgsqlRelationExists($sequenceReference)) { + return $statement; + } + + $sequenceName = $sequenceReference; + $schemaName = 'public'; + if (str_contains($sequenceReference, '.')) { + [$schemaName, $sequenceName] = explode('.', $sequenceReference, 2); + } + + $schemaName = trim($schemaName, "\"'"); + $sequenceName = trim($sequenceName, "\"'"); + + if (!preg_match('/^(.*)_([A-Za-z0-9]+)_seq\d*$/', $sequenceName, $parts)) { + return null; + } + + $tableName = $parts[1]; + $columnName = $parts[2]; + $actualSequence = $this->resolvePgsqlSerialSequence($schemaName, $tableName, $columnName); + if ($actualSequence === null) { + return null; + } + + return sprintf("SELECT setval('%s', %s, %s)", $actualSequence, $nextValue, $isCalled); + } + + private function pgsqlRelationExists(string $qualifiedName): bool + { + $statement = $this->pdo->prepare('SELECT to_regclass(:name) IS NOT NULL'); + $statement->execute(['name' => $qualifiedName]); + return (bool) $statement->fetchColumn(); + } + + private function resolvePgsqlSerialSequence(string $schemaName, string $tableName, string $columnName): ?string + { + $qualifiedTable = sprintf('"%s"."%s"', str_replace('"', '""', $schemaName), str_replace('"', '""', $tableName)); + $statement = $this->pdo->prepare('SELECT pg_get_serial_sequence(:table_name, :column_name)'); + $statement->execute([ + 'table_name' => $qualifiedTable, + 'column_name' => $columnName, + ]); + + $result = $statement->fetchColumn(); + return is_string($result) && $result !== '' ? $result : null; + } + + /** + * @return list + */ + private function splitStatements(string $sql): array + { + $statements = []; + $buffer = ''; + $length = strlen($sql); + $inSingleQuote = false; + $inDoubleQuote = false; + $inBacktickQuote = false; + $inLineComment = false; + $inBlockComment = false; + + for ($index = 0; $index < $length; $index++) { + $char = $sql[$index]; + $next = $index + 1 < $length ? $sql[$index + 1] : ''; + + if ($inLineComment) { + if ($char === "\n") { + $inLineComment = false; + $buffer .= $char; + } + continue; + } + + if ($inBlockComment) { + if ($char === '*' && $next === '/') { + $inBlockComment = false; + $index++; + } + continue; + } + + if (!$inSingleQuote && !$inDoubleQuote && !$inBacktickQuote) { + if ($char === '-' && $next === '-') { + $inLineComment = true; + $index++; + continue; + } + if ($char === '#') { + $inLineComment = true; + continue; + } + if ($char === '/' && $next === '*') { + $inBlockComment = true; + $index++; + continue; + } + } + + if ($char === "'" && !$inDoubleQuote && !$inBacktickQuote) { + $escaped = $index > 0 && $sql[$index - 1] === '\\'; + if (!$escaped) { + $inSingleQuote = !$inSingleQuote; + } + } elseif ($char === '"' && !$inSingleQuote && !$inBacktickQuote) { + $escaped = $index > 0 && $sql[$index - 1] === '\\'; + if (!$escaped) { + $inDoubleQuote = !$inDoubleQuote; + } + } elseif ($char === '`' && !$inSingleQuote && !$inDoubleQuote) { + $inBacktickQuote = !$inBacktickQuote; + } + + if ($char === ';' && !$inSingleQuote && !$inDoubleQuote && !$inBacktickQuote) { + $statements[] = $buffer; + $buffer = ''; + continue; + } + + $buffer .= $char; + } + + if (trim($buffer) !== '') { + $statements[] = $buffer; + } + + return $statements; + } +} diff --git a/custom/apps/mining-checker/src/Support/UploadedSqlFile.php b/custom/apps/mining-checker/src/Support/UploadedSqlFile.php new file mode 100644 index 00000000..ba6a9827 --- /dev/null +++ b/custom/apps/mining-checker/src/Support/UploadedSqlFile.php @@ -0,0 +1,35 @@ + $originalName, + 'sql' => $sql, + ]; + } +} diff --git a/public/assets/desktop/desktop.css b/public/assets/desktop/desktop.css index ce22777f..fd4917ae 100644 --- a/public/assets/desktop/desktop.css +++ b/public/assets/desktop/desktop.css @@ -1671,18 +1671,28 @@ body .tray-pill.tray-pill-status--unconfigured { .window { position: absolute; - inset: 0; - width: 100% !important; - height: 100% !important; min-height: 0; max-height: none; - left: 0 !important; - top: 0 !important; border-radius: 0; border: 0; box-shadow: none; } + .window.is-mobile-fullscreen { + left: 0 !important; + border-radius: 0; + border: 0; + box-shadow: none; + } + + .window.is-mobile-fullscreen .window-header { + cursor: default; + } + + .window.is-mobile-fullscreen .window-resize-handle { + display: none; + } + .window-header { padding: 10px 12px; } diff --git a/public/assets/desktop/desktop.js b/public/assets/desktop/desktop.js index b91400d8..5553e734 100644 --- a/public/assets/desktop/desktop.js +++ b/public/assets/desktop/desktop.js @@ -1171,6 +1171,8 @@ if (payloadNode) { openDebugWindow(); }; + const isMobileViewport = () => window.matchMedia('(max-width: 1100px)').matches; + const applyWindowLayout = (record) => { const { x, y, width, height } = record.layout; record.node.style.left = `${x}px`; @@ -1179,6 +1181,35 @@ if (payloadNode) { record.node.style.height = `${height}px`; }; + const applyManagedWindowFrame = (record) => { + if (isMobileViewport()) { + const mobileTopInset = maximizeOverSystemBar ? 0 : topDesktopInset; + record.node.classList.add('is-mobile-fullscreen'); + record.node.style.left = '0px'; + record.node.style.top = `${mobileTopInset}px`; + record.node.style.width = '100%'; + record.node.style.height = maximizeOverSystemBar + ? `calc(100% - ${bottomDesktopInset}px)` + : `calc(100% - ${mobileTopInset + bottomDesktopInset}px)`; + return; + } + + record.node.classList.remove('is-mobile-fullscreen'); + + if (record.state.maximized) { + const maximizedTopInset = maximizeOverSystemBar ? 0 : topDesktopInset; + record.node.style.left = `${sideDesktopInset}px`; + record.node.style.top = `${maximizedTopInset}px`; + record.node.style.width = sideDesktopInset === 0 ? '100%' : `calc(100% - ${sideDesktopInset * 2}px)`; + record.node.style.height = maximizeOverSystemBar + ? '100%' + : `calc(100% - ${maximizedTopInset + bottomDesktopInset}px)`; + return; + } + + applyWindowLayout(record); + }; + const clampWindowLayout = (record) => { const minX = 0; const minY = maximizeOverSystemBar ? 0 : topDesktopInset; @@ -1218,17 +1249,17 @@ if (payloadNode) { } if (!record.state.maximized) { + if (isMobileViewport()) { + focusWindow(windowId); + saveWindowState(); + return; + } + record.restoreLayout = { ...record.layout }; record.state.maximized = true; record.node.classList.add('is-maximized'); measureDesktopBounds(); - const maximizedTopInset = maximizeOverSystemBar ? 0 : topDesktopInset; - record.node.style.left = `${sideDesktopInset}px`; - record.node.style.top = `${maximizedTopInset}px`; - record.node.style.width = sideDesktopInset === 0 ? '100%' : `calc(100% - ${sideDesktopInset * 2}px)`; - record.node.style.height = maximizeOverSystemBar - ? '100%' - : `calc(100% - ${maximizedTopInset + bottomDesktopInset}px)`; + applyManagedWindowFrame(record); focusWindow(windowId); saveWindowState(); return; @@ -1239,7 +1270,7 @@ if (payloadNode) { if (record.restoreLayout) { record.layout = { ...record.restoreLayout }; clampWindowLayout(record); - applyWindowLayout(record); + applyManagedWindowFrame(record); } focusWindow(windowId); saveWindowState(); @@ -1537,14 +1568,8 @@ if (payloadNode) { if (record.state.maximized) { record.node.classList.add('is-maximized'); measureDesktopBounds(); - const maximizedTopInset = maximizeOverSystemBar ? 0 : topDesktopInset; - record.node.style.left = `${sideDesktopInset}px`; - record.node.style.top = `${maximizedTopInset}px`; - record.node.style.width = sideDesktopInset === 0 ? '100%' : `calc(100% - ${sideDesktopInset * 2}px)`; - record.node.style.height = maximizeOverSystemBar - ? '100%' - : `calc(100% - ${maximizedTopInset + bottomDesktopInset}px)`; } + applyManagedWindowFrame(record); node.addEventListener('mousedown', () => focusWindow(record.id)); @@ -2263,19 +2288,7 @@ if (payloadNode) { windows.forEach((record) => { clampWindowLayout(record); - - if (record.state.maximized) { - const maximizedTopInset = maximizeOverSystemBar ? 0 : topDesktopInset; - record.node.style.left = `${sideDesktopInset}px`; - record.node.style.top = `${maximizedTopInset}px`; - record.node.style.width = sideDesktopInset === 0 ? '100%' : `calc(100% - ${sideDesktopInset * 2}px)`; - record.node.style.height = maximizeOverSystemBar - ? '100%' - : `calc(100% - ${maximizedTopInset + bottomDesktopInset}px)`; - return; - } - - applyWindowLayout(record); + applyManagedWindowFrame(record); }); saveWindowState();